@oxy.so/services 12.5.0 → 12.6.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/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +27 -14
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/FollowTargetButton.js +39 -51
- package/lib/commonjs/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/components/followButtonAccessibility.js +24 -7
- package/lib/commonjs/ui/components/followButtonAccessibility.js.map +1 -1
- package/lib/commonjs/ui/components/followLabels.js +38 -0
- package/lib/commonjs/ui/components/followLabels.js.map +1 -0
- package/lib/commonjs/ui/components/followRules.js +29 -13
- package/lib/commonjs/ui/components/followRules.js.map +1 -1
- package/lib/commonjs/ui/hooks/useI18n.js +2 -0
- package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
- package/lib/commonjs/ui/utils/deviceSessionTitle.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +28 -15
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/FollowTargetButton.js +39 -51
- package/lib/module/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/components/followButtonAccessibility.js +24 -7
- package/lib/module/ui/components/followButtonAccessibility.js.map +1 -1
- package/lib/module/ui/components/followLabels.js +35 -0
- package/lib/module/ui/components/followLabels.js.map +1 -0
- package/lib/module/ui/components/followRules.js +30 -13
- package/lib/module/ui/components/followRules.js.map +1 -1
- package/lib/module/ui/hooks/useI18n.js +2 -0
- package/lib/module/ui/hooks/useI18n.js.map +1 -1
- package/lib/module/ui/utils/deviceSessionTitle.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +3 -1
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts +13 -0
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts +6 -18
- package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +1 -2
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/followButtonAccessibility.d.ts +12 -2
- package/lib/typescript/commonjs/ui/components/followButtonAccessibility.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/followLabels.d.ts +28 -0
- package/lib/typescript/commonjs/ui/components/followLabels.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/followRules.d.ts +20 -9
- package/lib/typescript/commonjs/ui/components/followRules.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts +3 -1
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/deviceSessionTitle.d.ts +1 -2
- package/lib/typescript/commonjs/ui/utils/deviceSessionTitle.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +3 -1
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowButton.d.ts +13 -0
- package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowTargetButton.d.ts +6 -18
- package/lib/typescript/module/ui/components/FollowTargetButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +1 -2
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/followButtonAccessibility.d.ts +12 -2
- package/lib/typescript/module/ui/components/followButtonAccessibility.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/followLabels.d.ts +28 -0
- package/lib/typescript/module/ui/components/followLabels.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/followRules.d.ts +20 -9
- package/lib/typescript/module/ui/components/followRules.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useI18n.d.ts +3 -1
- package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/deviceSessionTitle.d.ts +1 -2
- package/lib/typescript/module/ui/utils/deviceSessionTitle.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +2 -5
- package/src/ui/client.ts +7 -5
- package/src/ui/components/FollowButton.tsx +53 -23
- package/src/ui/components/FollowTargetButton.tsx +39 -52
- package/src/ui/components/OxyConsentScreen.tsx +1 -3
- package/src/ui/components/__tests__/followButtonAccessibility.test.tsx +32 -6
- package/src/ui/components/__tests__/followTargetButton.test.ts +7 -1
- package/src/ui/components/authChooser/types.ts +1 -2
- package/src/ui/components/followButtonAccessibility.ts +38 -25
- package/src/ui/components/followLabels.ts +48 -0
- package/src/ui/components/followRules.ts +29 -13
- package/src/ui/hooks/useI18n.ts +5 -3
- package/src/ui/utils/deviceSessionTitle.ts +1 -2
|
@@ -8,9 +8,14 @@
|
|
|
8
8
|
* (OxyHQ/oxy#1375 item 22). The name now says the state, and who it is for
|
|
9
9
|
* when the caller knows the handle.
|
|
10
10
|
*
|
|
11
|
+
* The words follow the button's verb ("Joined @fediverse", hinted "Leaves
|
|
12
|
+
* @fediverse") and its language: every string is `@oxy.so/core`'s `follow.*`.
|
|
13
|
+
*
|
|
11
14
|
* Pure, and kept apart from the components, so every state is pinned by a
|
|
12
15
|
* table test rather than by a render that can only see one state at a time.
|
|
13
16
|
*/
|
|
17
|
+
import type { Translate } from '../hooks/useI18n.js';
|
|
18
|
+
import type { FollowLabels, FollowVerb } from './followLabels.js';
|
|
14
19
|
export interface FollowButtonAccessibility {
|
|
15
20
|
accessibilityLabel: string;
|
|
16
21
|
accessibilityHint?: string;
|
|
@@ -27,13 +32,18 @@ export declare function formatHandle(username: string | undefined): string | und
|
|
|
27
32
|
* because a press does nothing until the write settles.
|
|
28
33
|
* - Following: "Following @nate", hinted "Unfollows @nate", because pressing
|
|
29
34
|
* it unfollows, which the visible "Following" does not say.
|
|
35
|
+
*
|
|
36
|
+
* The name is the visible label (the caller's, when it passed `labels`) beside
|
|
37
|
+
* the handle; the checking line and the hint are the verb's.
|
|
30
38
|
*/
|
|
31
39
|
export declare function describeFollowButton(input: {
|
|
32
40
|
isKnown: boolean;
|
|
33
41
|
isFollowing: boolean;
|
|
34
42
|
isPending: boolean;
|
|
35
43
|
username?: string;
|
|
36
|
-
|
|
44
|
+
verb: FollowVerb;
|
|
45
|
+
labels: Pick<FollowLabels, 'idle' | 'active'>;
|
|
46
|
+
}, t: Translate): FollowButtonAccessibility;
|
|
37
47
|
/**
|
|
38
48
|
* The "Follow all" button. The labels are the caller's, since a starter pack
|
|
39
49
|
* and a feed's member list word it differently; the hint carries the count.
|
|
@@ -44,5 +54,5 @@ export declare function describeFollowAllButton(input: {
|
|
|
44
54
|
count: number;
|
|
45
55
|
followAllLabel: string;
|
|
46
56
|
followedAllLabel: string;
|
|
47
|
-
}): FollowButtonAccessibility;
|
|
57
|
+
}, t: Translate): FollowButtonAccessibility;
|
|
48
58
|
//# sourceMappingURL=followButtonAccessibility.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"followButtonAccessibility.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/followButtonAccessibility.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"followButtonAccessibility.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/followButtonAccessibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAgB,CAAC;AAE/D,MAAM,WAAW,yBAAyB;IACxC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG7E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE;IACL,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;CAC/C,EACD,CAAC,EAAE,SAAS,GACX,yBAAyB,CAqB3B;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE;IACL,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,EACD,CAAC,EAAE,SAAS,GACX,yBAAyB,CAQ3B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words on a follow button, for every verb the ecosystem uses.
|
|
3
|
+
*
|
|
4
|
+
* "Follow" is wrong for half the things an Oxy user can follow: you subscribe
|
|
5
|
+
* to a channel, you join a community, you watch a listing. Both follow buttons
|
|
6
|
+
* read their words here, translated through `@oxy.so/core`'s `follow.*`
|
|
7
|
+
* strings, so a verb reads the same — and in the same language — wherever it is
|
|
8
|
+
* drawn.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The small vocabulary of verbs the ecosystem actually uses. An application
|
|
12
|
+
* whose verb is not here passes `labels` instead — the escape hatch exists so
|
|
13
|
+
* this union never has to grow to accommodate one product's wording.
|
|
14
|
+
*/
|
|
15
|
+
export type FollowVerb = 'follow' | 'subscribe' | 'join' | 'watch';
|
|
16
|
+
export interface FollowLabels {
|
|
17
|
+
/** Not following yet. */
|
|
18
|
+
idle: string;
|
|
19
|
+
/** Following, and this application acts on it. */
|
|
20
|
+
active: string;
|
|
21
|
+
/** Asked, waiting for the other side to accept. */
|
|
22
|
+
pending: string;
|
|
23
|
+
/** Following globally, but switched off in this application. */
|
|
24
|
+
disabled: string;
|
|
25
|
+
}
|
|
26
|
+
/** The labels for `verb` in the current language, with the caller's `overrides` on top. */
|
|
27
|
+
export declare function useFollowLabels(verb?: FollowVerb, overrides?: Partial<FollowLabels>): FollowLabels;
|
|
28
|
+
//# sourceMappingURL=followLabels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"followLabels.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/followLabels.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAC7B,IAAI,GAAE,UAAqB,EAC3B,SAAS,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAChC,YAAY,CAYd"}
|
|
@@ -5,10 +5,16 @@
|
|
|
5
5
|
* application drawing its own affordance (a chip grid, a compact row) imports
|
|
6
6
|
* them from here — through `@oxy.so/services/ui/client` — without paying for the
|
|
7
7
|
* button's dropdown menu.
|
|
8
|
+
*
|
|
9
|
+
* The menu's words are `@oxy.so/core`'s `follow.*` strings, read through the
|
|
10
|
+
* caller's `t`; without one they are English.
|
|
8
11
|
*/
|
|
9
12
|
import type { FollowApplicationMode } from '@oxy.so/contracts';
|
|
13
|
+
import type { Translate } from '../hooks/useI18n.js';
|
|
14
|
+
import type { FollowVerb } from './followLabels.js';
|
|
10
15
|
/** A timed-follow choice offered in the menu. */
|
|
11
16
|
export interface FollowDuration {
|
|
17
|
+
/** The duration as it reads inside a sentence: "24 hours", "a week". */
|
|
12
18
|
label: string;
|
|
13
19
|
seconds: number;
|
|
14
20
|
}
|
|
@@ -39,15 +45,6 @@ export interface FollowMenuItem {
|
|
|
39
45
|
type: 'unfollow';
|
|
40
46
|
};
|
|
41
47
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Which choices exist, given the state.
|
|
44
|
-
*
|
|
45
|
-
* Pure and exported because this is the product decision, not a rendering
|
|
46
|
-
* detail: a timed follow is only offered before following, turning it off here
|
|
47
|
-
* is only offered while following, and NEITHER is offered before the server has
|
|
48
|
-
* answered — every one of them addresses a relationship that does not exist
|
|
49
|
-
* yet, so offering them mid-write would mean sending a guessed id.
|
|
50
|
-
*/
|
|
51
48
|
/**
|
|
52
49
|
* What the main button should do for the current state.
|
|
53
50
|
*
|
|
@@ -59,13 +56,27 @@ export declare function resolveFollowPrimaryAction(input: {
|
|
|
59
56
|
isFollowing: boolean;
|
|
60
57
|
applicationMode: FollowApplicationMode;
|
|
61
58
|
}): 'follow' | 'unfollow' | 'enable-here';
|
|
59
|
+
/**
|
|
60
|
+
* Which choices exist, given the state.
|
|
61
|
+
*
|
|
62
|
+
* Pure and exported because this is the product decision, not a rendering
|
|
63
|
+
* detail: a timed follow is only offered before following, turning it off here
|
|
64
|
+
* is only offered while following, and NEITHER is offered before the server has
|
|
65
|
+
* answered — every one of them addresses a relationship that does not exist
|
|
66
|
+
* yet, so offering them mid-write would mean sending a guessed id.
|
|
67
|
+
*/
|
|
62
68
|
export declare function buildFollowMenuItems(input: {
|
|
63
69
|
following: boolean;
|
|
64
70
|
applicationMode: FollowApplicationMode;
|
|
65
71
|
hasRelationship: boolean;
|
|
66
72
|
isPending: boolean;
|
|
67
73
|
durations: FollowDuration[] | false;
|
|
74
|
+
/** The button's idle label, which a timed choice starts with: "Follow for 24 hours". */
|
|
68
75
|
idleVerb: string;
|
|
69
76
|
applicationName: string;
|
|
77
|
+
/** Words the "everywhere" line with this verb: "Leave everywhere". Default `follow`. */
|
|
78
|
+
verb?: FollowVerb;
|
|
79
|
+
/** `useI18n().t`, for the menu in the app's language. Default English. */
|
|
80
|
+
t?: Translate;
|
|
70
81
|
}): FollowMenuItem[];
|
|
71
82
|
//# sourceMappingURL=followRules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"followRules.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/followRules.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"followRules.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/followRules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAgB,CAAC;AAEjD,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAKD;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAC9C,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,cAAc,GAAG,UAAU,EACvE,OAAO,CAUR,CAAC;AAEF,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EACF;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAChE;QAAE,IAAI,EAAE,aAAa,CAAA;KAAE,GACvB;QAAE,IAAI,EAAE,cAAc,CAAA;KAAE,GACxB;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,qBAAqB,CAAC;CACxC,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAIxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,qBAAqB,CAAC;IACvC,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;IACpC,wFAAwF;IACxF,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,0EAA0E;IAC1E,CAAC,CAAC,EAAE,SAAS,CAAC;CACf,GAAG,cAAc,EAAE,CAwCnB"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** `useI18n().t`: a key and its `{{placeholders}}` in, the current language's text out. */
|
|
2
|
+
export type Translate = (key: string, vars?: Record<string, string | number>) => string;
|
|
1
3
|
/**
|
|
2
4
|
* Display copy for SDK surfaces.
|
|
3
5
|
*
|
|
@@ -12,7 +14,7 @@
|
|
|
12
14
|
* Anything that reads or mutates session state uses `useOxy()`, which throws.
|
|
13
15
|
*/
|
|
14
16
|
export declare function useI18n(): {
|
|
15
|
-
t:
|
|
17
|
+
t: Translate;
|
|
16
18
|
locale: string;
|
|
17
19
|
};
|
|
18
20
|
//# sourceMappingURL=useI18n.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useI18n.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useI18n.ts"],"names":[],"mappings":"AAWA
|
|
1
|
+
{"version":3,"file":"useI18n.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useI18n.ts"],"names":[],"mappings":"AAWA,2FAA2F;AAC3F,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;AAExF;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO;;;EActB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DeviceLinkedSession } from '@oxy.so/core';
|
|
2
|
-
type Translate
|
|
2
|
+
import type { Translate } from '../hooks/useI18n.js';
|
|
3
3
|
/**
|
|
4
4
|
* The title of one row in "Sessions & devices".
|
|
5
5
|
*
|
|
@@ -14,5 +14,4 @@ type Translate = (key: string, vars?: Record<string, string | number>) => string
|
|
|
14
14
|
* response does carry. Never the string "undefined".
|
|
15
15
|
*/
|
|
16
16
|
export declare function deviceSessionTitle(session: Pick<DeviceLinkedSession, 'deviceName' | 'isCurrent' | 'user'>, t: Translate): string;
|
|
17
|
-
export {};
|
|
18
17
|
//# sourceMappingURL=deviceSessionTitle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceSessionTitle.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/deviceSessionTitle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"deviceSessionTitle.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/deviceSessionTitle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAkB,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,YAAY,GAAG,WAAW,GAAG,MAAM,CAAC,EACvE,CAAC,EAAE,SAAS,GACX,MAAM,CAQR"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/services",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.6.0",
|
|
4
4
|
"description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"@commitlint/config-conventional": "^21.2.0",
|
|
161
161
|
"@expo/app-integrity": "~58.0.2",
|
|
162
162
|
"@expo/vector-icons": "^15.0.3",
|
|
163
|
-
"@oxy.so/core": "6.
|
|
163
|
+
"@oxy.so/core": "6.4.0",
|
|
164
164
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
165
165
|
"@react-native-community/netinfo": "^12.0.1",
|
|
166
166
|
"@react-native/babel-preset": "^0.88.0",
|
|
@@ -210,12 +210,12 @@
|
|
|
210
210
|
"expo-video": "~58.0.11"
|
|
211
211
|
},
|
|
212
212
|
"//@oxy.so/bloom": "Bloom 11.1.0 adds the shared grouped sections, usage card, subscription plans, frame appearance and overlay navigation APIs used by this SDK. First-party Bloom peers remain open above this tested minimum.",
|
|
213
|
-
"//@oxy.so/core": "Core 6.
|
|
213
|
+
"//@oxy.so/core": "Core 6.4.0 supplies the `follow.*` strings the follow buttons read for every verb and language; 6.1.0 the `oxy.auth.email.{startChange,change,remove}` and `oxy.auth.password.remove` the sign-in & security panels call. Keep one Core instance per app; raise this floor with the root catalogue when new runtime APIs are required.",
|
|
214
214
|
"peerDependencies": {
|
|
215
215
|
"@expo/app-integrity": ">=56.0.0 <59.0.0",
|
|
216
216
|
"@expo/vector-icons": "^15.0.3",
|
|
217
217
|
"@oxy.so/bloom": ">=11.1.0",
|
|
218
|
-
"@oxy.so/core": "^6.
|
|
218
|
+
"@oxy.so/core": "^6.4.0",
|
|
219
219
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
220
220
|
"@react-native-community/netinfo": ">=11.4.1 <13.0.0",
|
|
221
221
|
"@tanstack/query-async-storage-persister": "^5.101",
|
package/src/index.ts
CHANGED
|
@@ -344,11 +344,8 @@ export {
|
|
|
344
344
|
FOLLOW_ACTION_LEAVES_ACTIVE,
|
|
345
345
|
} from './ui/components/followRules';
|
|
346
346
|
export type { FollowDuration, FollowMenuItem } from './ui/components/followRules';
|
|
347
|
-
export type {
|
|
348
|
-
|
|
349
|
-
FollowVerb,
|
|
350
|
-
FollowLabels,
|
|
351
|
-
} from './ui/components/FollowTargetButton';
|
|
347
|
+
export type { FollowTargetButtonProps } from './ui/components/FollowTargetButton';
|
|
348
|
+
export type { FollowVerb, FollowLabels } from './ui/components/followLabels';
|
|
352
349
|
export { useFollowTarget } from './ui/hooks/useFollowTarget';
|
|
353
350
|
export type { UseFollowTargetResult } from './ui/hooks/useFollowTarget';
|
|
354
351
|
export {
|
package/src/ui/client.ts
CHANGED
|
@@ -23,12 +23,14 @@ export type { OxyAuthPromptProps } from './components/OxyAuthPrompt';
|
|
|
23
23
|
export { LogoIcon } from './components/logo/LogoIcon';
|
|
24
24
|
export { LogoText } from './components/logo/LogoText';
|
|
25
25
|
export { default as FollowButton } from './components/FollowButton';
|
|
26
|
-
export { FollowTargetButton } from './components/FollowTargetButton';
|
|
27
26
|
export type {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} from './components/
|
|
27
|
+
FollowButtonProps,
|
|
28
|
+
SingleFollowButtonProps,
|
|
29
|
+
MultiFollowButtonProps,
|
|
30
|
+
} from './components/FollowButton';
|
|
31
|
+
export { FollowTargetButton } from './components/FollowTargetButton';
|
|
32
|
+
export type { FollowTargetButtonProps } from './components/FollowTargetButton';
|
|
33
|
+
export type { FollowVerb, FollowLabels } from './components/followLabels';
|
|
32
34
|
export { default as ProfileButton } from './components/ProfileButton';
|
|
33
35
|
export type { ProfileButtonProps } from './components/ProfileButton';
|
|
34
36
|
export { default as PeableButton } from './components/PeableButton';
|
|
@@ -8,10 +8,9 @@ import { useFollow, useFollowForButton } from '../hooks/useFollow';
|
|
|
8
8
|
import { useFollowStore } from '../stores/followStore';
|
|
9
9
|
import type { OxyServices, BulkFollowResult, BulkUnfollowResult } from '@oxy.so/core';
|
|
10
10
|
import { useShallow } from 'zustand/react/shallow';
|
|
11
|
+
import { useI18n } from '../hooks/useI18n';
|
|
11
12
|
import { describeFollowAllButton, describeFollowButton } from './followButtonAccessibility';
|
|
12
|
-
|
|
13
|
-
const DEFAULT_FOLLOW_ALL_LABEL = 'Follow all';
|
|
14
|
-
const DEFAULT_FOLLOWED_ALL_LABEL = 'Following';
|
|
13
|
+
import { type FollowLabels, type FollowVerb, useFollowLabels } from './followLabels';
|
|
15
14
|
|
|
16
15
|
/** Props shared by both single- and multi-user follow modes. */
|
|
17
16
|
interface FollowButtonBaseProps {
|
|
@@ -35,6 +34,16 @@ export interface SingleFollowButtonProps extends FollowButtonBaseProps {
|
|
|
35
34
|
* still follows the state.
|
|
36
35
|
*/
|
|
37
36
|
username?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The word for following this account: a fediverse community reads
|
|
39
|
+
* "Join"/"Joined". Translated, like every default label here.
|
|
40
|
+
*/
|
|
41
|
+
verb?: FollowVerb;
|
|
42
|
+
/**
|
|
43
|
+
* Overrides for a verb the vocabulary does not carry. This button shows
|
|
44
|
+
* `idle` and `active`; a user follow has no pending or switched-off state.
|
|
45
|
+
*/
|
|
46
|
+
labels?: Partial<FollowLabels>;
|
|
38
47
|
userIds?: never;
|
|
39
48
|
}
|
|
40
49
|
|
|
@@ -42,7 +51,9 @@ export interface SingleFollowButtonProps extends FollowButtonBaseProps {
|
|
|
42
51
|
export interface MultiFollowButtonProps extends FollowButtonBaseProps {
|
|
43
52
|
userIds: string[];
|
|
44
53
|
initiallyAllFollowing?: boolean;
|
|
54
|
+
/** Default: "Follow all", translated. */
|
|
45
55
|
followAllLabel?: string;
|
|
56
|
+
/** Default: "Following", translated. */
|
|
46
57
|
followedAllLabel?: string;
|
|
47
58
|
onBulkFollow?: (result: BulkFollowResult) => void;
|
|
48
59
|
onBulkUnfollow?: (result: BulkUnfollowResult) => void;
|
|
@@ -63,6 +74,8 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
63
74
|
oxyServices,
|
|
64
75
|
initiallyFollowing,
|
|
65
76
|
username,
|
|
77
|
+
verb = 'follow',
|
|
78
|
+
labels,
|
|
66
79
|
size = 'md',
|
|
67
80
|
onFollowChange,
|
|
68
81
|
style,
|
|
@@ -71,6 +84,8 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
71
84
|
showLoadingState = true,
|
|
72
85
|
preventParentActions = true,
|
|
73
86
|
}: SingleFollowButtonProps & { oxyServices: OxyServices }) {
|
|
87
|
+
const { t } = useI18n();
|
|
88
|
+
const text = useFollowLabels(verb, labels);
|
|
74
89
|
const { isFollowing, isKnown, isLoading, toggleFollow, resolveStatus } = useFollowForButton(
|
|
75
90
|
userId,
|
|
76
91
|
oxyServices,
|
|
@@ -91,10 +106,19 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
91
106
|
if (accepted) onFollowChange?.(!isFollowing);
|
|
92
107
|
} catch (err: unknown) {
|
|
93
108
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
94
|
-
toast.error(error.message || '
|
|
109
|
+
toast.error(error.message || t('follow.errors.update'));
|
|
95
110
|
}
|
|
96
111
|
},
|
|
97
|
-
[
|
|
112
|
+
[
|
|
113
|
+
disabled,
|
|
114
|
+
isLoading,
|
|
115
|
+
isKnown,
|
|
116
|
+
toggleFollow,
|
|
117
|
+
onFollowChange,
|
|
118
|
+
isFollowing,
|
|
119
|
+
preventParentActions,
|
|
120
|
+
t,
|
|
121
|
+
],
|
|
98
122
|
);
|
|
99
123
|
|
|
100
124
|
// Enqueue a batched status fetch ONLY when the status is genuinely unknown
|
|
@@ -110,12 +134,10 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
110
134
|
const isBusy = isLoading || !isKnown;
|
|
111
135
|
const showFollowing = isKnown && isFollowing;
|
|
112
136
|
const showSpinner = showLoadingState && isBusy;
|
|
113
|
-
const { accessibilityLabel, accessibilityHint } = describeFollowButton(
|
|
114
|
-
isKnown,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
username,
|
|
118
|
-
});
|
|
137
|
+
const { accessibilityLabel, accessibilityHint } = describeFollowButton(
|
|
138
|
+
{ isKnown, isFollowing: showFollowing, isPending: isLoading, username, verb, labels: text },
|
|
139
|
+
t,
|
|
140
|
+
);
|
|
119
141
|
|
|
120
142
|
return (
|
|
121
143
|
<BloomFollowButton
|
|
@@ -128,7 +150,8 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
128
150
|
disabled={disabled || isBusy}
|
|
129
151
|
style={style}
|
|
130
152
|
textStyle={textStyle}
|
|
131
|
-
label={isKnown || showSpinner ?
|
|
153
|
+
label={isKnown || showSpinner ? text.idle : ''}
|
|
154
|
+
followingLabel={text.active}
|
|
132
155
|
accessibilityLabel={accessibilityLabel}
|
|
133
156
|
accessibilityHint={accessibilityHint}
|
|
134
157
|
/>
|
|
@@ -139,8 +162,8 @@ const FollowButtonMultiInner = memo(function FollowButtonMultiInner({
|
|
|
139
162
|
userIds,
|
|
140
163
|
initiallyAllFollowing = false,
|
|
141
164
|
size = 'md',
|
|
142
|
-
followAllLabel
|
|
143
|
-
followedAllLabel
|
|
165
|
+
followAllLabel: followAllLabelProp,
|
|
166
|
+
followedAllLabel: followedAllLabelProp,
|
|
144
167
|
onFollowChange,
|
|
145
168
|
onBulkFollow,
|
|
146
169
|
onBulkUnfollow,
|
|
@@ -150,6 +173,9 @@ const FollowButtonMultiInner = memo(function FollowButtonMultiInner({
|
|
|
150
173
|
showLoadingState = true,
|
|
151
174
|
preventParentActions = true,
|
|
152
175
|
}: MultiFollowButtonProps) {
|
|
176
|
+
const { t } = useI18n();
|
|
177
|
+
const followAllLabel = followAllLabelProp ?? t('follow.all.idle');
|
|
178
|
+
const followedAllLabel = followedAllLabelProp ?? t('follow.verbs.follow.active');
|
|
153
179
|
const follow = useFollow(userIds);
|
|
154
180
|
const followAllUsers = 'followAllUsers' in follow ? follow.followAllUsers : undefined;
|
|
155
181
|
const unfollowAllUsers = 'unfollowAllUsers' in follow ? follow.unfollowAllUsers : undefined;
|
|
@@ -209,7 +235,7 @@ const FollowButtonMultiInner = memo(function FollowButtonMultiInner({
|
|
|
209
235
|
onBulkUnfollow?.(result);
|
|
210
236
|
} catch (err: unknown) {
|
|
211
237
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
212
|
-
toast.error(error.message || '
|
|
238
|
+
toast.error(error.message || t('follow.errors.update'));
|
|
213
239
|
} finally {
|
|
214
240
|
setIsSubmitting(false);
|
|
215
241
|
}
|
|
@@ -236,7 +262,7 @@ const FollowButtonMultiInner = memo(function FollowButtonMultiInner({
|
|
|
236
262
|
onBulkFollow?.(result);
|
|
237
263
|
} catch (err: unknown) {
|
|
238
264
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
239
|
-
toast.error(error.message || '
|
|
265
|
+
toast.error(error.message || t('follow.errors.update'));
|
|
240
266
|
} finally {
|
|
241
267
|
setIsSubmitting(false);
|
|
242
268
|
}
|
|
@@ -251,17 +277,21 @@ const FollowButtonMultiInner = memo(function FollowButtonMultiInner({
|
|
|
251
277
|
onBulkFollow,
|
|
252
278
|
onBulkUnfollow,
|
|
253
279
|
preventParentActions,
|
|
280
|
+
t,
|
|
254
281
|
],
|
|
255
282
|
);
|
|
256
283
|
|
|
257
284
|
const showSpinner = showLoadingState && isLoading;
|
|
258
|
-
const { accessibilityLabel, accessibilityHint } = describeFollowAllButton(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
285
|
+
const { accessibilityLabel, accessibilityHint } = describeFollowAllButton(
|
|
286
|
+
{
|
|
287
|
+
allFollowing,
|
|
288
|
+
isPending: Boolean(isLoading),
|
|
289
|
+
count: userIds.length,
|
|
290
|
+
followAllLabel,
|
|
291
|
+
followedAllLabel,
|
|
292
|
+
},
|
|
293
|
+
t,
|
|
294
|
+
);
|
|
265
295
|
|
|
266
296
|
return (
|
|
267
297
|
<BloomFollowButton
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
* small vocabulary of defaults and a full override, rather than a lookup keyed
|
|
23
23
|
* on kind — a kind→verb table in this package would need a release every time
|
|
24
24
|
* an application invents a kind, which is exactly the coupling #809 removes.
|
|
25
|
+
* The vocabulary and its translated words live in `followLabels`, shared with
|
|
26
|
+
* `FollowButton`.
|
|
25
27
|
*
|
|
26
28
|
* ## Relationship to the legacy `FollowButton`
|
|
27
29
|
*
|
|
@@ -58,55 +60,28 @@ import {
|
|
|
58
60
|
type FollowDuration,
|
|
59
61
|
type FollowMenuItem,
|
|
60
62
|
} from './followRules';
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* The small vocabulary of verbs the ecosystem actually uses. An application
|
|
64
|
-
* whose verb is not here passes `labels` instead — the escape hatch exists so
|
|
65
|
-
* this union never has to grow to accommodate one product's wording.
|
|
66
|
-
*/
|
|
67
|
-
export type FollowVerb = 'follow' | 'subscribe' | 'join' | 'watch';
|
|
68
|
-
|
|
69
|
-
export interface FollowLabels {
|
|
70
|
-
/** Not following yet. */
|
|
71
|
-
idle: string;
|
|
72
|
-
/** Following, and this application acts on it. */
|
|
73
|
-
active: string;
|
|
74
|
-
/** Asked, waiting for the other side to accept. */
|
|
75
|
-
pending: string;
|
|
76
|
-
/** Following globally, but switched off in this application. */
|
|
77
|
-
disabled: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const VERB_LABELS: Record<FollowVerb, FollowLabels> = {
|
|
81
|
-
follow: { idle: 'Follow', active: 'Following', pending: 'Requested', disabled: 'Off here' },
|
|
82
|
-
subscribe: {
|
|
83
|
-
idle: 'Subscribe',
|
|
84
|
-
active: 'Subscribed',
|
|
85
|
-
pending: 'Requested',
|
|
86
|
-
disabled: 'Off here',
|
|
87
|
-
},
|
|
88
|
-
join: { idle: 'Join', active: 'Joined', pending: 'Requested', disabled: 'Off here' },
|
|
89
|
-
watch: { idle: 'Watch', active: 'Watching', pending: 'Requested', disabled: 'Off here' },
|
|
90
|
-
};
|
|
63
|
+
import { type FollowLabels, type FollowVerb, useFollowLabels } from './followLabels';
|
|
64
|
+
import { useI18n } from '../hooks/useI18n';
|
|
91
65
|
|
|
92
66
|
const HOUR = 60 * 60;
|
|
93
67
|
|
|
94
68
|
/**
|
|
95
69
|
* Defaults chosen for the cases a timed follow is actually for: a live event
|
|
96
70
|
* tonight, a story running this week. Not a general-purpose duration picker —
|
|
97
|
-
* an application that needs one passes its own.
|
|
71
|
+
* an application that needs one passes its own. Labels are catalog keys, read
|
|
72
|
+
* at render in the app's language.
|
|
98
73
|
*/
|
|
99
|
-
const DEFAULT_DURATIONS
|
|
100
|
-
{
|
|
101
|
-
{
|
|
102
|
-
{
|
|
103
|
-
];
|
|
74
|
+
const DEFAULT_DURATIONS = [
|
|
75
|
+
{ key: 'follow.durations.day', seconds: 24 * HOUR },
|
|
76
|
+
{ key: 'follow.durations.threeDays', seconds: 72 * HOUR },
|
|
77
|
+
{ key: 'follow.durations.week', seconds: 7 * 24 * HOUR },
|
|
78
|
+
] as const;
|
|
104
79
|
|
|
105
80
|
export interface FollowTargetButtonProps {
|
|
106
81
|
/** The registered target's id. Registration is separate — see the SDK. */
|
|
107
82
|
targetId: string;
|
|
108
83
|
verb?: FollowVerb;
|
|
109
|
-
/** Full override, for a verb the vocabulary
|
|
84
|
+
/** Full override, for a verb the vocabulary does not carry. */
|
|
110
85
|
labels?: Partial<FollowLabels>;
|
|
111
86
|
size?: 'sm' | 'md' | 'lg';
|
|
112
87
|
style?: StyleProp<ViewStyle>;
|
|
@@ -118,7 +93,8 @@ export interface FollowTargetButtonProps {
|
|
|
118
93
|
showOptions?: boolean;
|
|
119
94
|
/**
|
|
120
95
|
* Offer timed follows. Pass `false` where a temporary follow makes no sense —
|
|
121
|
-
* a store you buy from, an account you know.
|
|
96
|
+
* a store you buy from, an account you know. Default: 24 hours, 72 hours and
|
|
97
|
+
* a week, translated.
|
|
122
98
|
*/
|
|
123
99
|
durations?: FollowDuration[] | false;
|
|
124
100
|
/**
|
|
@@ -156,7 +132,7 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
156
132
|
style,
|
|
157
133
|
disabled = false,
|
|
158
134
|
showOptions = true,
|
|
159
|
-
durations
|
|
135
|
+
durations,
|
|
160
136
|
applicationName,
|
|
161
137
|
initialStatus,
|
|
162
138
|
onChange,
|
|
@@ -164,13 +140,18 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
164
140
|
const { status, isFollowing, isUnknown, isPending, follow, unfollow, disableHere, enableHere } =
|
|
165
141
|
useFollowTarget(targetId, { initialStatus });
|
|
166
142
|
|
|
143
|
+
const { t } = useI18n();
|
|
144
|
+
const text = useFollowLabels(verb, labels);
|
|
145
|
+
|
|
146
|
+
const timedChoices = useMemo<FollowDuration[] | false>(
|
|
147
|
+
() => durations ?? DEFAULT_DURATIONS.map(({ key, seconds }) => ({ label: t(key), seconds })),
|
|
148
|
+
[durations, t],
|
|
149
|
+
);
|
|
150
|
+
|
|
167
151
|
const reportMutationFailure = useCallback(() => {
|
|
168
|
-
const message =
|
|
169
|
-
useFollowTargetStore.getState().errors[targetId] ?? 'Could not update follow status';
|
|
152
|
+
const message = useFollowTargetStore.getState().errors[targetId] ?? t('follow.errors.update');
|
|
170
153
|
toast.error(message);
|
|
171
|
-
}, [targetId]);
|
|
172
|
-
|
|
173
|
-
const text = useMemo(() => ({ ...VERB_LABELS[verb], ...labels }), [verb, labels]);
|
|
154
|
+
}, [targetId, t]);
|
|
174
155
|
|
|
175
156
|
const label = useMemo(() => {
|
|
176
157
|
if (status.globalState === 'requested') return text.pending;
|
|
@@ -224,9 +205,9 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
224
205
|
// The confirmation is the point: a follow that ends on its own is a
|
|
225
206
|
// promise, and a user who does not see it made will not believe it.
|
|
226
207
|
// Which is also why it must not appear when the promise was not made.
|
|
227
|
-
toast.success(
|
|
208
|
+
toast.success(t('follow.menu.forDuration', { label: text.active, duration: durationLabel }));
|
|
228
209
|
},
|
|
229
|
-
[follow, onChange, reportMutationFailure],
|
|
210
|
+
[follow, onChange, reportMutationFailure, t, text.active],
|
|
230
211
|
);
|
|
231
212
|
|
|
232
213
|
const menuItems = useMemo(
|
|
@@ -236,18 +217,22 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
236
217
|
applicationMode: status.applicationMode,
|
|
237
218
|
hasRelationship: Boolean(status.relationshipId),
|
|
238
219
|
isPending,
|
|
239
|
-
durations,
|
|
220
|
+
durations: timedChoices,
|
|
240
221
|
idleVerb: text.idle,
|
|
241
|
-
applicationName: applicationName ?? '
|
|
222
|
+
applicationName: applicationName ?? t('follow.menu.thisApp'),
|
|
223
|
+
verb,
|
|
224
|
+
t,
|
|
242
225
|
}),
|
|
243
226
|
[
|
|
244
227
|
isFollowing,
|
|
245
228
|
status.applicationMode,
|
|
246
229
|
status.relationshipId,
|
|
247
230
|
isPending,
|
|
248
|
-
|
|
231
|
+
timedChoices,
|
|
249
232
|
text.idle,
|
|
250
233
|
applicationName,
|
|
234
|
+
verb,
|
|
235
|
+
t,
|
|
251
236
|
],
|
|
252
237
|
);
|
|
253
238
|
|
|
@@ -304,6 +289,8 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
304
289
|
return <View style={style}>{primary}</View>;
|
|
305
290
|
}
|
|
306
291
|
|
|
292
|
+
const optionsLabel = t('follow.menu.options', { label });
|
|
293
|
+
|
|
307
294
|
return (
|
|
308
295
|
<View style={[{ flexDirection: 'row', alignItems: 'center', gap: 4 }, style]}>
|
|
309
296
|
{primary}
|
|
@@ -321,11 +308,11 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
321
308
|
size={size === 'lg' ? 'lg' : 'sm'}
|
|
322
309
|
icon={<ChevronDown width={16} />}
|
|
323
310
|
disabled={disabled || isUnknown}
|
|
324
|
-
accessibilityLabel={
|
|
325
|
-
accessibilityHint=
|
|
311
|
+
accessibilityLabel={optionsLabel}
|
|
312
|
+
accessibilityHint={t('follow.menu.optionsHint')}
|
|
326
313
|
/>
|
|
327
314
|
</DropdownMenuTrigger>
|
|
328
|
-
<DropdownMenuContent label={
|
|
315
|
+
<DropdownMenuContent label={optionsLabel}>
|
|
329
316
|
{menuItems.map((item) => (
|
|
330
317
|
<DropdownMenuItem key={item.key} onPress={() => runItem(item)}>
|
|
331
318
|
{item.label}
|
|
@@ -27,7 +27,7 @@ import { Avatar } from '@oxy.so/bloom/avatar';
|
|
|
27
27
|
import { Button, LinkButton } from '@oxy.so/bloom/button';
|
|
28
28
|
import { Text } from '@oxy.so/bloom/typography';
|
|
29
29
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
30
|
-
import { useI18n } from '../hooks/useI18n';
|
|
30
|
+
import { type Translate, useI18n } from '../hooks/useI18n';
|
|
31
31
|
import { LogoIcon } from './logo/LogoIcon';
|
|
32
32
|
|
|
33
33
|
/** The requesting application, resolved by the caller (never user-supplied raw). */
|
|
@@ -133,8 +133,6 @@ const SCOPE_LABEL_KEYS: Readonly<Record<string, string>> = {
|
|
|
133
133
|
'federation:write': 'consent.scopes.federationWrite',
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
-
type Translate = ReturnType<typeof useI18n>['t'];
|
|
137
|
-
|
|
138
136
|
/** One permission row of the "Permissions requested" list. */
|
|
139
137
|
interface PermissionRow {
|
|
140
138
|
/** Stable list key / testID suffix — the first scope that produced this row. */
|