@getnori/rewards 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -99,8 +99,9 @@ function PointsBadge({
|
|
|
99
99
|
}) {
|
|
100
100
|
const { summary } = useRewards(client);
|
|
101
101
|
if (!summary) return null;
|
|
102
|
-
const points = show === "today" ? summary.today.points : summary.total_points;
|
|
103
|
-
const
|
|
102
|
+
const points = show === "today" ? summary.today.points : show === "creator" ? summary.creator_points ?? 0 : summary.total_points;
|
|
103
|
+
const suffix = show === "today" ? " today" : show === "creator" ? " as creator" : "";
|
|
104
|
+
const label = `${points.toLocaleString()} ${summary.currency_name}${suffix}`;
|
|
104
105
|
const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [styles.badge, { backgroundColor }, style], testID: "nori-points-badge", children: [
|
|
105
106
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [styles.star, { color: textColor }], children: "\u2605" }),
|
|
106
107
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [styles.text, { color: textColor }], children: label })
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/PointsBadge.tsx","../src/useRewards.ts","../src/RewardToasts.tsx"],"sourcesContent":["export { PointsBadge } from \"./PointsBadge\";\nexport type { PointsBadgeProps } from \"./PointsBadge\";\nexport { RewardToasts } from \"./RewardToasts\";\nexport type { RewardToastsProps } from \"./RewardToasts\";\nexport { useRewards, useRewardDecisions } from \"./useRewards\";\nexport type { RewardsState, UseRewardsOptions } from \"./useRewards\";\n","import React from \"react\";\nimport { Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient } from \"@getnori/core\";\nimport { useRewards } from \"./useRewards\";\n\nexport interface PointsBadgeProps {\n client: NoriClient;\n /** What to show: all-time Nori earnings or today's. Default \"total\". */\n show?: \"total\" | \"today\";\n onPress?: () => void;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** A small pill with the viewer's points, e.g. in the feed's corner. Hidden until loaded. */\nexport function PointsBadge({\n client,\n show = \"total\",\n onPress,\n backgroundColor = \"rgba(0,0,0,0.55)\",\n textColor = \"#ffffff\",\n style,\n}: PointsBadgeProps) {\n const { summary } = useRewards(client);\n if (!summary) return null;\n\n const points = show === \"today\" ? summary.today.points : summary.total_points;\n const label = `${points.toLocaleString()} ${summary.currency_name}${show === \"today\" ? \" today\" : \"\"}`;\n const content = (\n <View style={[styles.badge, { backgroundColor }, style]} testID=\"nori-points-badge\">\n <Text style={[styles.star, { color: textColor }]}>★</Text>\n <Text style={[styles.text, { color: textColor }]}>{label}</Text>\n </View>\n );\n return onPress ? (\n <Pressable onPress={onPress} accessibilityRole=\"button\" accessibilityLabel={label}>\n {content}\n </Pressable>\n ) : (\n content\n );\n}\n\nconst styles = StyleSheet.create({\n badge: { flexDirection: \"row\", alignItems: \"center\", alignSelf: \"flex-start\", borderRadius: 16, paddingHorizontal: 12, paddingVertical: 6 },\n star: { fontSize: 13, marginRight: 6 },\n text: { fontSize: 13, fontWeight: \"700\" },\n});\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { NoriClient, NoriError, RewardDecision, RewardsSummary } from \"@getnori/core\";\n\nexport interface UseRewardsOptions {\n /** History entries to load. Default 20. */\n historyLimit?: number;\n /** Re-read the summary this long after a reward, to pick up credited status. Default 1500 ms. */\n refreshDelayMs?: number;\n}\n\nexport interface RewardsState {\n summary: RewardsSummary | null;\n loading: boolean;\n error: NoriError | Error | null;\n refresh: () => Promise<void>;\n}\n\n/** Calls `listener` with each reward the server grants this viewer. */\nexport function useRewardDecisions(client: Pick<NoriClient, \"on\">, listener: (decision: RewardDecision) => void): void {\n const ref = useRef(listener);\n ref.current = listener;\n useEffect(\n () =>\n client.on(\"reward\", (decisions) => {\n for (const d of decisions) ref.current(d);\n }),\n [client],\n );\n}\n\n/**\n * The viewer's Nori earnings. Updates the moment a view is rewarded, then\n * re-reads the server for the authoritative numbers.\n */\nexport function useRewards(client: NoriClient, { historyLimit = 20, refreshDelayMs = 1500 }: UseRewardsOptions = {}): RewardsState {\n const [summary, setSummary] = useState<RewardsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<NoriError | Error | null>(null);\n const live = useRef(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const refresh = useCallback(async () => {\n try {\n const next = await client.rewards({ limit: historyLimit });\n if (!live.current) return;\n setSummary(next);\n setError(null);\n } catch (e) {\n if (live.current) setError(e as Error);\n } finally {\n if (live.current) setLoading(false);\n }\n }, [client, historyLimit]);\n\n useEffect(() => {\n live.current = true;\n void refresh();\n return () => {\n live.current = false;\n if (timer.current) clearTimeout(timer.current);\n };\n }, [refresh]);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status !== \"rewarded\") return;\n setSummary((prev) =>\n prev\n ? {\n ...prev,\n total_points: prev.total_points + decision.points,\n today: {\n ...prev.today,\n points: prev.today.points + decision.points,\n remaining: Math.max(0, prev.today.remaining - decision.points),\n },\n }\n : prev,\n );\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => void refresh(), refreshDelayMs);\n });\n\n return { summary, loading, error, refresh };\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { Animated, StyleSheet, Text, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient, RewardDecision } from \"@getnori/core\";\nimport { useRewardDecisions } from \"./useRewards\";\n\nexport interface RewardToastsProps {\n client: NoriClient;\n /** Text for a reward. Default \"+10 points\". */\n format?: (decision: RewardDecision) => string;\n /** Tell the viewer once per session when they hit the daily cap. Default true. */\n showDailyCapNotice?: boolean;\n /** How long each toast stays. Default 2500 ms. */\n durationMs?: number;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface Toast {\n key: number;\n text: string;\n}\n\n/**\n * Shows \"+10 points\" when the server rewards a view. Mount it once, above\n * the feed (it positions itself at the top of its parent).\n */\nexport function RewardToasts({\n client,\n format = (d) => `+${d.points} ${d.currency_name}`,\n showDailyCapNotice = true,\n durationMs = 2500,\n backgroundColor = \"rgba(17,17,17,0.9)\",\n textColor = \"#ffffff\",\n style,\n}: RewardToastsProps) {\n const [toast, setToast] = useState<Toast | null>(null);\n const capShown = useRef(false);\n const counter = useRef(0);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status === \"rewarded\" && decision.points > 0) {\n setToast({ key: ++counter.current, text: format(decision) });\n } else if (decision.reason === \"daily_cap\" && showDailyCapNotice && !capShown.current) {\n capShown.current = true;\n setToast({ key: ++counter.current, text: `You've earned today's maximum ${decision.currency_name}. Come back tomorrow!` });\n }\n });\n\n if (!toast) return null;\n return (\n <ToastView\n key={toast.key}\n text={toast.text}\n durationMs={durationMs}\n backgroundColor={backgroundColor}\n textColor={textColor}\n style={style}\n onDone={() => setToast((t) => (t?.key === toast.key ? null : t))}\n />\n );\n}\n\nfunction ToastView({\n text,\n durationMs,\n backgroundColor,\n textColor,\n style,\n onDone,\n}: {\n text: string;\n durationMs: number;\n backgroundColor: string;\n textColor: string;\n style?: StyleProp<ViewStyle>;\n onDone: () => void;\n}) {\n const opacity = useRef(new Animated.Value(0)).current;\n const onDoneRef = useRef(onDone);\n onDoneRef.current = onDone;\n\n useEffect(() => {\n Animated.timing(opacity, { toValue: 1, duration: 180, useNativeDriver: true }).start();\n const timer = setTimeout(() => {\n Animated.timing(opacity, { toValue: 0, duration: 220, useNativeDriver: true }).start(() => onDoneRef.current());\n }, durationMs);\n return () => clearTimeout(timer);\n }, [durationMs, opacity]);\n\n return (\n <Animated.View\n pointerEvents=\"none\"\n style={[styles.toast, { backgroundColor, opacity }, style]}\n accessibilityLiveRegion=\"polite\"\n accessibilityRole=\"alert\"\n testID=\"nori-reward-toast\"\n >\n <Text style={[styles.text, { color: textColor }]}>{text}</Text>\n </Animated.View>\n );\n}\n\nconst styles = StyleSheet.create({\n toast: {\n position: \"absolute\", top: 56, alignSelf: \"center\", maxWidth: \"86%\",\n borderRadius: 20, paddingHorizontal: 16, paddingVertical: 10,\n },\n text: { fontSize: 15, fontWeight: \"700\", textAlign: \"center\" },\n});\n"],"mappings":"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","names":["import_react","import_react_native","import_jsx_runtime","styles"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PointsBadge.tsx","../src/useRewards.ts","../src/RewardToasts.tsx"],"sourcesContent":["export { PointsBadge } from \"./PointsBadge\";\nexport type { PointsBadgeProps } from \"./PointsBadge\";\nexport { RewardToasts } from \"./RewardToasts\";\nexport type { RewardToastsProps } from \"./RewardToasts\";\nexport { useRewards, useRewardDecisions } from \"./useRewards\";\nexport type { RewardsState, UseRewardsOptions } from \"./useRewards\";\n","import React from \"react\";\nimport { Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient } from \"@getnori/core\";\nimport { useRewards } from \"./useRewards\";\n\nexport interface PointsBadgeProps {\n client: NoriClient;\n /** What to show: all-time Nori earnings, today's (as a viewer), or what I earned as a creator. Default \"total\". */\n show?: \"total\" | \"today\" | \"creator\";\n onPress?: () => void;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** A small pill with the viewer's points, e.g. in the feed's corner. Hidden until loaded. */\nexport function PointsBadge({\n client,\n show = \"total\",\n onPress,\n backgroundColor = \"rgba(0,0,0,0.55)\",\n textColor = \"#ffffff\",\n style,\n}: PointsBadgeProps) {\n const { summary } = useRewards(client);\n if (!summary) return null;\n\n const points =\n show === \"today\" ? summary.today.points : show === \"creator\" ? summary.creator_points ?? 0 : summary.total_points;\n const suffix = show === \"today\" ? \" today\" : show === \"creator\" ? \" as creator\" : \"\";\n const label = `${points.toLocaleString()} ${summary.currency_name}${suffix}`;\n const content = (\n <View style={[styles.badge, { backgroundColor }, style]} testID=\"nori-points-badge\">\n <Text style={[styles.star, { color: textColor }]}>★</Text>\n <Text style={[styles.text, { color: textColor }]}>{label}</Text>\n </View>\n );\n return onPress ? (\n <Pressable onPress={onPress} accessibilityRole=\"button\" accessibilityLabel={label}>\n {content}\n </Pressable>\n ) : (\n content\n );\n}\n\nconst styles = StyleSheet.create({\n badge: { flexDirection: \"row\", alignItems: \"center\", alignSelf: \"flex-start\", borderRadius: 16, paddingHorizontal: 12, paddingVertical: 6 },\n star: { fontSize: 13, marginRight: 6 },\n text: { fontSize: 13, fontWeight: \"700\" },\n});\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { NoriClient, NoriError, RewardDecision, RewardsSummary } from \"@getnori/core\";\n\nexport interface UseRewardsOptions {\n /** History entries to load. Default 20. */\n historyLimit?: number;\n /** Re-read the summary this long after a reward, to pick up credited status. Default 1500 ms. */\n refreshDelayMs?: number;\n}\n\nexport interface RewardsState {\n summary: RewardsSummary | null;\n loading: boolean;\n error: NoriError | Error | null;\n refresh: () => Promise<void>;\n}\n\n/** Calls `listener` with each reward the server grants this viewer. */\nexport function useRewardDecisions(client: Pick<NoriClient, \"on\">, listener: (decision: RewardDecision) => void): void {\n const ref = useRef(listener);\n ref.current = listener;\n useEffect(\n () =>\n client.on(\"reward\", (decisions) => {\n for (const d of decisions) ref.current(d);\n }),\n [client],\n );\n}\n\n/**\n * The viewer's Nori earnings. Updates the moment a view is rewarded, then\n * re-reads the server for the authoritative numbers.\n */\nexport function useRewards(client: NoriClient, { historyLimit = 20, refreshDelayMs = 1500 }: UseRewardsOptions = {}): RewardsState {\n const [summary, setSummary] = useState<RewardsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<NoriError | Error | null>(null);\n const live = useRef(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const refresh = useCallback(async () => {\n try {\n const next = await client.rewards({ limit: historyLimit });\n if (!live.current) return;\n setSummary(next);\n setError(null);\n } catch (e) {\n if (live.current) setError(e as Error);\n } finally {\n if (live.current) setLoading(false);\n }\n }, [client, historyLimit]);\n\n useEffect(() => {\n live.current = true;\n void refresh();\n return () => {\n live.current = false;\n if (timer.current) clearTimeout(timer.current);\n };\n }, [refresh]);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status !== \"rewarded\") return;\n setSummary((prev) =>\n prev\n ? {\n ...prev,\n total_points: prev.total_points + decision.points,\n today: {\n ...prev.today,\n points: prev.today.points + decision.points,\n remaining: Math.max(0, prev.today.remaining - decision.points),\n },\n }\n : prev,\n );\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => void refresh(), refreshDelayMs);\n });\n\n return { summary, loading, error, refresh };\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { Animated, StyleSheet, Text, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient, RewardDecision } from \"@getnori/core\";\nimport { useRewardDecisions } from \"./useRewards\";\n\nexport interface RewardToastsProps {\n client: NoriClient;\n /** Text for a reward. Default \"+10 points\". */\n format?: (decision: RewardDecision) => string;\n /** Tell the viewer once per session when they hit the daily cap. Default true. */\n showDailyCapNotice?: boolean;\n /** How long each toast stays. Default 2500 ms. */\n durationMs?: number;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface Toast {\n key: number;\n text: string;\n}\n\n/**\n * Shows \"+10 points\" when the server rewards a view. Mount it once, above\n * the feed (it positions itself at the top of its parent).\n */\nexport function RewardToasts({\n client,\n format = (d) => `+${d.points} ${d.currency_name}`,\n showDailyCapNotice = true,\n durationMs = 2500,\n backgroundColor = \"rgba(17,17,17,0.9)\",\n textColor = \"#ffffff\",\n style,\n}: RewardToastsProps) {\n const [toast, setToast] = useState<Toast | null>(null);\n const capShown = useRef(false);\n const counter = useRef(0);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status === \"rewarded\" && decision.points > 0) {\n setToast({ key: ++counter.current, text: format(decision) });\n } else if (decision.reason === \"daily_cap\" && showDailyCapNotice && !capShown.current) {\n capShown.current = true;\n setToast({ key: ++counter.current, text: `You've earned today's maximum ${decision.currency_name}. Come back tomorrow!` });\n }\n });\n\n if (!toast) return null;\n return (\n <ToastView\n key={toast.key}\n text={toast.text}\n durationMs={durationMs}\n backgroundColor={backgroundColor}\n textColor={textColor}\n style={style}\n onDone={() => setToast((t) => (t?.key === toast.key ? null : t))}\n />\n );\n}\n\nfunction ToastView({\n text,\n durationMs,\n backgroundColor,\n textColor,\n style,\n onDone,\n}: {\n text: string;\n durationMs: number;\n backgroundColor: string;\n textColor: string;\n style?: StyleProp<ViewStyle>;\n onDone: () => void;\n}) {\n const opacity = useRef(new Animated.Value(0)).current;\n const onDoneRef = useRef(onDone);\n onDoneRef.current = onDone;\n\n useEffect(() => {\n Animated.timing(opacity, { toValue: 1, duration: 180, useNativeDriver: true }).start();\n const timer = setTimeout(() => {\n Animated.timing(opacity, { toValue: 0, duration: 220, useNativeDriver: true }).start(() => onDoneRef.current());\n }, durationMs);\n return () => clearTimeout(timer);\n }, [durationMs, opacity]);\n\n return (\n <Animated.View\n pointerEvents=\"none\"\n style={[styles.toast, { backgroundColor, opacity }, style]}\n accessibilityLiveRegion=\"polite\"\n accessibilityRole=\"alert\"\n testID=\"nori-reward-toast\"\n >\n <Text style={[styles.text, { color: textColor }]}>{text}</Text>\n </Animated.View>\n );\n}\n\nconst styles = StyleSheet.create({\n toast: {\n position: \"absolute\", top: 56, alignSelf: \"center\", maxWidth: \"86%\",\n borderRadius: 20, paddingHorizontal: 16, paddingVertical: 10,\n },\n text: { fontSize: 15, fontWeight: \"700\", textAlign: \"center\" },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,0BAAkF;;;ACDlF,mBAAyD;AAkBlD,SAAS,mBAAmB,QAAgC,UAAoD;AACrH,QAAM,UAAM,qBAAO,QAAQ;AAC3B,MAAI,UAAU;AACd;AAAA,IACE,MACE,OAAO,GAAG,UAAU,CAAC,cAAc;AACjC,iBAAW,KAAK,UAAW,KAAI,QAAQ,CAAC;AAAA,IAC1C,CAAC;AAAA,IACH,CAAC,MAAM;AAAA,EACT;AACF;AAMO,SAAS,WAAW,QAAoB,EAAE,eAAe,IAAI,iBAAiB,KAAK,IAAuB,CAAC,GAAiB;AACjI,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAgC,IAAI;AAClE,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,IAAI;AAC3C,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAmC,IAAI;AACjE,QAAM,WAAO,qBAAO,IAAI;AACxB,QAAM,YAAQ,qBAA6C,IAAI;AAE/D,QAAM,cAAU,0BAAY,YAAY;AACtC,QAAI;AACF,YAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,OAAO,aAAa,CAAC;AACzD,UAAI,CAAC,KAAK,QAAS;AACnB,iBAAW,IAAI;AACf,eAAS,IAAI;AAAA,IACf,SAAS,GAAG;AACV,UAAI,KAAK,QAAS,UAAS,CAAU;AAAA,IACvC,UAAE;AACA,UAAI,KAAK,QAAS,YAAW,KAAK;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,QAAQ,YAAY,CAAC;AAEzB,8BAAU,MAAM;AACd,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,WAAO,MAAM;AACX,WAAK,UAAU;AACf,UAAI,MAAM,QAAS,cAAa,MAAM,OAAO;AAAA,IAC/C;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,qBAAmB,QAAQ,CAAC,aAAa;AACvC,QAAI,SAAS,WAAW,WAAY;AACpC;AAAA,MAAW,CAAC,SACV,OACI;AAAA,QACE,GAAG;AAAA,QACH,cAAc,KAAK,eAAe,SAAS;AAAA,QAC3C,OAAO;AAAA,UACL,GAAG,KAAK;AAAA,UACR,QAAQ,KAAK,MAAM,SAAS,SAAS;AAAA,UACrC,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,SAAS,MAAM;AAAA,QAC/D;AAAA,MACF,IACA;AAAA,IACN;AACA,QAAI,MAAM,QAAS,cAAa,MAAM,OAAO;AAC7C,UAAM,UAAU,WAAW,MAAM,KAAK,QAAQ,GAAG,cAAc;AAAA,EACjE,CAAC;AAED,SAAO,EAAE,SAAS,SAAS,OAAO,QAAQ;AAC5C;;;ADnDI;AAhBG,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ;AACF,GAAqB;AACnB,QAAM,EAAE,QAAQ,IAAI,WAAW,MAAM;AACrC,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,SACJ,SAAS,UAAU,QAAQ,MAAM,SAAS,SAAS,YAAY,QAAQ,kBAAkB,IAAI,QAAQ;AACvG,QAAM,SAAS,SAAS,UAAU,WAAW,SAAS,YAAY,gBAAgB;AAClF,QAAM,QAAQ,GAAG,OAAO,eAAe,CAAC,IAAI,QAAQ,aAAa,GAAG,MAAM;AAC1E,QAAM,UACJ,6CAAC,4BAAK,OAAO,CAAC,OAAO,OAAO,EAAE,gBAAgB,GAAG,KAAK,GAAG,QAAO,qBAC9D;AAAA,gDAAC,4BAAK,OAAO,CAAC,OAAO,MAAM,EAAE,OAAO,UAAU,CAAC,GAAG,oBAAC;AAAA,IACnD,4CAAC,4BAAK,OAAO,CAAC,OAAO,MAAM,EAAE,OAAO,UAAU,CAAC,GAAI,iBAAM;AAAA,KAC3D;AAEF,SAAO,UACL,4CAAC,iCAAU,SAAkB,mBAAkB,UAAS,oBAAoB,OACzE,mBACH,IAEA;AAEJ;AAEA,IAAM,SAAS,+BAAW,OAAO;AAAA,EAC/B,OAAO,EAAE,eAAe,OAAO,YAAY,UAAU,WAAW,cAAc,cAAc,IAAI,mBAAmB,IAAI,iBAAiB,EAAE;AAAA,EAC1I,MAAM,EAAE,UAAU,IAAI,aAAa,EAAE;AAAA,EACrC,MAAM,EAAE,UAAU,IAAI,YAAY,MAAM;AAC1C,CAAC;;;AElDD,IAAAA,gBAAmD;AACnD,IAAAC,uBAA2E;AAkDvE,IAAAC,sBAAA;AAxBG,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,SAAS,CAAC,MAAM,IAAI,EAAE,MAAM,IAAI,EAAE,aAAa;AAAA,EAC/C,qBAAqB;AAAA,EACrB,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ;AACF,GAAsB;AACpB,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AACrD,QAAM,eAAW,sBAAO,KAAK;AAC7B,QAAM,cAAU,sBAAO,CAAC;AAExB,qBAAmB,QAAQ,CAAC,aAAa;AACvC,QAAI,SAAS,WAAW,cAAc,SAAS,SAAS,GAAG;AACzD,eAAS,EAAE,KAAK,EAAE,QAAQ,SAAS,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,IAC7D,WAAW,SAAS,WAAW,eAAe,sBAAsB,CAAC,SAAS,SAAS;AACrF,eAAS,UAAU;AACnB,eAAS,EAAE,KAAK,EAAE,QAAQ,SAAS,MAAM,iCAAiC,SAAS,aAAa,wBAAwB,CAAC;AAAA,IAC3H;AAAA,EACF,CAAC;AAED,MAAI,CAAC,MAAO,QAAO;AACnB,SACE;AAAA,IAAC;AAAA;AAAA,MAEC,MAAM,MAAM;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,MAAM,SAAS,CAAC,MAAO,GAAG,QAAQ,MAAM,MAAM,OAAO,CAAE;AAAA;AAAA,IAN1D,MAAM;AAAA,EAOb;AAEJ;AAEA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,cAAU,sBAAO,IAAI,8BAAS,MAAM,CAAC,CAAC,EAAE;AAC9C,QAAM,gBAAY,sBAAO,MAAM;AAC/B,YAAU,UAAU;AAEpB,+BAAU,MAAM;AACd,kCAAS,OAAO,SAAS,EAAE,SAAS,GAAG,UAAU,KAAK,iBAAiB,KAAK,CAAC,EAAE,MAAM;AACrF,UAAM,QAAQ,WAAW,MAAM;AAC7B,oCAAS,OAAO,SAAS,EAAE,SAAS,GAAG,UAAU,KAAK,iBAAiB,KAAK,CAAC,EAAE,MAAM,MAAM,UAAU,QAAQ,CAAC;AAAA,IAChH,GAAG,UAAU;AACb,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,YAAY,OAAO,CAAC;AAExB,SACE;AAAA,IAAC,8BAAS;AAAA,IAAT;AAAA,MACC,eAAc;AAAA,MACd,OAAO,CAACC,QAAO,OAAO,EAAE,iBAAiB,QAAQ,GAAG,KAAK;AAAA,MACzD,yBAAwB;AAAA,MACxB,mBAAkB;AAAA,MAClB,QAAO;AAAA,MAEP,uDAAC,6BAAK,OAAO,CAACA,QAAO,MAAM,EAAE,OAAO,UAAU,CAAC,GAAI,gBAAK;AAAA;AAAA,EAC1D;AAEJ;AAEA,IAAMA,UAAS,gCAAW,OAAO;AAAA,EAC/B,OAAO;AAAA,IACL,UAAU;AAAA,IAAY,KAAK;AAAA,IAAI,WAAW;AAAA,IAAU,UAAU;AAAA,IAC9D,cAAc;AAAA,IAAI,mBAAmB;AAAA,IAAI,iBAAiB;AAAA,EAC5D;AAAA,EACA,MAAM,EAAE,UAAU,IAAI,YAAY,OAAO,WAAW,SAAS;AAC/D,CAAC;","names":["import_react","import_react_native","import_jsx_runtime","styles"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -4,8 +4,8 @@ import { NoriClient, RewardDecision, RewardsSummary, NoriError } from '@getnori/
|
|
|
4
4
|
|
|
5
5
|
interface PointsBadgeProps {
|
|
6
6
|
client: NoriClient;
|
|
7
|
-
/** What to show: all-time Nori earnings
|
|
8
|
-
show?: "total" | "today";
|
|
7
|
+
/** What to show: all-time Nori earnings, today's (as a viewer), or what I earned as a creator. Default "total". */
|
|
8
|
+
show?: "total" | "today" | "creator";
|
|
9
9
|
onPress?: () => void;
|
|
10
10
|
backgroundColor?: string;
|
|
11
11
|
textColor?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { NoriClient, RewardDecision, RewardsSummary, NoriError } from '@getnori/
|
|
|
4
4
|
|
|
5
5
|
interface PointsBadgeProps {
|
|
6
6
|
client: NoriClient;
|
|
7
|
-
/** What to show: all-time Nori earnings
|
|
8
|
-
show?: "total" | "today";
|
|
7
|
+
/** What to show: all-time Nori earnings, today's (as a viewer), or what I earned as a creator. Default "total". */
|
|
8
|
+
show?: "total" | "today" | "creator";
|
|
9
9
|
onPress?: () => void;
|
|
10
10
|
backgroundColor?: string;
|
|
11
11
|
textColor?: string;
|
package/dist/index.js
CHANGED
|
@@ -70,8 +70,9 @@ function PointsBadge({
|
|
|
70
70
|
}) {
|
|
71
71
|
const { summary } = useRewards(client);
|
|
72
72
|
if (!summary) return null;
|
|
73
|
-
const points = show === "today" ? summary.today.points : summary.total_points;
|
|
74
|
-
const
|
|
73
|
+
const points = show === "today" ? summary.today.points : show === "creator" ? summary.creator_points ?? 0 : summary.total_points;
|
|
74
|
+
const suffix = show === "today" ? " today" : show === "creator" ? " as creator" : "";
|
|
75
|
+
const label = `${points.toLocaleString()} ${summary.currency_name}${suffix}`;
|
|
75
76
|
const content = /* @__PURE__ */ jsxs(View, { style: [styles.badge, { backgroundColor }, style], testID: "nori-points-badge", children: [
|
|
76
77
|
/* @__PURE__ */ jsx(Text, { style: [styles.star, { color: textColor }], children: "\u2605" }),
|
|
77
78
|
/* @__PURE__ */ jsx(Text, { style: [styles.text, { color: textColor }], children: label })
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/PointsBadge.tsx","../src/useRewards.ts","../src/RewardToasts.tsx"],"sourcesContent":["import React from \"react\";\nimport { Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient } from \"@getnori/core\";\nimport { useRewards } from \"./useRewards\";\n\nexport interface PointsBadgeProps {\n client: NoriClient;\n /** What to show: all-time Nori earnings or today's. Default \"total\". */\n show?: \"total\" | \"today\";\n onPress?: () => void;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** A small pill with the viewer's points, e.g. in the feed's corner. Hidden until loaded. */\nexport function PointsBadge({\n client,\n show = \"total\",\n onPress,\n backgroundColor = \"rgba(0,0,0,0.55)\",\n textColor = \"#ffffff\",\n style,\n}: PointsBadgeProps) {\n const { summary } = useRewards(client);\n if (!summary) return null;\n\n const points = show === \"today\" ? summary.today.points : summary.total_points;\n const label = `${points.toLocaleString()} ${summary.currency_name}${show === \"today\" ? \" today\" : \"\"}`;\n const content = (\n <View style={[styles.badge, { backgroundColor }, style]} testID=\"nori-points-badge\">\n <Text style={[styles.star, { color: textColor }]}>★</Text>\n <Text style={[styles.text, { color: textColor }]}>{label}</Text>\n </View>\n );\n return onPress ? (\n <Pressable onPress={onPress} accessibilityRole=\"button\" accessibilityLabel={label}>\n {content}\n </Pressable>\n ) : (\n content\n );\n}\n\nconst styles = StyleSheet.create({\n badge: { flexDirection: \"row\", alignItems: \"center\", alignSelf: \"flex-start\", borderRadius: 16, paddingHorizontal: 12, paddingVertical: 6 },\n star: { fontSize: 13, marginRight: 6 },\n text: { fontSize: 13, fontWeight: \"700\" },\n});\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { NoriClient, NoriError, RewardDecision, RewardsSummary } from \"@getnori/core\";\n\nexport interface UseRewardsOptions {\n /** History entries to load. Default 20. */\n historyLimit?: number;\n /** Re-read the summary this long after a reward, to pick up credited status. Default 1500 ms. */\n refreshDelayMs?: number;\n}\n\nexport interface RewardsState {\n summary: RewardsSummary | null;\n loading: boolean;\n error: NoriError | Error | null;\n refresh: () => Promise<void>;\n}\n\n/** Calls `listener` with each reward the server grants this viewer. */\nexport function useRewardDecisions(client: Pick<NoriClient, \"on\">, listener: (decision: RewardDecision) => void): void {\n const ref = useRef(listener);\n ref.current = listener;\n useEffect(\n () =>\n client.on(\"reward\", (decisions) => {\n for (const d of decisions) ref.current(d);\n }),\n [client],\n );\n}\n\n/**\n * The viewer's Nori earnings. Updates the moment a view is rewarded, then\n * re-reads the server for the authoritative numbers.\n */\nexport function useRewards(client: NoriClient, { historyLimit = 20, refreshDelayMs = 1500 }: UseRewardsOptions = {}): RewardsState {\n const [summary, setSummary] = useState<RewardsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<NoriError | Error | null>(null);\n const live = useRef(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const refresh = useCallback(async () => {\n try {\n const next = await client.rewards({ limit: historyLimit });\n if (!live.current) return;\n setSummary(next);\n setError(null);\n } catch (e) {\n if (live.current) setError(e as Error);\n } finally {\n if (live.current) setLoading(false);\n }\n }, [client, historyLimit]);\n\n useEffect(() => {\n live.current = true;\n void refresh();\n return () => {\n live.current = false;\n if (timer.current) clearTimeout(timer.current);\n };\n }, [refresh]);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status !== \"rewarded\") return;\n setSummary((prev) =>\n prev\n ? {\n ...prev,\n total_points: prev.total_points + decision.points,\n today: {\n ...prev.today,\n points: prev.today.points + decision.points,\n remaining: Math.max(0, prev.today.remaining - decision.points),\n },\n }\n : prev,\n );\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => void refresh(), refreshDelayMs);\n });\n\n return { summary, loading, error, refresh };\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { Animated, StyleSheet, Text, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient, RewardDecision } from \"@getnori/core\";\nimport { useRewardDecisions } from \"./useRewards\";\n\nexport interface RewardToastsProps {\n client: NoriClient;\n /** Text for a reward. Default \"+10 points\". */\n format?: (decision: RewardDecision) => string;\n /** Tell the viewer once per session when they hit the daily cap. Default true. */\n showDailyCapNotice?: boolean;\n /** How long each toast stays. Default 2500 ms. */\n durationMs?: number;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface Toast {\n key: number;\n text: string;\n}\n\n/**\n * Shows \"+10 points\" when the server rewards a view. Mount it once, above\n * the feed (it positions itself at the top of its parent).\n */\nexport function RewardToasts({\n client,\n format = (d) => `+${d.points} ${d.currency_name}`,\n showDailyCapNotice = true,\n durationMs = 2500,\n backgroundColor = \"rgba(17,17,17,0.9)\",\n textColor = \"#ffffff\",\n style,\n}: RewardToastsProps) {\n const [toast, setToast] = useState<Toast | null>(null);\n const capShown = useRef(false);\n const counter = useRef(0);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status === \"rewarded\" && decision.points > 0) {\n setToast({ key: ++counter.current, text: format(decision) });\n } else if (decision.reason === \"daily_cap\" && showDailyCapNotice && !capShown.current) {\n capShown.current = true;\n setToast({ key: ++counter.current, text: `You've earned today's maximum ${decision.currency_name}. Come back tomorrow!` });\n }\n });\n\n if (!toast) return null;\n return (\n <ToastView\n key={toast.key}\n text={toast.text}\n durationMs={durationMs}\n backgroundColor={backgroundColor}\n textColor={textColor}\n style={style}\n onDone={() => setToast((t) => (t?.key === toast.key ? null : t))}\n />\n );\n}\n\nfunction ToastView({\n text,\n durationMs,\n backgroundColor,\n textColor,\n style,\n onDone,\n}: {\n text: string;\n durationMs: number;\n backgroundColor: string;\n textColor: string;\n style?: StyleProp<ViewStyle>;\n onDone: () => void;\n}) {\n const opacity = useRef(new Animated.Value(0)).current;\n const onDoneRef = useRef(onDone);\n onDoneRef.current = onDone;\n\n useEffect(() => {\n Animated.timing(opacity, { toValue: 1, duration: 180, useNativeDriver: true }).start();\n const timer = setTimeout(() => {\n Animated.timing(opacity, { toValue: 0, duration: 220, useNativeDriver: true }).start(() => onDoneRef.current());\n }, durationMs);\n return () => clearTimeout(timer);\n }, [durationMs, opacity]);\n\n return (\n <Animated.View\n pointerEvents=\"none\"\n style={[styles.toast, { backgroundColor, opacity }, style]}\n accessibilityLiveRegion=\"polite\"\n accessibilityRole=\"alert\"\n testID=\"nori-reward-toast\"\n >\n <Text style={[styles.text, { color: textColor }]}>{text}</Text>\n </Animated.View>\n );\n}\n\nconst styles = StyleSheet.create({\n toast: {\n position: \"absolute\", top: 56, alignSelf: \"center\", maxWidth: \"86%\",\n borderRadius: 20, paddingHorizontal: 16, paddingVertical: 10,\n },\n text: { fontSize: 15, fontWeight: \"700\", textAlign: \"center\" },\n});\n"],"mappings":"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","names":["useEffect","useRef","useState","StyleSheet","Text","jsx","useState","useRef","useEffect","styles","Text","StyleSheet"]}
|
|
1
|
+
{"version":3,"sources":["../src/PointsBadge.tsx","../src/useRewards.ts","../src/RewardToasts.tsx"],"sourcesContent":["import React from \"react\";\nimport { Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient } from \"@getnori/core\";\nimport { useRewards } from \"./useRewards\";\n\nexport interface PointsBadgeProps {\n client: NoriClient;\n /** What to show: all-time Nori earnings, today's (as a viewer), or what I earned as a creator. Default \"total\". */\n show?: \"total\" | \"today\" | \"creator\";\n onPress?: () => void;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** A small pill with the viewer's points, e.g. in the feed's corner. Hidden until loaded. */\nexport function PointsBadge({\n client,\n show = \"total\",\n onPress,\n backgroundColor = \"rgba(0,0,0,0.55)\",\n textColor = \"#ffffff\",\n style,\n}: PointsBadgeProps) {\n const { summary } = useRewards(client);\n if (!summary) return null;\n\n const points =\n show === \"today\" ? summary.today.points : show === \"creator\" ? summary.creator_points ?? 0 : summary.total_points;\n const suffix = show === \"today\" ? \" today\" : show === \"creator\" ? \" as creator\" : \"\";\n const label = `${points.toLocaleString()} ${summary.currency_name}${suffix}`;\n const content = (\n <View style={[styles.badge, { backgroundColor }, style]} testID=\"nori-points-badge\">\n <Text style={[styles.star, { color: textColor }]}>★</Text>\n <Text style={[styles.text, { color: textColor }]}>{label}</Text>\n </View>\n );\n return onPress ? (\n <Pressable onPress={onPress} accessibilityRole=\"button\" accessibilityLabel={label}>\n {content}\n </Pressable>\n ) : (\n content\n );\n}\n\nconst styles = StyleSheet.create({\n badge: { flexDirection: \"row\", alignItems: \"center\", alignSelf: \"flex-start\", borderRadius: 16, paddingHorizontal: 12, paddingVertical: 6 },\n star: { fontSize: 13, marginRight: 6 },\n text: { fontSize: 13, fontWeight: \"700\" },\n});\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { NoriClient, NoriError, RewardDecision, RewardsSummary } from \"@getnori/core\";\n\nexport interface UseRewardsOptions {\n /** History entries to load. Default 20. */\n historyLimit?: number;\n /** Re-read the summary this long after a reward, to pick up credited status. Default 1500 ms. */\n refreshDelayMs?: number;\n}\n\nexport interface RewardsState {\n summary: RewardsSummary | null;\n loading: boolean;\n error: NoriError | Error | null;\n refresh: () => Promise<void>;\n}\n\n/** Calls `listener` with each reward the server grants this viewer. */\nexport function useRewardDecisions(client: Pick<NoriClient, \"on\">, listener: (decision: RewardDecision) => void): void {\n const ref = useRef(listener);\n ref.current = listener;\n useEffect(\n () =>\n client.on(\"reward\", (decisions) => {\n for (const d of decisions) ref.current(d);\n }),\n [client],\n );\n}\n\n/**\n * The viewer's Nori earnings. Updates the moment a view is rewarded, then\n * re-reads the server for the authoritative numbers.\n */\nexport function useRewards(client: NoriClient, { historyLimit = 20, refreshDelayMs = 1500 }: UseRewardsOptions = {}): RewardsState {\n const [summary, setSummary] = useState<RewardsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<NoriError | Error | null>(null);\n const live = useRef(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const refresh = useCallback(async () => {\n try {\n const next = await client.rewards({ limit: historyLimit });\n if (!live.current) return;\n setSummary(next);\n setError(null);\n } catch (e) {\n if (live.current) setError(e as Error);\n } finally {\n if (live.current) setLoading(false);\n }\n }, [client, historyLimit]);\n\n useEffect(() => {\n live.current = true;\n void refresh();\n return () => {\n live.current = false;\n if (timer.current) clearTimeout(timer.current);\n };\n }, [refresh]);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status !== \"rewarded\") return;\n setSummary((prev) =>\n prev\n ? {\n ...prev,\n total_points: prev.total_points + decision.points,\n today: {\n ...prev.today,\n points: prev.today.points + decision.points,\n remaining: Math.max(0, prev.today.remaining - decision.points),\n },\n }\n : prev,\n );\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => void refresh(), refreshDelayMs);\n });\n\n return { summary, loading, error, refresh };\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { Animated, StyleSheet, Text, type StyleProp, type ViewStyle } from \"react-native\";\nimport type { NoriClient, RewardDecision } from \"@getnori/core\";\nimport { useRewardDecisions } from \"./useRewards\";\n\nexport interface RewardToastsProps {\n client: NoriClient;\n /** Text for a reward. Default \"+10 points\". */\n format?: (decision: RewardDecision) => string;\n /** Tell the viewer once per session when they hit the daily cap. Default true. */\n showDailyCapNotice?: boolean;\n /** How long each toast stays. Default 2500 ms. */\n durationMs?: number;\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface Toast {\n key: number;\n text: string;\n}\n\n/**\n * Shows \"+10 points\" when the server rewards a view. Mount it once, above\n * the feed (it positions itself at the top of its parent).\n */\nexport function RewardToasts({\n client,\n format = (d) => `+${d.points} ${d.currency_name}`,\n showDailyCapNotice = true,\n durationMs = 2500,\n backgroundColor = \"rgba(17,17,17,0.9)\",\n textColor = \"#ffffff\",\n style,\n}: RewardToastsProps) {\n const [toast, setToast] = useState<Toast | null>(null);\n const capShown = useRef(false);\n const counter = useRef(0);\n\n useRewardDecisions(client, (decision) => {\n if (decision.status === \"rewarded\" && decision.points > 0) {\n setToast({ key: ++counter.current, text: format(decision) });\n } else if (decision.reason === \"daily_cap\" && showDailyCapNotice && !capShown.current) {\n capShown.current = true;\n setToast({ key: ++counter.current, text: `You've earned today's maximum ${decision.currency_name}. Come back tomorrow!` });\n }\n });\n\n if (!toast) return null;\n return (\n <ToastView\n key={toast.key}\n text={toast.text}\n durationMs={durationMs}\n backgroundColor={backgroundColor}\n textColor={textColor}\n style={style}\n onDone={() => setToast((t) => (t?.key === toast.key ? null : t))}\n />\n );\n}\n\nfunction ToastView({\n text,\n durationMs,\n backgroundColor,\n textColor,\n style,\n onDone,\n}: {\n text: string;\n durationMs: number;\n backgroundColor: string;\n textColor: string;\n style?: StyleProp<ViewStyle>;\n onDone: () => void;\n}) {\n const opacity = useRef(new Animated.Value(0)).current;\n const onDoneRef = useRef(onDone);\n onDoneRef.current = onDone;\n\n useEffect(() => {\n Animated.timing(opacity, { toValue: 1, duration: 180, useNativeDriver: true }).start();\n const timer = setTimeout(() => {\n Animated.timing(opacity, { toValue: 0, duration: 220, useNativeDriver: true }).start(() => onDoneRef.current());\n }, durationMs);\n return () => clearTimeout(timer);\n }, [durationMs, opacity]);\n\n return (\n <Animated.View\n pointerEvents=\"none\"\n style={[styles.toast, { backgroundColor, opacity }, style]}\n accessibilityLiveRegion=\"polite\"\n accessibilityRole=\"alert\"\n testID=\"nori-reward-toast\"\n >\n <Text style={[styles.text, { color: textColor }]}>{text}</Text>\n </Animated.View>\n );\n}\n\nconst styles = StyleSheet.create({\n toast: {\n position: \"absolute\", top: 56, alignSelf: \"center\", maxWidth: \"86%\",\n borderRadius: 20, paddingHorizontal: 16, paddingVertical: 10,\n },\n text: { fontSize: 15, fontWeight: \"700\", textAlign: \"center\" },\n});\n"],"mappings":";AACA,SAAS,WAAW,YAAY,MAAM,YAA4C;;;ACDlF,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AAkBlD,SAAS,mBAAmB,QAAgC,UAAoD;AACrH,QAAM,MAAM,OAAO,QAAQ;AAC3B,MAAI,UAAU;AACd;AAAA,IACE,MACE,OAAO,GAAG,UAAU,CAAC,cAAc;AACjC,iBAAW,KAAK,UAAW,KAAI,QAAQ,CAAC;AAAA,IAC1C,CAAC;AAAA,IACH,CAAC,MAAM;AAAA,EACT;AACF;AAMO,SAAS,WAAW,QAAoB,EAAE,eAAe,IAAI,iBAAiB,KAAK,IAAuB,CAAC,GAAiB;AACjI,QAAM,CAAC,SAAS,UAAU,IAAI,SAAgC,IAAI;AAClE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAmC,IAAI;AACjE,QAAM,OAAO,OAAO,IAAI;AACxB,QAAM,QAAQ,OAA6C,IAAI;AAE/D,QAAM,UAAU,YAAY,YAAY;AACtC,QAAI;AACF,YAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,OAAO,aAAa,CAAC;AACzD,UAAI,CAAC,KAAK,QAAS;AACnB,iBAAW,IAAI;AACf,eAAS,IAAI;AAAA,IACf,SAAS,GAAG;AACV,UAAI,KAAK,QAAS,UAAS,CAAU;AAAA,IACvC,UAAE;AACA,UAAI,KAAK,QAAS,YAAW,KAAK;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,QAAQ,YAAY,CAAC;AAEzB,YAAU,MAAM;AACd,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,WAAO,MAAM;AACX,WAAK,UAAU;AACf,UAAI,MAAM,QAAS,cAAa,MAAM,OAAO;AAAA,IAC/C;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,qBAAmB,QAAQ,CAAC,aAAa;AACvC,QAAI,SAAS,WAAW,WAAY;AACpC;AAAA,MAAW,CAAC,SACV,OACI;AAAA,QACE,GAAG;AAAA,QACH,cAAc,KAAK,eAAe,SAAS;AAAA,QAC3C,OAAO;AAAA,UACL,GAAG,KAAK;AAAA,UACR,QAAQ,KAAK,MAAM,SAAS,SAAS;AAAA,UACrC,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,SAAS,MAAM;AAAA,QAC/D;AAAA,MACF,IACA;AAAA,IACN;AACA,QAAI,MAAM,QAAS,cAAa,MAAM,OAAO;AAC7C,UAAM,UAAU,WAAW,MAAM,KAAK,QAAQ,GAAG,cAAc;AAAA,EACjE,CAAC;AAED,SAAO,EAAE,SAAS,SAAS,OAAO,QAAQ;AAC5C;;;ADnDI,SACE,KADF;AAhBG,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ;AACF,GAAqB;AACnB,QAAM,EAAE,QAAQ,IAAI,WAAW,MAAM;AACrC,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,SACJ,SAAS,UAAU,QAAQ,MAAM,SAAS,SAAS,YAAY,QAAQ,kBAAkB,IAAI,QAAQ;AACvG,QAAM,SAAS,SAAS,UAAU,WAAW,SAAS,YAAY,gBAAgB;AAClF,QAAM,QAAQ,GAAG,OAAO,eAAe,CAAC,IAAI,QAAQ,aAAa,GAAG,MAAM;AAC1E,QAAM,UACJ,qBAAC,QAAK,OAAO,CAAC,OAAO,OAAO,EAAE,gBAAgB,GAAG,KAAK,GAAG,QAAO,qBAC9D;AAAA,wBAAC,QAAK,OAAO,CAAC,OAAO,MAAM,EAAE,OAAO,UAAU,CAAC,GAAG,oBAAC;AAAA,IACnD,oBAAC,QAAK,OAAO,CAAC,OAAO,MAAM,EAAE,OAAO,UAAU,CAAC,GAAI,iBAAM;AAAA,KAC3D;AAEF,SAAO,UACL,oBAAC,aAAU,SAAkB,mBAAkB,UAAS,oBAAoB,OACzE,mBACH,IAEA;AAEJ;AAEA,IAAM,SAAS,WAAW,OAAO;AAAA,EAC/B,OAAO,EAAE,eAAe,OAAO,YAAY,UAAU,WAAW,cAAc,cAAc,IAAI,mBAAmB,IAAI,iBAAiB,EAAE;AAAA,EAC1I,MAAM,EAAE,UAAU,IAAI,aAAa,EAAE;AAAA,EACrC,MAAM,EAAE,UAAU,IAAI,YAAY,MAAM;AAC1C,CAAC;;;AElDD,SAAgB,aAAAA,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AACnD,SAAS,UAAU,cAAAC,aAAY,QAAAC,aAA4C;AAkDvE,gBAAAC,YAAA;AAxBG,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,SAAS,CAAC,MAAM,IAAI,EAAE,MAAM,IAAI,EAAE,aAAa;AAAA,EAC/C,qBAAqB;AAAA,EACrB,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ;AACF,GAAsB;AACpB,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAuB,IAAI;AACrD,QAAM,WAAWC,QAAO,KAAK;AAC7B,QAAM,UAAUA,QAAO,CAAC;AAExB,qBAAmB,QAAQ,CAAC,aAAa;AACvC,QAAI,SAAS,WAAW,cAAc,SAAS,SAAS,GAAG;AACzD,eAAS,EAAE,KAAK,EAAE,QAAQ,SAAS,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,IAC7D,WAAW,SAAS,WAAW,eAAe,sBAAsB,CAAC,SAAS,SAAS;AACrF,eAAS,UAAU;AACnB,eAAS,EAAE,KAAK,EAAE,QAAQ,SAAS,MAAM,iCAAiC,SAAS,aAAa,wBAAwB,CAAC;AAAA,IAC3H;AAAA,EACF,CAAC;AAED,MAAI,CAAC,MAAO,QAAO;AACnB,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MAEC,MAAM,MAAM;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,MAAM,SAAS,CAAC,MAAO,GAAG,QAAQ,MAAM,MAAM,OAAO,CAAE;AAAA;AAAA,IAN1D,MAAM;AAAA,EAOb;AAEJ;AAEA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,UAAUE,QAAO,IAAI,SAAS,MAAM,CAAC,CAAC,EAAE;AAC9C,QAAM,YAAYA,QAAO,MAAM;AAC/B,YAAU,UAAU;AAEpB,EAAAC,WAAU,MAAM;AACd,aAAS,OAAO,SAAS,EAAE,SAAS,GAAG,UAAU,KAAK,iBAAiB,KAAK,CAAC,EAAE,MAAM;AACrF,UAAM,QAAQ,WAAW,MAAM;AAC7B,eAAS,OAAO,SAAS,EAAE,SAAS,GAAG,UAAU,KAAK,iBAAiB,KAAK,CAAC,EAAE,MAAM,MAAM,UAAU,QAAQ,CAAC;AAAA,IAChH,GAAG,UAAU;AACb,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,YAAY,OAAO,CAAC;AAExB,SACE,gBAAAH;AAAA,IAAC,SAAS;AAAA,IAAT;AAAA,MACC,eAAc;AAAA,MACd,OAAO,CAACI,QAAO,OAAO,EAAE,iBAAiB,QAAQ,GAAG,KAAK;AAAA,MACzD,yBAAwB;AAAA,MACxB,mBAAkB;AAAA,MAClB,QAAO;AAAA,MAEP,0BAAAJ,KAACK,OAAA,EAAK,OAAO,CAACD,QAAO,MAAM,EAAE,OAAO,UAAU,CAAC,GAAI,gBAAK;AAAA;AAAA,EAC1D;AAEJ;AAEA,IAAMA,UAASE,YAAW,OAAO;AAAA,EAC/B,OAAO;AAAA,IACL,UAAU;AAAA,IAAY,KAAK;AAAA,IAAI,WAAW;AAAA,IAAU,UAAU;AAAA,IAC9D,cAAc;AAAA,IAAI,mBAAmB;AAAA,IAAI,iBAAiB;AAAA,EAC5D;AAAA,EACA,MAAM,EAAE,UAAU,IAAI,YAAY,OAAO,WAAW,SAAS;AAC/D,CAAC;","names":["useEffect","useRef","useState","StyleSheet","Text","jsx","useState","useRef","useEffect","styles","Text","StyleSheet"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getnori/rewards",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Nori Rewards SDK: points badges, reward toasts and earning history for Nori viewers.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@getnori/core": "^0.
|
|
35
|
+
"@getnori/core": "^0.2.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"react": "19.2.3",
|