@xiao-ying/miniapp-hooks 1.1.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.
Files changed (48) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +47 -0
  3. package/dist/index.d.ts +11 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +12 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/use-auth-state.d.ts +18 -0
  8. package/dist/use-auth-state.d.ts.map +1 -0
  9. package/dist/use-auth-state.js +44 -0
  10. package/dist/use-auth-state.js.map +1 -0
  11. package/dist/use-brightness.d.ts +13 -0
  12. package/dist/use-brightness.d.ts.map +1 -0
  13. package/dist/use-brightness.js +38 -0
  14. package/dist/use-brightness.js.map +1 -0
  15. package/dist/use-foreground.d.ts +12 -0
  16. package/dist/use-foreground.d.ts.map +1 -0
  17. package/dist/use-foreground.js +24 -0
  18. package/dist/use-foreground.js.map +1 -0
  19. package/dist/use-haptic-feedback.d.ts +20 -0
  20. package/dist/use-haptic-feedback.d.ts.map +1 -0
  21. package/dist/use-haptic-feedback.js +21 -0
  22. package/dist/use-haptic-feedback.js.map +1 -0
  23. package/dist/use-media-query.d.ts +20 -0
  24. package/dist/use-media-query.d.ts.map +1 -0
  25. package/dist/use-media-query.js +36 -0
  26. package/dist/use-media-query.js.map +1 -0
  27. package/dist/use-sdk-error.d.ts +47 -0
  28. package/dist/use-sdk-error.d.ts.map +1 -0
  29. package/dist/use-sdk-error.js +60 -0
  30. package/dist/use-sdk-error.js.map +1 -0
  31. package/dist/use-share.d.ts +23 -0
  32. package/dist/use-share.d.ts.map +1 -0
  33. package/dist/use-share.js +72 -0
  34. package/dist/use-share.js.map +1 -0
  35. package/dist/use-storage.d.ts +37 -0
  36. package/dist/use-storage.d.ts.map +1 -0
  37. package/dist/use-storage.js +132 -0
  38. package/dist/use-storage.js.map +1 -0
  39. package/package.json +43 -0
  40. package/src/index.ts +36 -0
  41. package/src/use-auth-state.ts +51 -0
  42. package/src/use-brightness.ts +44 -0
  43. package/src/use-foreground.ts +28 -0
  44. package/src/use-haptic-feedback.ts +32 -0
  45. package/src/use-media-query.ts +48 -0
  46. package/src/use-sdk-error.ts +110 -0
  47. package/src/use-share.ts +84 -0
  48. package/src/use-storage.ts +170 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-share.js","sourceRoot":"","sources":["../src/use-share.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * useStorage 的配置项。
