Adds support for displaying Linkat board links on user profiles. The Links tab appears when a user has configured a blue.linkat.board record with link cards.
-3
src/components/ProfileLinkatSection.tsx
-3
src/components/ProfileLinkatSection.tsx
src/screens/Profile/Sections/Linkat.tsx
src/screens/Profile/Sections/Linkat.tsx
This file has not been changed.
src/state/queries/linkat.ts
src/state/queries/linkat.ts
This file has not been changed.
src/view/screens/Profile.tsx
src/view/screens/Profile.tsx
This file has not been changed.
+15
-7
README.md
+15
-7
README.md
···
21
21
- Embed player works with [stream.place](https://stream.place/) links!
22
22
- Open skeets in PDSls and original pages of bridged posts
23
23
- You can redraft skeets
24
-
- Better defaults (alt text required 😉 autoplay off 🫨)
24
+
- Better defaults (alt text required 😉)
25
25
- More unique repost icons
26
26
- Can download videos
27
27
- 'Mutuals' in place of 'Following' when relevant
···
50
50
- Toggle similar account recommendations
51
51
- Toggle to make all user avatars square (like labelers)
52
52
- Toggle for more square-ish UI (still slightly rounded)
53
-
- Toggle to remove the composer prompt at the top of the Following & Discover feeds
54
-
- Change post translation provider (between Google, Kagi, Papago, and LibreTranslate)
55
53
56
54
#### Metrics
57
55
···
66
64
- following
67
65
- & who someone's followed by
68
66
67
+
#### Gates
68
+
69
+
- Toggle for an alternate share icon
70
+
- Toggle to show feed context for debugging
71
+
- Toggle to hide the 'show latest' button
72
+
- Toggle to make reply button open thread from feeds
73
+
- More may be available in developer mode? Often less 🤷
74
+
- (Accessible by holding the version in the About settings screen)
75
+
69
76
## Upcoming or wishful features
70
77
71
78
- Better OpenGraph support for sharing profiles & skeets (including videos & fixing quotes)
···
76
83
### TODO: Xan
77
84
78
85
- [ ] Setup App Linking for Android (.well-known w/ app package fingerprint)
79
-
- [ ] Fallback/email addresses to use witchsky.social in Automatic PDS detection
86
+
- [ ] Automatic PDS detection like other social-app forks (fallback/email addresses to use witchsky.social)
80
87
- [ ] Change followed accounts [on onboarding](https://github.com/blacksky-algorithms/blacksky.community/commit/e36ee43efb4999f070860d7f70122e45b28c1e2b)
81
-
- [ ] Join date & switch accounts in composer from a fork like [deer.aylac.top](https://github.com/ayla6/deer-social-test)
88
+
- [ ] Join date & switch accounts from composer from a fork like [deer.aylac.top](https://github.com/ayla6/deer-social-test)
82
89
- [ ] Visual replies indicator like the [Firmament userstyle](https://witchsky.app/profile/did:plc:jwhxcrf5uvl3vyw7nurecgt5/post/3m4rr3vzmak2a) (and likes?)
90
+
- [ ] Additional translation service providers + setting (Deepl, Kagi)
83
91
- [ ] Put DeerSettings into separate subpages
84
92
- [ ] After subpages for options, add [Outlinks page](https://witchsky.app/profile/did:plc:q7suwaz53ztc4mbiqyygbn43/post/3m5zjhhshic2g) &
85
93
- [ ] ShareMenuItems.tsx, ShareMenuItems.web.tsx
86
94
- [ ] For profile meatball button, Open profile in PDSls & Open bridged OG fedi account page
87
95
- [ ] ProfileMenu.tsx
88
-
- [ ] Witchsky PDS and .social site (list good songs containing 'bitch' in their titles for related site)
96
+
- [ ] Witchsky PDS and .social site (list good songs containing 'bitch' in their titles)
89
97
90
98
### Even more wishful or far off
91
99
···
93
101
- [ ] Submit releases to the Google Play Store and iOS App Store
94
102
- [ ] Move from [Cloudflare Pages](https://pages.cloudflare.com/) to [wisp.place](https://wisp.place/) (needs serverless for embeds)
95
103
- [ ] Toggle between handle and DID in share links
96
-
- [ ] Move TOS and privacy policy to Jollywhoppers website
104
+
- [ ] Move TOS and privacy policy to Jollywhoppers website?
97
105
- [ ] Ignore `!no-unauthenticated` labels
98
106
- [ ] Material 3 Expressive theming on Android (Liquid **ass on iOS)
99
107
+1
-10
src/lib/hooks/useTranslate.ts
+1
-10
src/lib/hooks/useTranslate.ts
···
1
1
import {useCallback} from 'react'
2
2
import * as IntentLauncher from 'expo-intent-launcher'
3
3
4
-
import {
5
-
getTranslatorLink,
6
-
getTranslatorLinkKagi,
7
-
getTranslatorLinkLibreTranslate,
8
-
getTranslatorLinkPapago,
9
-
} from '#/locale/helpers'
4
+
import {getTranslatorLink, getTranslatorLinkKagi} from '#/locale/helpers'
10
5
import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference'
11
6
import {IS_ANDROID} from '#/env'
12
7
import {useOpenLink} from './useOpenLink'
···
24
19
// it is a mystery https://www.youtube.com/watch?v=fq3abPnEEGE
25
20
if (translationServicePreference == 'kagi') {
26
21
translateUrl = getTranslatorLinkKagi(text, language)
27
-
} else if (translationServicePreference == 'papago') {
28
-
translateUrl = getTranslatorLinkPapago(text, language)
29
-
} else if (translationServicePreference == 'libreTranslate') {
30
-
translateUrl = getTranslatorLinkLibreTranslate(text, language)
31
22
} else {
32
23
translateUrl = getTranslatorLink(text, language)
33
24
}
-17
src/locale/helpers.ts
-17
src/locale/helpers.ts
···
3
3
import lande from 'lande'
4
4
5
5
import {hasProp} from '#/lib/type-guards'
6
-
import * as persisted from '#/state/persisted'
7
6
import {
8
7
AppLanguage,
9
8
type Language,
···
139
138
return `https://translate.kagi.com/?from=auto&to=${lang}&text=${encodeURIComponent(
140
139
text,
141
140
)}`
142
-
}
143
-
144
-
export function getTranslatorLinkPapago(text: string, lang: string): string {
145
-
return `https://papago.naver.com/?sk=auto&tk=${lang}&st=${encodeURIComponent(
146
-
text,
147
-
)}`
148
-
}
149
-
150
-
export function getTranslatorLinkLibreTranslate(
151
-
text: string,
152
-
lang: string,
153
-
): string {
154
-
const instance =
155
-
persisted.get('libreTranslateInstance') ??
156
-
persisted.defaults.libreTranslateInstance!
157
-
return `${instance}?source=auto&target=${lang}&q=${encodeURIComponent(text)}`
158
141
}
159
142
160
143
/**
+19
-142
src/screens/Settings/DeerSettings.tsx
+19
-142
src/screens/Settings/DeerSettings.tsx
···
108
108
useShowLinkInHandle,
109
109
} from '#/state/preferences/show-link-in-handle.tsx'
110
110
import {
111
-
useLibreTranslateInstance,
112
-
useSetLibreTranslateInstance,
113
111
useSetTranslationServicePreference,
114
112
useTranslationServicePreference,
115
113
} from '#/state/preferences/translation-service-preference'
···
130
128
import {Star_Stroke2_Corner0_Rounded as StarIcon} from '#/components/icons/Star'
131
129
import {Verified_Stroke2_Corner2_Rounded as VerifiedIcon} from '#/components/icons/Verified'
132
130
import * as Layout from '#/components/Layout'
133
-
import {InlineLinkText} from '#/components/Link'
134
131
import {Text} from '#/components/Typography'
135
132
import {IS_WEB} from '#/env'
136
133
import {SearchProfileCard} from '../Search/components/SearchProfileCard'
···
145
142
const pal = usePalette('default')
146
143
const {_} = useLingui()
147
144
148
-
const constellationInstance = useConstellationInstance()
149
-
const [url, setUrl] = useState(constellationInstance ?? '')
145
+
const [url, setUrl] = useState('')
150
146
const setConstellationInstance = useSetConstellationInstance()
151
147
152
148
const submit = () => {
···
166
162
<Dialog.Outer
167
163
control={control}
168
164
nativeOptions={{preventExpansion: true}}
169
-
onClose={() => setUrl(constellationInstance ?? '')}>
165
+
onClose={() => setUrl('')}>
170
166
<Dialog.Handle />
171
167
<Dialog.ScrollableInner label={_(msg`Constellations instance URL`)}>
172
168
<View style={[a.gap_sm, a.pb_lg]}>
···
189
185
accessibilityHint={_(
190
186
msg`Input the url of the constellations instance to use`,
191
187
)}
192
-
defaultValue={constellationInstance}
193
-
/>
194
-
195
-
<View style={IS_WEB && [a.flex_row, a.justify_end]}>
196
-
<Button
197
-
label={_(msg`Save`)}
198
-
size="large"
199
-
onPress={submit}
200
-
variant="solid"
201
-
color="primary"
202
-
disabled={shouldDisable()}>
203
-
<ButtonText>
204
-
<Trans>Save</Trans>
205
-
</ButtonText>
206
-
</Button>
207
-
</View>
208
-
</View>
209
-
210
-
<Dialog.Close />
211
-
</Dialog.ScrollableInner>
212
-
</Dialog.Outer>
213
-
)
214
-
}
215
-
216
-
function LibreTranslateInstanceDialog({
217
-
control,
218
-
}: {
219
-
control: Dialog.DialogControlProps
220
-
}) {
221
-
const pal = usePalette('default')
222
-
const {_} = useLingui()
223
-
224
-
const libreTranslateInstance = useLibreTranslateInstance()
225
-
const [url, setUrl] = useState(libreTranslateInstance ?? '')
226
-
const setLibreTranslateInstance = useSetLibreTranslateInstance()
227
-
228
-
const submit = () => {
229
-
setLibreTranslateInstance(url)
230
-
control.close()
231
-
}
232
-
233
-
const shouldDisable = () => {
234
-
try {
235
-
return !new URL(url).hostname.includes('.')
236
-
} catch (e) {
237
-
return true
238
-
}
239
-
}
240
-
241
-
return (
242
-
<Dialog.Outer
243
-
control={control}
244
-
nativeOptions={{preventExpansion: true}}
245
-
onClose={() => setUrl(libreTranslateInstance ?? '')}>
246
-
<Dialog.Handle />
247
-
<Dialog.ScrollableInner label={_(msg`LibreTranslate instance URL`)}>
248
-
<View style={[a.gap_sm, a.pb_lg]}>
249
-
<Text style={[a.text_2xl, a.font_bold]}>
250
-
<Trans>LibreTranslate instance URL</Trans>
251
-
</Text>
252
-
</View>
253
-
254
-
<View style={a.gap_lg}>
255
-
<Dialog.Input
256
-
label="Text input field"
257
-
autoFocus
258
-
style={[styles.textInput, pal.border, pal.text]}
259
-
onChangeText={value => {
260
-
setUrl(value)
261
-
}}
262
-
placeholder={persisted.defaults.libreTranslateInstance}
263
-
placeholderTextColor={pal.colors.textLight}
264
-
onSubmitEditing={submit}
265
-
accessibilityHint={_(
266
-
msg`Input the url of the LibreTranslate instance to use`,
267
-
)}
268
-
defaultValue={libreTranslateInstance}
269
188
/>
270
189
271
190
<View style={IS_WEB && [a.flex_row, a.justify_end]}>
···
423
342
const translationServicePreference = useTranslationServicePreference()
424
343
const setTranslationServicePreference = useSetTranslationServicePreference()
425
344
426
-
const setLibreTranslateInstanceControl = Dialog.useDialogControl()
427
-
428
345
return (
429
346
<Layout.Screen>
430
347
<Layout.Header.Outer>
···
472
389
<Toggle.LabelText style={[a.flex_1]}>
473
390
<Trans>
474
391
Fetch records directly from PDS to see contents of blocked and
475
-
detached quotes
392
+
detatched quotes
476
393
</Trans>
477
394
</Toggle.LabelText>
478
395
<Toggle.Platform />
···
563
480
<Trans>
564
481
Constellation is used to supplement AppView responses for custom
565
482
verifications and nuclear block bypass, via backlinks. Current
566
-
instance:
567
-
<InlineLinkText
568
-
to={constellationInstance}
569
-
label={constellationInstance}>
570
-
{constellationInstance}
571
-
</InlineLinkText>
483
+
instance: {constellationInstance}
572
484
</Trans>
573
485
</Admonition>
574
486
</SettingsList.Item>
575
-
576
-
<SettingsList.Divider />
577
487
578
488
<SettingsList.Group contentContainerStyle={[a.gap_sm]}>
579
489
<SettingsList.ItemIcon icon={PaintRollerIcon} />
···
733
643
</Admonition>
734
644
</SettingsList.Group>
735
645
736
-
<SettingsList.Divider />
737
-
738
646
<SettingsList.Group contentContainerStyle={[a.gap_sm]}>
739
647
<SettingsList.ItemIcon icon={EarthIcon} />
740
648
<SettingsList.ItemText>
741
-
<Trans>Post Translation Engine</Trans>
649
+
<Trans>Translation Engine</Trans>
742
650
</SettingsList.ItemText>
651
+
652
+
<Admonition type="info" style={[a.flex_1]}>
653
+
<Trans>Choose the engine to use when translating posts.</Trans>
654
+
</Admonition>
743
655
744
656
<Toggle.Item
745
657
name="service_google"
···
764
676
</Toggle.LabelText>
765
677
<Toggle.Radio />
766
678
</Toggle.Item>
767
-
768
-
<Toggle.Item
769
-
name="service_papago"
770
-
label={_(msg`Use Naver Papago`)}
771
-
value={translationServicePreference === 'papago'}
772
-
onChange={() => setTranslationServicePreference('papago')}
773
-
style={[a.w_full]}>
774
-
<Toggle.LabelText style={[a.flex_1]}>
775
-
<Trans>Use Naver Papago</Trans>
776
-
</Toggle.LabelText>
777
-
<Toggle.Radio />
778
-
</Toggle.Item>
779
-
780
-
<Toggle.Item
781
-
name="service_libreTranslate"
782
-
label={_(msg`Use LibreTranslate`)}
783
-
value={translationServicePreference === 'libreTranslate'}
784
-
onChange={() => setTranslationServicePreference('libreTranslate')}
785
-
style={[a.w_full]}>
786
-
<Toggle.LabelText style={[a.flex_1]}>
787
-
<Trans>Use LibreTranslate</Trans>
788
-
</Toggle.LabelText>
789
-
<Toggle.Radio />
790
-
</Toggle.Item>
791
679
</SettingsList.Group>
792
680
793
-
{translationServicePreference === 'libreTranslate' && (
794
-
<SettingsList.Item>
795
-
<SettingsList.ItemIcon icon={EarthIcon} />
796
-
<SettingsList.ItemText>
797
-
<Trans>{`LibreTranslate Instance`}</Trans>
798
-
</SettingsList.ItemText>
799
-
<SettingsList.BadgeButton
800
-
label={_(msg`Change`)}
801
-
onPress={() => setLibreTranslateInstanceControl.open()}
802
-
/>
803
-
</SettingsList.Item>
804
-
)}
805
-
806
-
<SettingsList.Divider />
807
-
808
681
<SettingsList.Group contentContainerStyle={[a.gap_sm]}>
809
682
<SettingsList.ItemIcon icon={VisibilityIcon} />
810
683
<SettingsList.ItemText>
···
825
698
826
699
<Toggle.Item
827
700
name="disable_reposts_metrics"
828
-
label={_(msg`Disable reskeet metrics`)}
701
+
label={_(msg`Disable reskeets metrics`)}
829
702
value={disableRepostsMetrics}
830
703
onChange={value => setDisableRepostsMetrics(value)}
831
704
style={[a.w_full]}>
832
705
<Toggle.LabelText style={[a.flex_1]}>
833
-
<Trans>Disable reskeet metrics</Trans>
706
+
<Trans>Disable reskeets metrics</Trans>
834
707
</Toggle.LabelText>
835
708
<Toggle.Platform />
836
709
</Toggle.Item>
···
920
793
</Toggle.Item>
921
794
</SettingsList.Group>
922
795
923
-
<SettingsList.Divider />
796
+
<SettingsList.Item>
797
+
<Admonition type="warning" style={[a.flex_1]}>
798
+
<Trans>
799
+
These settings might summon nasal demons! Restart the app after
800
+
changing if anything breaks.
801
+
</Trans>
802
+
</Admonition>
803
+
</SettingsList.Item>
924
804
925
805
<SettingsList.Group contentContainerStyle={[a.gap_sm]}>
926
806
<SettingsList.ItemIcon icon={RaisingHandIcon} />
···
967
847
</Layout.Content>
968
848
<ConstellationInstanceDialog control={setConstellationInstanceControl} />
969
849
<TrustedVerifiersDialog control={setTrustedVerifiersDialogControl} />
970
-
<LibreTranslateInstanceDialog
971
-
control={setLibreTranslateInstanceControl}
972
-
/>
973
850
</Layout.Screen>
974
851
)
975
852
}
+1
-8
src/state/persisted/schema.ts
+1
-8
src/state/persisted/schema.ts
···
172
172
173
173
showExternalShareButtons: z.boolean().optional(),
174
174
175
-
translationServicePreference: z.enum([
176
-
'google',
177
-
'kagi',
178
-
'papago',
179
-
'libreTranslate',
180
-
]),
181
-
libreTranslateInstance: z.string().optional(),
175
+
translationServicePreference: z.enum(['google', 'kagi']),
182
176
183
177
/** @deprecated */
184
178
mutedThreads: z.array(z.string()),
···
290
284
hideUnreplyablePosts: false,
291
285
showExternalShareButtons: false,
292
286
translationServicePreference: 'google',
293
-
libreTranslateInstance: 'https://libretranslate.com/',
294
287
}
295
288
296
289
export function tryParse(rawData: string): Schema | undefined {
+1
-43
src/state/preferences/translation-service-preference.tsx
+1
-43
src/state/preferences/translation-service-preference.tsx
···
4
4
5
5
type StateContext = persisted.Schema['translationServicePreference']
6
6
type SetContext = (v: persisted.Schema['translationServicePreference']) => void
7
-
type InstanceStateContext = persisted.Schema['libreTranslateInstance']
8
-
type SetInstanceContext = (
9
-
v: persisted.Schema['libreTranslateInstance'],
10
-
) => void
11
7
12
8
const stateContext = React.createContext<StateContext>(
13
9
persisted.defaults.translationServicePreference,
···
15
11
const setContext = React.createContext<SetContext>(
16
12
(_: persisted.Schema['translationServicePreference']) => {},
17
13
)
18
-
const instanceStateContext = React.createContext<InstanceStateContext>(
19
-
persisted.defaults.libreTranslateInstance,
20
-
)
21
-
const setInstanceContext = React.createContext<SetInstanceContext>(
22
-
(_: persisted.Schema['libreTranslateInstance']) => {},
23
-
)
24
14
25
15
export function Provider({children}: React.PropsWithChildren<{}>) {
26
16
const [state, setState] = React.useState(
27
17
persisted.get('translationServicePreference'),
28
18
)
29
-
const [instanceState, setInstanceState] = React.useState(
30
-
persisted.get('libreTranslateInstance'),
31
-
)
32
19
33
20
const setStateWrapped = React.useCallback(
34
21
(
···
43
30
[setState],
44
31
)
45
32
46
-
const setInstanceStateWrapped = React.useCallback(
47
-
(libreTranslateInstance: persisted.Schema['libreTranslateInstance']) => {
48
-
setInstanceState(libreTranslateInstance)
49
-
persisted.write('libreTranslateInstance', libreTranslateInstance)
50
-
},
51
-
[setInstanceState],
52
-
)
53
-
54
33
React.useEffect(() => {
55
34
return persisted.onUpdate(
56
35
'translationServicePreference',
···
60
39
)
61
40
}, [setStateWrapped])
62
41
63
-
React.useEffect(() => {
64
-
return persisted.onUpdate('libreTranslateInstance', nextInstance => {
65
-
setInstanceState(nextInstance)
66
-
})
67
-
}, [setInstanceStateWrapped])
68
-
69
42
return (
70
43
<stateContext.Provider value={state}>
71
44
<setContext.Provider value={setStateWrapped}>
72
-
<instanceStateContext.Provider value={instanceState}>
73
-
<setInstanceContext.Provider value={setInstanceStateWrapped}>
74
-
{children}
75
-
</setInstanceContext.Provider>
76
-
</instanceStateContext.Provider>
45
+
{children}
77
46
</setContext.Provider>
78
47
</stateContext.Provider>
79
48
)
···
86
55
export function useSetTranslationServicePreference() {
87
56
return React.useContext(setContext)
88
57
}
89
-
90
-
export function useLibreTranslateInstance() {
91
-
return (
92
-
React.useContext(instanceStateContext) ??
93
-
persisted.defaults.libreTranslateInstance!
94
-
)
95
-
}
96
-
97
-
export function useSetLibreTranslateInstance() {
98
-
return React.useContext(setInstanceContext)
99
-
}
History
2 rounds
0 comments
ewancroft.uk
submitted
#1
expand 0 comments
closed without merging
ewancroft.uk
submitted
#0
1 commit
expand
collapse
feat: add Linkat links tab to profile
Adds support for displaying Linkat board links on user profiles.
The Links tab appears when a user has configured a blue.linkat.board
record with link cards.