3
+ *
4
+ * @param defaultValue - 初始值(当 storage 为空时使用)。
5
+ * @param disabled - 禁用读写与订阅。
6
+ */
7
+ export interface UseStorageOptions<T> {
8
+ defaultValue?: T;
9
+ disabled?: boolean;
10
+ }
11
+ /**
12
+ * useStorage 的返回结果。
13
+ *
14
+ * - `value`: 当前值
15
+ * - `loading`: 是否在加载
16
+ * - `error`: 最近一次错误
17
+ * - `setValue/remove/refresh`: 操作方法
18
+ */
19
+ export interface UseStorageResult<T> {
20
+ value: T | null;
21
+ loading: boolean;
22
+ error?: unknown;
23
+ setValue: (next: T) => Promise<void>;
24
+ remove: () => Promise<void>;
25
+ refresh: () => Promise<void>;
26
+ }
27
+ /**
28
+ * 读取并订阅小应小程序存储的指定 key。
29
+ *
30
+ * @param key - storage key。
31
+ * @param options - 读取配置。
32
+ *
33
+ * @example
34
+ * const storage = useStorage<string>('nickname', { defaultValue: '游客' })
35
+ */
36
+ export declare const useStorage: <T = any>(key: string, options?: UseStorageOptions<T>) => UseStorageResult<T>;
37
+ //# sourceMappingURL=use-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-storage.d.ts","sourceRoot":"","sources":["../src/use-storage.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,GAAG,EAChC,KAAK,MAAM,EACX,UAAS,iBAAiB,CAAC,CAAC,CAAM,KACjC,gBAAgB,CAAC,CAAC,CA+HpB,CAAA"}
@@ -0,0 +1,132 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ /**
3
+ * 读取并订阅小应小程序存储的指定 key。
4
+ *
5
+ * @param key - storage key。
6
+ * @param options - 读取配置。
7
+ *
8
+ * @example
9
+ * const storage = useStorage<string>('nickname', { defaultValue: '游客' })
10
+ */
11
+ export const useStorage = (key, options = {}) => {
12
+ const { defaultValue, disabled = false } = options;
13
+ const initialDefault = defaultValue ?? null;
14
+ const isMounted = useRef(true);
15
+ const [value, setValueState] = useState(initialDefault);
16
+ const [loading, setLoading] = useState(!disabled);
17
+ const [error, setError] = useState();
18
+ useEffect(() => {
19
+ isMounted.current = true;
20
+ return () => {
21
+ isMounted.current = false;
22
+ };
23
+ }, []);
24
+ const setValueSafe = useCallback((next) => {
25
+ if (!isMounted.current)
26
+ return;
27
+ setValueState(next);
28
+ }, []);
29
+ const setLoadingSafe = useCallback((next) => {
30
+ if (!isMounted.current)
31
+ return;
32
+ setLoading(next);
33
+ }, []);
34
+ const setErrorSafe = useCallback((next) => {
35
+ if (!isMounted.current)
36
+ return;
37
+ setError(next);
38
+ }, []);
39
+ const refresh = useCallback(async () => {
40
+ if (disabled) {
41
+ setValueSafe(initialDefault);
42
+ setLoadingSafe(false);
43
+ setErrorSafe(undefined);
44
+ return;
45
+ }
46
+ setLoadingSafe(true);
47
+ try {
48
+ const res = await xy.getStorage({ key });
49
+ const resolved = res?.data === null || typeof res?.data === 'undefined' ? initialDefault : res.data;
50
+ setValueSafe(resolved);
51
+ setErrorSafe(undefined);
52
+ }
53
+ catch (err) {
54
+ setErrorSafe(err);
55
+ throw err;
56
+ }
57
+ finally {
58
+ setLoadingSafe(false);
59
+ }
60
+ }, [disabled, initialDefault, key, setErrorSafe, setLoadingSafe, setValueSafe, xy]);
61
+ useEffect(() => {
62
+ void refresh();
63
+ }, [refresh]);
64
+ useEffect(() => {
65
+ if (disabled || typeof xy.onStorageChange !== 'function') {
66
+ return;
67
+ }
68
+ const unsubscribe = xy.onStorageChange((event) => {
69
+ if (!event)
70
+ return;
71
+ if (event.key === '*' || event.key === key) {
72
+ const nextValue = event.key === '*' ? initialDefault : (event.newValue ?? initialDefault);
73
+ setValueSafe(nextValue);
74
+ setErrorSafe(undefined);
75
+ }
76
+ });
77
+ return () => {
78
+ if (typeof unsubscribe === 'function') {
79
+ unsubscribe();
80
+ }
81
+ };
82
+ }, [disabled, initialDefault, key, setErrorSafe, setValueSafe, xy]);
83
+ const setValue = useCallback(async (next) => {
84
+ if (disabled)
85
+ return;
86
+ setLoadingSafe(true);
87
+ try {
88
+ const res = await xy.setStorage({ key, data: next });
89
+ if (res?.success === false) {
90
+ throw new Error('SET_STORAGE_FAILED');
91
+ }
92
+ setValueSafe(next);
93
+ setErrorSafe(undefined);
94
+ }
95
+ catch (err) {
96
+ setErrorSafe(err);
97
+ throw err;
98
+ }
99
+ finally {
100
+ setLoadingSafe(false);
101
+ }
102
+ }, [disabled, key, setErrorSafe, setLoadingSafe, setValueSafe, xy]);
103
+ const remove = useCallback(async () => {
104
+ if (disabled)
105
+ return;
106
+ setLoadingSafe(true);
107
+ try {
108
+ const res = await xy.removeStorage({ key });
109
+ if (res?.success === false) {
110
+ throw new Error('REMOVE_STORAGE_FAILED');
111
+ }
112
+ setValueSafe(initialDefault);
113
+ setErrorSafe(undefined);
114
+ }
115
+ catch (err) {
116
+ setErrorSafe(err);
117
+ throw err;
118
+ }
119
+ finally {
120
+ setLoadingSafe(false);
121
+ }
122
+ }, [disabled, initialDefault, key, setErrorSafe, setLoadingSafe, setValueSafe, xy]);
123
+ return {
124
+ value,
125
+ loading,
126
+ error,
127
+ setValue,
128
+ remove,
129
+ refresh
130
+ };
131
+ };
132
+ //# sourceMappingURL=use-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-storage.js","sourceRoot":"","sources":["../src/use-storage.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@xiao-ying/miniapp-hooks",
3
+ "version": "1.1.0",
4
+ "description": "React hooks for @xiao-ying/miniapp-sdk to build reactive miniapp UIs",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "sideEffects": false,
20
+ "license": "Shanghai Plum Technology Ltd. Software License Agreement",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "author": "Liplum",
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "peerDependencies": {
29
+ "react": "^18.0.0 || ^19.0.0",
30
+ "@xiao-ying/miniapp-sdk": "^1.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^19.2.5",
34
+ "react": "^19.2.0",
35
+ "typescript": "^5.9.3",
36
+ "@xiao-ying/miniapp-sdk": "^1.1.0"
37
+ },
38
+ "scripts": {
39
+ "build": "node ../scripts/clean-dist.mjs && tsc -p tsconfig.json",
40
+ "build:publish": "node ../scripts/clean-dist.mjs && tsc -p tsconfig.json",
41
+ "check": "tsc -p tsconfig.json --noEmit"
42
+ }
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ 'use client'
2
+
3
+ export { useBrightness } from './use-brightness.js'
4
+ export { useForeground } from './use-foreground.js'
5
+ export {
6
+ useMediaQuery,
7
+ type UseMediaQueryResult
8
+ } from './use-media-query.js'
9
+ export {
10
+ useStorage,
11
+ type UseStorageOptions,
12
+ type UseStorageResult
13
+ } from './use-storage.js'
14
+ export { useShare } from './use-share.js'
15
+ export { useHapticFeedback, type UseHapticFeedbackOptions } from './use-haptic-feedback.js'
16
+ export {
17
+ useSdkError,
18
+ type UseSdkErrorOptions,
19
+ type UseSdkErrorResult,
20
+ type UseSdkErrorFilter
21
+ } from './use-sdk-error.js'
22
+ // Backward compatibility
23
+ export { useHapticFeedback as useHapticPress } from './use-haptic-feedback.js'
24
+ export { useAuthState } from './use-auth-state.js'
25
+
26
+ export type {
27
+ XYSdk,
28
+ XYMediaQuery,
29
+ XYSafeAreaInsets,
30
+ XYViewportSize,
31
+ XYBrightnessEvent,
32
+ XYStorageEvent,
33
+ XyShareData,
34
+ XyShareHandlerPayload,
35
+ MaybePromise
36
+ } from '@xiao-ying/miniapp-sdk'
@@ -0,0 +1,51 @@
1
+ import { useEffect, useMemo, useState } from 'react'
2
+ import type { XYAuthState } from '@xiao-ying/miniapp-sdk'
3
+
4
+ // undefined 代表无 XIAOYING_AUTH 权限;其余用 XYAuthState 表示。
5
+ type AuthState = XYAuthState | undefined
6
+
7
+ const readAuthState = (): AuthState => {
8
+ try {
9
+ const current = xy.getAuthState()
10
+ if (typeof current === 'undefined') return undefined
11
+ return current ?? { state: 'notSignedIn' }
12
+ } catch (_) {
13
+ return { state: 'notSignedIn' }
14
+ }
15
+ }
16
+
17
+ /**
18
+ * 获取并订阅宿主认证状态,提供常用派生字段。
19
+ *
20
+ * @returns `{ authState, hasAuthPermission, isSignedIn, userId }`
21
+ *
22
+ * @example
23
+ * const { isSignedIn } = useAuthState()
24
+ */
25
+ export const useAuthState = () => {
26
+ const [authState, setAuthState] = useState<AuthState>(() => readAuthState())
27
+
28
+ useEffect(() => {
29
+ if (typeof xy.onAuthStateChange !== 'function') return
30
+ const unsubscribe = xy.onAuthStateChange((next) => {
31
+ if (typeof next === 'undefined') {
32
+ setAuthState(undefined)
33
+ } else {
34
+ setAuthState(next ?? { state: 'notSignedIn' })
35
+ }
36
+ })
37
+ return unsubscribe
38
+ }, [])
39
+
40
+ const derived = useMemo(
41
+ () => ({
42
+ authState,
43
+ hasAuthPermission: typeof authState !== 'undefined',
44
+ isSignedIn: authState?.state === 'signedIn',
45
+ userId: authState?.state === 'signedIn' ? authState.userId : undefined,
46
+ }),
47
+ [authState]
48
+ )
49
+
50
+ return derived
51
+ }
@@ -0,0 +1,44 @@
1
+ import { useEffect, useMemo, useState } from 'react'
2
+
3
+ /**
4
+ * 订阅宿主亮度变化并返回当前亮度信息。
5
+ *
6
+ * @returns `{ brightness, isDark }`,亮度变化会触发重新渲染。
7
+ *
8
+ * @example
9
+ * const { brightness, isDark } = useBrightness()
10
+ */
11
+ export const useBrightness = (): { brightness: 'light' | 'dark'; isDark: boolean } => {
12
+ const [brightness, setBrightness] = useState<'light' | 'dark'>(() => {
13
+ try {
14
+ return xy.brightness() === 'dark' ? 'dark' : 'light'
15
+ } catch (_) {
16
+ return 'light'
17
+ }
18
+ })
19
+
20
+ useEffect(() => {
21
+ if (typeof xy.onBrightnessChange !== 'function') {
22
+ return
23
+ }
24
+
25
+ const unsubscribe = xy.onBrightnessChange((event) => {
26
+ const next = event?.brightness === 'dark' ? 'dark' : 'light'
27
+ setBrightness(next)
28
+ })
29
+
30
+ return () => {
31
+ if (typeof unsubscribe === 'function') {
32
+ unsubscribe()
33
+ }
34
+ }
35
+ }, [xy])
36
+
37
+ return useMemo(
38
+ () => ({
39
+ brightness,
40
+ isDark: brightness === 'dark'
41
+ }),
42
+ [brightness]
43
+ )
44
+ }
@@ -0,0 +1,28 @@
1
+ import { useEffect, useMemo, useState } from 'react'
2
+ /**
3
+ * 订阅前后台状态变化并返回当前前台状态。
4
+ *
5
+ * @returns `{ isForeground }`。
6
+ *
7
+ * @example
8
+ * const { isForeground } = useForeground()
9
+ */
10
+ export const useForeground = (): { isForeground: boolean } => {
11
+ const [isForeground, setIsForeground] = useState<boolean>(() => !!xy.isForeground())
12
+
13
+ useEffect(() => {
14
+ const offBg = xy.onBackground(() => setIsForeground(false))
15
+ const offFg = xy.onForeground(() => setIsForeground(true))
16
+ return () => {
17
+ offBg()
18
+ offFg()
19
+ }
20
+ }, [])
21
+
22
+ return useMemo(
23
+ () => ({
24
+ isForeground
25
+ }),
26
+ [isForeground]
27
+ )
28
+ }
@@ -0,0 +1,32 @@
1
+ import { useCallback } from 'react'
2
+ import type { XYVibrateImpact } from '@xiao-ying/miniapp-sdk'
3
+
4
+ /**
5
+ * 触感反馈 Hook 的配置项。
6
+ *
7
+ * @param impact - 震动强度;传 `false` 禁用。
8
+ * @param disabled - 是否禁用。
9
+ */
10
+ export interface UseHapticFeedbackOptions {
11
+ impact?: XYVibrateImpact | false
12
+ disabled?: boolean
13
+ }
14
+
15
+ /**
16
+ * 返回可复用的触感反馈触发函数;禁用或不支持时为空操作。
17
+ *
18
+ * @example
19
+ * const haptic = useHapticFeedback({ impact: 'medium' })
20
+ * <button onClick={haptic}>Click</button>
21
+ */
22
+ export const useHapticFeedback = (options: UseHapticFeedbackOptions = {}): (() => void) => {
23
+ const { impact = 'selection', disabled = false } = options
24
+
25
+ return useCallback(() => {
26
+ if (disabled || impact === false) return
27
+ if (typeof xy.vibrate !== 'function') return
28
+ void xy.vibrate({ impact }).catch(() => {
29
+ /* best-effort */
30
+ })
31
+ }, [disabled, impact, xy])
32
+ }
@@ -0,0 +1,48 @@
1
+ import { useMemo } from 'react'
2
+ import type { XYMediaQuery } from '@xiao-ying/miniapp-sdk'
3
+
4
+ /**
5
+ * useMediaQuery 的返回结果(安全区、尺寸、DPR 等)。
6
+ *
7
+ * @property safeAreaInsets - 安全区内边距(px)。
8
+ * @property size - 视口尺寸(px)。
9
+ * @property devicePixelRatio - 设备 DPR。
10
+ */
11
+ export interface UseMediaQueryResult extends XYMediaQuery {
12
+ }
13
+
14
+ const clampInset = (value?: number): number => {
15
+ if (typeof value !== 'number' || !Number.isFinite(value)) return 0
16
+ return Math.max(0, value)
17
+ }
18
+
19
+ /**
20
+ * 获取媒体查询信息(安全区/尺寸/DPR),基于当前 `xy.mediaQuery` 快照。
21
+ *
22
+ * @returns 规范化后的媒体查询信息(数值兜底)。
23
+ *
24
+ * @example
25
+ * const { safeAreaInsets } = useMediaQuery()
26
+ */
27
+ export const useMediaQuery = (): UseMediaQueryResult => {
28
+ return useMemo(() => {
29
+ const mediaQuery = (xy.mediaQuery ?? {}) as Partial<XYMediaQuery>
30
+ const insets = mediaQuery.safeAreaInsets ?? {}
31
+ const statusBarHeight = clampInset(mediaQuery.statusBarHeight)
32
+ const devicePixelRatio =
33
+ typeof mediaQuery.devicePixelRatio === 'number' && Number.isFinite(mediaQuery.devicePixelRatio)
34
+ ? mediaQuery.devicePixelRatio
35
+ : 1
36
+ return {
37
+ statusBarHeight,
38
+ safeAreaInsets: {
39
+ top: clampInset(insets.top),
40
+ bottom: clampInset(insets.bottom),
41
+ left: clampInset(insets.left),
42
+ right: clampInset(insets.right)
43
+ },
44
+ size: mediaQuery.size,
45
+ devicePixelRatio
46
+ }
47
+ }, [xy.mediaQuery])
48
+ }
@@ -0,0 +1,110 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react'
2
+ import type { XYErrorInfo } from '@xiao-ying/miniapp-sdk'
3
+
4
+ type XYRuntime = XYErrorInfo['runtime']
5
+
6
+ /**
7
+ * SDK 错误过滤条件。
8
+ *
9
+ * - `method`: 过滤指定方法名
10
+ * - `code`: 过滤指定错误码
11
+ * - `runtime`: 过滤运行时(app/browser/unknown)
12
+ */
13
+ export interface UseSdkErrorFilter {
14
+ method?: string | string[]
15
+ code?: string | string[]
16
+ runtime?: XYRuntime | XYRuntime[]
17
+ }
18
+
19
+ /**
20
+ * useSdkError 配置项。
21
+ *
22
+ * @param enabled - 是否启用订阅(默认 `true`)。
23
+ * @param limit - 缓存的错误数量上限(默认 `8`)。
24
+ * @param filter - 过滤条件。
25
+ */
26
+ export interface UseSdkErrorOptions {
27
+ enabled?: boolean
28
+ limit?: number
29
+ filter?: UseSdkErrorFilter
30
+ }
31
+
32
+ /**
33
+ * useSdkError 返回值。
34
+ *
35
+ * - `latest`: 最新错误
36
+ * - `errors`: 错误列表
37
+ * - `clear`: 清空错误
38
+ */
39
+ export interface UseSdkErrorResult {
40
+ latest?: XYErrorInfo
41
+ errors: XYErrorInfo[]
42
+ clear: () => void
43
+ }
44
+
45
+ const normalizeFilterSet = <T extends string>(value?: T | T[]): Set<T> | undefined => {
46
+ if (typeof value === 'undefined') return undefined
47
+ const items = Array.isArray(value) ? value : [value]
48
+ const normalized = items.filter((item) => typeof item === 'string' && item.length > 0) as T[]
49
+ if (normalized.length === 0) return undefined
50
+ return new Set(normalized)
51
+ }
52
+
53
+ /**
54
+ * 订阅 SDK 错误并在组件内维护错误列表。
55
+ *
56
+ * @example
57
+ * const { latest, errors, clear } = useSdkError({ filter: { method: 'request' } })
58
+ */
59
+ export const useSdkError = (options: UseSdkErrorOptions = {}): UseSdkErrorResult => {
60
+ const { enabled = true, limit = 8, filter } = options
61
+ const resolvedLimit = Number.isFinite(limit) ? limit : 8
62
+
63
+ const methodSet = useMemo(() => normalizeFilterSet(filter?.method), [filter?.method])
64
+ const codeSet = useMemo(() => normalizeFilterSet(filter?.code), [filter?.code])
65
+ const runtimeSet = useMemo(() => normalizeFilterSet(filter?.runtime), [filter?.runtime])
66
+
67
+ const matchesFilter = useCallback(
68
+ (info: XYErrorInfo) => {
69
+ if (methodSet && !methodSet.has(info.method)) return false
70
+ if (codeSet && !codeSet.has(info.code)) return false
71
+ if (runtimeSet && !runtimeSet.has(info.runtime)) return false
72
+ return true
73
+ },
74
+ [methodSet, codeSet, runtimeSet]
75
+ )
76
+
77
+ const [errors, setErrors] = useState<XYErrorInfo[]>([])
78
+
79
+ useEffect(() => {
80
+ if (!enabled || typeof xy.onError !== 'function') {
81
+ return
82
+ }
83
+
84
+ const unsubscribe = xy.onError((info) => {
85
+ if (!matchesFilter(info)) return
86
+ setErrors((prev) => {
87
+ const next = [info, ...prev]
88
+ if (resolvedLimit <= 0) return next
89
+ return next.slice(0, resolvedLimit)
90
+ })
91
+ })
92
+
93
+ return () => {
94
+ if (typeof unsubscribe === 'function') {
95
+ unsubscribe()
96
+ }
97
+ }
98
+ }, [enabled, matchesFilter, resolvedLimit, xy])
99
+
100
+ const clear = useCallback(() => setErrors([]), [])
101
+
102
+ return useMemo(
103
+ () => ({
104
+ latest: errors[0],
105
+ errors,
106
+ clear
107
+ }),
108
+ [errors, clear]
109
+ )
110
+ }
@@ -0,0 +1,84 @@
1
+ import { useEffect, useMemo } from 'react'
2
+ import type { MaybePromise, XyShareData, XyShareHandlerPayload } from '@xiao-ying/miniapp-sdk'
3
+
4
+ type SharePayload = Omit<XyShareHandlerPayload, 'path'> & {
5
+ path?: string
6
+ }
7
+
8
+ const buildDefaultSharePath = (): string | undefined => {
9
+ if (typeof window === 'undefined' || typeof window.location === 'undefined') return undefined
10
+ const { hash, pathname, search } = window.location
11
+
12
+ // Hash Router:使用 hash 部分作为真实路径(包含查询)
13
+ if (hash && hash !== '#') {
14
+ const hashBody = hash.slice(1) // 去掉开头的 #
15
+ const normalized = hashBody.startsWith('/') ? hashBody : `/${hashBody}`
16
+ return `/#${normalized}`
17
+ }
18
+
19
+ // 非 hash:直接使用 pathname + search
20
+ const safePath = pathname && pathname.startsWith('/') ? pathname : `/${pathname ?? ''}`
21
+ const safeSearch = typeof search === 'string' ? search : ''
22
+ return `${safePath || '/'}${safeSearch}`
23
+ }
24
+
25
+ const normalizeSharePath = (path: string): string => {
26
+ const hashIndex = path.indexOf('#')
27
+ if (hashIndex === -1) return path
28
+ const afterHash = path.slice(hashIndex + 1)
29
+ if (!afterHash) return '/'
30
+ return afterHash.startsWith('/') ? afterHash : `/${afterHash}`
31
+ }
32
+
33
+ const buildDefaultShareTitle = (): string => {
34
+ const name = typeof xy.manifest?.name === 'string' ? xy.manifest.name.trim() : ''
35
+ return name || '小应生活'
36
+ }
37
+
38
+ type ShareHandler = () => MaybePromise<SharePayload | XyShareData | null | undefined>
39
+
40
+ /**
41
+ * 注册分享处理器,自动补齐默认 path 与 title。
42
+ *
43
+ * - 传入函数:自定义动态返回分享数据
44
+ * - 传入对象:作为静态配置
45
+ * - 不传:使用默认配置(自动 path/title)
46
+ *
47
+ * @param input - 处理器函数或静态分享对象。
48
+ *
49
+ * @example
50
+ * useShare(() => ({
51
+ * title: '示例',
52
+ * path: '/home'
53
+ * }))
54
+ */
55
+ export const useShare = (input?: ShareHandler | SharePayload | XyShareData | null): void => {
56
+ const resolveHandler = useMemo<ShareHandler>(() => {
57
+ if (typeof input === 'function') return input
58
+ if (!input) return () => ({})
59
+ const payload = input
60
+ return () => payload
61
+ }, [input])
62
+
63
+ const wrappedHandler = useMemo(
64
+ () => async () => {
65
+ const res = await resolveHandler()
66
+ if (!res || typeof res !== 'object') return res
67
+
68
+ let path = res.path ?? buildDefaultSharePath()
69
+ const title = res.title ?? buildDefaultShareTitle()
70
+ if (!path) return null
71
+ path = normalizeSharePath(path)
72
+
73
+ // 返回符合 XyShareData 的结构,确保 path 为 string
74
+ const { path: _ignoredPath, title: _ignoredTitle, ...rest } = res
75
+ return { ...rest, path, title } as XyShareData
76
+ },
77
+ [resolveHandler]
78
+ )
79
+
80
+ useEffect(() => {
81
+ xy.setShareHandler(wrappedHandler)
82
+ return () => xy.setShareHandler(null)
83
+ }, [wrappedHandler, xy])
84
+ }