Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at f8975036440051185486f6b2c00a201ef2e18a8c 1383 lines 49 kB view raw
1import {useState} from 'react' 2import {View} from 'react-native' 3import {type ProfileViewBasic} from '@atproto/api/dist/client/types/app/bsky/actor/defs' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6import {type NativeStackScreenProps} from '@react-navigation/native-stack' 7 8import { DEFAULT_ALT_TEXT_AI_MODEL } from '#/lib/constants' 9import {usePalette} from '#/lib/hooks/usePalette' 10import {type CommonNavigatorParams} from '#/lib/routes/types' 11import {dynamicActivate} from '#/locale/i18n' 12import {dynamicActivate as dynamicActivateWeb} from '#/locale/i18n.web' 13import {type AppLanguage} from '#/locale/languages' 14import * as persisted from '#/state/persisted' 15import {useGoLinksEnabled, useSetGoLinksEnabled} from '#/state/preferences' 16import { 17 useConstellationInstance, 18 useSetConstellationInstance, 19} from '#/state/preferences/constellation-instance' 20import { 21 useDeerVerificationEnabled, 22 useDeerVerificationTrusted, 23 useSetDeerVerificationEnabled, 24} from '#/state/preferences/deer-verification' 25import { 26 useDirectFetchRecords, 27 useSetDirectFetchRecords, 28} from '#/state/preferences/direct-fetch-records' 29import { 30 useDisableComposerPrompt, 31 useSetDisableComposerPrompt, 32} from '#/state/preferences/disable-composer-prompt' 33import { 34 useDisableFollowedByMetrics, 35 useSetDisableFollowedByMetrics, 36} from '#/state/preferences/disable-followed-by-metrics' 37import { 38 useDisableFollowersMetrics, 39 useSetDisableFollowersMetrics, 40} from '#/state/preferences/disable-followers-metrics' 41import { 42 useDisableFollowingMetrics, 43 useSetDisableFollowingMetrics, 44} from '#/state/preferences/disable-following-metrics' 45import { 46 useDisableLikesMetrics, 47 useSetDisableLikesMetrics, 48} from '#/state/preferences/disable-likes-metrics' 49import { 50 useDisablePostsMetrics, 51 useSetDisablePostsMetrics, 52} from '#/state/preferences/disable-posts-metrics' 53import { 54 useDisableQuotesMetrics, 55 useSetDisableQuotesMetrics, 56} from '#/state/preferences/disable-quotes-metrics' 57import { 58 useDisableReplyMetrics, 59 useSetDisableReplyMetrics, 60} from '#/state/preferences/disable-reply-metrics' 61import { 62 useDisableRepostsMetrics, 63 useSetDisableRepostsMetrics, 64} from '#/state/preferences/disable-reposts-metrics' 65import { 66 useDisableSavesMetrics, 67 useSetDisableSavesMetrics, 68} from '#/state/preferences/disable-saves-metrics' 69import { 70 useDisableVerifyEmailReminder, 71 useSetDisableVerifyEmailReminder, 72} from '#/state/preferences/disable-verify-email-reminder' 73import { 74 useDisableViaRepostNotification, 75 useSetDisableViaRepostNotification, 76} from '#/state/preferences/disable-via-repost-notification' 77import { 78 useDiscoverContextEnabled, 79 useSetDiscoverContextEnabled, 80} from '#/state/preferences/discover-context-enabled' 81import { 82 useSetShowExternalShareButtons, 83 useShowExternalShareButtons, 84} from '#/state/preferences/external-share-buttons' 85import { 86 useHideFeedsPromoTab, 87 useSetHideFeedsPromoTab, 88} from '#/state/preferences/hide-feeds-promo-tab' 89import { 90 useHideSimilarAccountsRecomm, 91 useSetHideSimilarAccountsRecomm, 92} from '#/state/preferences/hide-similar-accounts-recommendations' 93import { 94 useHideUnreplyablePosts, 95 useSetHideUnreplyablePosts, 96} from '#/state/preferences/hide-unreplyable-posts' 97import { 98 useHighQualityImages, 99 useSetHighQualityImages, 100} from '#/state/preferences/high-quality-images' 101import {useModerationOpts} from '#/state/preferences/moderation-opts' 102import { 103 useNoAppLabelers, 104 useSetNoAppLabelers, 105} from '#/state/preferences/no-app-labelers' 106import { 107 useNoDiscoverFallback, 108 useSetNoDiscoverFallback, 109} from '#/state/preferences/no-discover-fallback' 110import { 111 useOpenRouterApiKey, 112 useOpenRouterConfigured, 113 useOpenRouterModel, 114 useSetOpenRouterApiKey, 115 useSetOpenRouterModel, 116} from '#/state/preferences/openrouter' 117import { 118 usePostReplacement, 119 useSetPostReplacement, 120} from '#/state/preferences/post-name-replacement' 121import { 122 useRepostCarouselEnabled, 123 useSetRepostCarouselEnabled, 124} from '#/state/preferences/repost-carousel-enabled' 125import { 126 useSetShowLinkInHandle, 127 useShowLinkInHandle, 128} from '#/state/preferences/show-link-in-handle.tsx' 129import { 130 useLibreTranslateInstance, 131 useSetLibreTranslateInstance, 132 useSetTranslationServicePreference, 133 useTranslationServicePreference, 134} from '#/state/preferences/translation-service-preference' 135import { 136 useHandleInLinks, 137 useSetHandleInLinks, 138} from '#/state/preferences/use-handle-in-links' 139import {useProfilesQuery} from '#/state/queries/profile' 140import * as SettingsList from '#/screens/Settings/components/SettingsList' 141import {atoms as a, useBreakpoints} from '#/alf' 142import {Admonition} from '#/components/Admonition' 143import {Button, ButtonText} from '#/components/Button' 144import * as Dialog from '#/components/Dialog' 145import * as Toggle from '#/components/forms/Toggle' 146import {Atom_Stroke2_Corner0_Rounded as AtomIcon} from '#/components/icons/Atom' 147import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 148import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 149import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe' 150import {Lab_Stroke2_Corner0_Rounded as _BeakerIcon} from '#/components/icons/Lab' 151import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' 152import {Pencil_Stroke2_Corner0_Rounded as PencilIcon} from '#/components/icons/Pencil' 153import {RaisingHand4Finger_Stroke2_Corner0_Rounded as RaisingHandIcon} from '#/components/icons/RaisingHand' 154import {Star_Stroke2_Corner0_Rounded as StarIcon} from '#/components/icons/Star' 155import {Verified_Stroke2_Corner2_Rounded as VerifiedIcon} from '#/components/icons/Verified' 156import * as Layout from '#/components/Layout' 157import {InlineLinkText} from '#/components/Link' 158import {Text} from '#/components/Typography' 159import {IS_WEB} from '#/env' 160import {SearchProfileCard} from '../Search/components/SearchProfileCard' 161 162type Props = NativeStackScreenProps<CommonNavigatorParams> 163 164function ConstellationInstanceDialog({ 165 control, 166}: { 167 control: Dialog.DialogControlProps 168}) { 169 const pal = usePalette('default') 170 const {_} = useLingui() 171 172 const constellationInstance = useConstellationInstance() 173 const [url, setUrl] = useState(constellationInstance ?? '') 174 const setConstellationInstance = useSetConstellationInstance() 175 176 const submit = () => { 177 setConstellationInstance(url) 178 control.close() 179 } 180 181 const shouldDisable = () => { 182 try { 183 return !new URL(url).hostname.includes('.') 184 } catch (e) { 185 return true 186 } 187 } 188 189 return ( 190 <Dialog.Outer 191 control={control} 192 nativeOptions={{preventExpansion: true}} 193 onClose={() => setUrl(constellationInstance ?? '')}> 194 <Dialog.Handle /> 195 <Dialog.ScrollableInner label={_(msg`Constellations instance URL`)}> 196 <View style={[a.gap_sm, a.pb_lg]}> 197 <Text style={[a.text_2xl, a.font_bold]}> 198 <Trans>Constellations instance URL</Trans> 199 </Text> 200 </View> 201 202 <View style={a.gap_lg}> 203 <Dialog.Input 204 label="Text input field" 205 autoFocus 206 style={[styles.textInput, pal.border, pal.text]} 207 onChangeText={value => { 208 setUrl(value) 209 }} 210 placeholder={persisted.defaults.constellationInstance} 211 placeholderTextColor={pal.colors.textLight} 212 onSubmitEditing={submit} 213 accessibilityHint={_( 214 msg`Input the url of the constellations instance to use`, 215 )} 216 defaultValue={constellationInstance} 217 /> 218 219 <View style={IS_WEB && [a.flex_row, a.justify_end]}> 220 <Button 221 label={_(msg`Save`)} 222 size="large" 223 onPress={submit} 224 variant="solid" 225 color="primary" 226 disabled={shouldDisable()}> 227 <ButtonText> 228 <Trans>Save</Trans> 229 </ButtonText> 230 </Button> 231 </View> 232 </View> 233 234 <Dialog.Close /> 235 </Dialog.ScrollableInner> 236 </Dialog.Outer> 237 ) 238} 239 240function LibreTranslateInstanceDialog({ 241 control, 242}: { 243 control: Dialog.DialogControlProps 244}) { 245 const pal = usePalette('default') 246 const {_} = useLingui() 247 248 const libreTranslateInstance = useLibreTranslateInstance() 249 const [url, setUrl] = useState(libreTranslateInstance ?? '') 250 const setLibreTranslateInstance = useSetLibreTranslateInstance() 251 252 const submit = () => { 253 setLibreTranslateInstance(url) 254 control.close() 255 } 256 257 const shouldDisable = () => { 258 try { 259 return !new URL(url).hostname.includes('.') 260 } catch (e) { 261 return true 262 } 263 } 264 265 return ( 266 <Dialog.Outer 267 control={control} 268 nativeOptions={{preventExpansion: true}} 269 onClose={() => setUrl(libreTranslateInstance ?? '')}> 270 <Dialog.Handle /> 271 <Dialog.ScrollableInner label={_(msg`LibreTranslate instance URL`)}> 272 <View style={[a.gap_sm, a.pb_lg]}> 273 <Text style={[a.text_2xl, a.font_bold]}> 274 <Trans>LibreTranslate instance URL</Trans> 275 </Text> 276 </View> 277 278 <View style={a.gap_lg}> 279 <Dialog.Input 280 label="Text input field" 281 autoFocus 282 style={[styles.textInput, pal.border, pal.text]} 283 onChangeText={value => { 284 setUrl(value) 285 }} 286 placeholder={persisted.defaults.libreTranslateInstance} 287 placeholderTextColor={pal.colors.textLight} 288 onSubmitEditing={submit} 289 accessibilityHint={_( 290 msg`Input the url of the LibreTranslate instance to use`, 291 )} 292 defaultValue={libreTranslateInstance} 293 /> 294 295 <View style={IS_WEB && [a.flex_row, a.justify_end]}> 296 <Button 297 label={_(msg`Save`)} 298 size="large" 299 onPress={submit} 300 variant="solid" 301 color="primary" 302 disabled={shouldDisable()}> 303 <ButtonText> 304 <Trans>Save</Trans> 305 </ButtonText> 306 </Button> 307 </View> 308 </View> 309 310 <Dialog.Close /> 311 </Dialog.ScrollableInner> 312 </Dialog.Outer> 313 ) 314} 315 316function PostReplacementDialog({ 317 control, 318}: { 319 control: Dialog.DialogControlProps 320}) { 321 const pal = usePalette('default') 322 const {_, i18n} = useLingui() 323 324 const postReplacement = usePostReplacement() 325 const setPostReplacement = useSetPostReplacement() 326 327 const [singular, setSingular] = useState(postReplacement.postName) 328 const [plural, setPlural] = useState(postReplacement.postsName) 329 const [pluralManuallyEdited, setPluralManuallyEdited] = useState(false) 330 331 const submit = async () => { 332 setPostReplacement({ 333 enabled: singular.trim().toLowerCase() !== 'post', 334 postName: singular, 335 postsName: plural, 336 }) 337 338 // Force reload the i18n messages to apply the replacement immediately 339 const locale = i18n.locale 340 await (IS_WEB 341 ? dynamicActivateWeb(locale as AppLanguage) 342 : dynamicActivate(locale as AppLanguage)) 343 344 control.close() 345 } 346 347 const handleSingularChange = (value: string) => { 348 setSingular(value) 349 if (!pluralManuallyEdited) { 350 setPlural(value + 's') 351 } 352 } 353 354 const handlePluralChange = (value: string) => { 355 setPlural(value) 356 setPluralManuallyEdited(true) 357 } 358 359 const handlePresetSelect = (singularForm: string, pluralForm: string) => { 360 setSingular(singularForm) 361 setPlural(pluralForm) 362 setPluralManuallyEdited(false) 363 } 364 365 const shouldDisable = () => { 366 return !singular.trim() || !plural.trim() 367 } 368 369 return ( 370 <Dialog.Outer 371 control={control} 372 nativeOptions={{preventExpansion: true}} 373 onClose={() => { 374 setSingular(postReplacement.postName) 375 setPlural(postReplacement.postsName) 376 setPluralManuallyEdited(false) 377 }}> 378 <Dialog.Handle /> 379 <Dialog.ScrollableInner label={_(msg`Custom post phrase`)}> 380 <View style={[a.gap_sm, a.pb_lg]}> 381 <Text style={[a.text_2xl, a.font_bold]}> 382 <Trans>Custom post phrase</Trans> 383 </Text> 384 </View> 385 386 <View style={a.gap_lg}> 387 <Dialog.Input 388 label="Singular form" 389 autoFocus 390 style={[styles.textInput, pal.border, pal.text]} 391 onChangeText={handleSingularChange} 392 placeholder="skeet" 393 placeholderTextColor={pal.colors.textLight} 394 accessibilityHint={_(msg`Input the singular form (e.g., "skeet")`)} 395 value={singular} 396 /> 397 398 <View style={[a.flex_row, a.flex_wrap, a.mb_xs]}> 399 {[ 400 {singular: 'post', plural: 'posts'}, 401 {singular: 'skeet', plural: 'skeets'}, 402 {singular: 'note', plural: 'notes'}, 403 {singular: 'woot', plural: 'woots'}, 404 {singular: 'toot', plural: 'toots'}, 405 {singular: 'silly', plural: 'sillies'}, 406 ].map(preset => ( 407 <Button 408 key={preset.singular} 409 variant="ghost" 410 color="primary" 411 label={preset.singular} 412 style={[a.px_sm, a.py_xs, a.rounded_sm, a.gap_sm]} 413 onPress={() => 414 handlePresetSelect(preset.singular, preset.plural) 415 }> 416 <ButtonText>{preset.singular}</ButtonText> 417 </Button> 418 ))} 419 </View> 420 421 <Dialog.Input 422 label="Plural form" 423 style={[styles.textInput, pal.border, pal.text]} 424 onChangeText={handlePluralChange} 425 placeholder="skeets" 426 placeholderTextColor={pal.colors.textLight} 427 accessibilityHint={_(msg`Input the plural form (e.g., "skeets")`)} 428 value={plural} 429 /> 430 431 <View style={IS_WEB && [a.flex_row, a.justify_end]}> 432 <Button 433 label={_(msg`Save`)} 434 size="large" 435 onPress={submit} 436 variant="solid" 437 color="primary" 438 disabled={shouldDisable()}> 439 <ButtonText> 440 <Trans>Save</Trans> 441 </ButtonText> 442 </Button> 443 </View> 444 </View> 445 446 <Dialog.Close /> 447 </Dialog.ScrollableInner> 448 </Dialog.Outer> 449 ) 450} 451 452function TrustedVerifiersDialog({ 453 control, 454}: { 455 control: Dialog.DialogControlProps 456}) { 457 const {_} = useLingui() 458 459 return ( 460 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 461 <Dialog.Handle /> 462 <Dialog.ScrollableInner label={_(msg`Trusted Verifiers`)}> 463 <View style={[a.gap_sm, a.pb_lg]}> 464 <Text style={[a.text_2xl, a.font_bold]}> 465 <Trans>Trusted Verifiers</Trans> 466 </Text> 467 </View> 468 469 <TrustedVerifiers /> 470 471 <Dialog.Close /> 472 </Dialog.ScrollableInner> 473 </Dialog.Outer> 474 ) 475} 476 477const TrustedVerifiers = (): React.ReactNode => { 478 const trusted = useDeerVerificationTrusted() 479 const moderationOpts = useModerationOpts() 480 481 const results = useProfilesQuery({ 482 handles: Array.from(trusted), 483 }) 484 485 const {gtMobile} = useBreakpoints() 486 487 return ( 488 results.data && 489 moderationOpts !== undefined && ( 490 <View style={[gtMobile ? a.pl_md : a.pl_sm, a.pb_sm]}> 491 {results.data.profiles.map(profile => ( 492 <SearchProfileCard 493 key={profile.did} 494 profile={profile as ProfileViewBasic} 495 moderationOpts={moderationOpts} 496 /> 497 ))} 498 </View> 499 ) 500 ) 501} 502 503function OpenRouterApiKeyDialog({ 504 control, 505}: { 506 control: Dialog.DialogControlProps 507}) { 508 const pal = usePalette('default') 509 const {_} = useLingui() 510 511 const apiKey = useOpenRouterApiKey() 512 const [value, setValue] = useState(apiKey ?? '') 513 const setApiKey = useSetOpenRouterApiKey() 514 515 const submit = () => { 516 setApiKey(value.trim() || undefined) 517 control.close() 518 } 519 520 return ( 521 <Dialog.Outer 522 control={control} 523 nativeOptions={{preventExpansion: true}} 524 onClose={() => setValue(apiKey ?? '')}> 525 <Dialog.Handle /> 526 <Dialog.ScrollableInner label={_(msg`OpenRouter API Key`)}> 527 <View style={[a.gap_sm, a.pb_lg]}> 528 <Text style={[a.text_2xl, a.font_bold]}> 529 <Trans>OpenRouter API Key</Trans> 530 </Text> 531 </View> 532 533 <View style={a.gap_lg}> 534 <Dialog.Input 535 label="API Key" 536 autoFocus 537 style={[styles.textInput, pal.border, pal.text]} 538 onChangeText={setValue} 539 placeholder="sk-or-..." 540 placeholderTextColor={pal.colors.textLight} 541 onSubmitEditing={submit} 542 accessibilityHint={_( 543 msg`Enter your OpenRouter API key for AI alt text generation`, 544 )} 545 defaultValue={apiKey ?? ''} 546 secureTextEntry 547 /> 548 549 <View style={IS_WEB && [a.flex_row, a.justify_end]}> 550 <Button 551 label={_(msg`Save`)} 552 size="large" 553 onPress={submit} 554 variant="solid" 555 color="primary"> 556 <ButtonText> 557 <Trans>Save</Trans> 558 </ButtonText> 559 </Button> 560 </View> 561 </View> 562 563 <Dialog.Close /> 564 </Dialog.ScrollableInner> 565 </Dialog.Outer> 566 ) 567} 568 569function OpenRouterModelDialog({ 570 control, 571}: { 572 control: Dialog.DialogControlProps 573}) { 574 const pal = usePalette('default') 575 const {_} = useLingui() 576 577 const model = useOpenRouterModel() 578 const [value, setValue] = useState(model ?? '') 579 const setModel = useSetOpenRouterModel() 580 581 const submit = () => { 582 setModel(value.trim() || undefined) 583 control.close() 584 } 585 586 return ( 587 <Dialog.Outer 588 control={control} 589 nativeOptions={{preventExpansion: true}} 590 onClose={() => setValue(model ?? '')}> 591 <Dialog.Handle /> 592 <Dialog.ScrollableInner label={_(msg`OpenRouter Model`)}> 593 <View style={[a.gap_sm, a.pb_lg]}> 594 <Text style={[a.text_2xl, a.font_bold]}> 595 <Trans>OpenRouter Model</Trans> 596 </Text> 597 </View> 598 599 <View style={a.gap_lg}> 600 <Dialog.Input 601 label="Model" 602 autoFocus 603 style={[styles.textInput, pal.border, pal.text]} 604 onChangeText={setValue} 605 placeholder={DEFAULT_ALT_TEXT_AI_MODEL} 606 placeholderTextColor={pal.colors.textLight} 607 onSubmitEditing={submit} 608 accessibilityHint={_( 609 msg`Enter the model ID to use for alt text generation`, 610 )} 611 defaultValue={model ?? ''} 612 /> 613 614 <View style={IS_WEB && [a.flex_row, a.justify_end]}> 615 <Button 616 label={_(msg`Save`)} 617 size="large" 618 onPress={submit} 619 variant="solid" 620 color="primary"> 621 <ButtonText> 622 <Trans>Save</Trans> 623 </ButtonText> 624 </Button> 625 </View> 626 </View> 627 628 <Dialog.Close /> 629 </Dialog.ScrollableInner> 630 </Dialog.Outer> 631 ) 632} 633 634export function RunesSettingsScreen({}: Props) { 635 const {_} = useLingui() 636 637 const goLinksEnabled = useGoLinksEnabled() 638 const setGoLinksEnabled = useSetGoLinksEnabled() 639 640 const directFetchRecords = useDirectFetchRecords() 641 const setDirectFetchRecords = useSetDirectFetchRecords() 642 643 const showExternalShareButtons = useShowExternalShareButtons() 644 const setShowExternalShareButtons = useSetShowExternalShareButtons() 645 646 const noAppLabelers = useNoAppLabelers() 647 const setNoAppLabelers = useSetNoAppLabelers() 648 649 const noDiscoverFallback = useNoDiscoverFallback() 650 const setNoDiscoverFallback = useSetNoDiscoverFallback() 651 652 const highQualityImages = useHighQualityImages() 653 const setHighQualityImages = useSetHighQualityImages() 654 655 const hideFeedsPromoTab = useHideFeedsPromoTab() 656 const setHideFeedsPromoTab = useSetHideFeedsPromoTab() 657 658 const disableViaRepostNotification = useDisableViaRepostNotification() 659 const setDisableViaRepostNotification = useSetDisableViaRepostNotification() 660 661 const disableComposerPrompt = useDisableComposerPrompt() 662 const setDisableComposerPrompt = useSetDisableComposerPrompt() 663 664 const discoverContextEnabled = useDiscoverContextEnabled() 665 const setDiscoverContextEnabled = useSetDiscoverContextEnabled() 666 667 const disableLikesMetrics = useDisableLikesMetrics() 668 const setDisableLikesMetrics = useSetDisableLikesMetrics() 669 670 const disableRepostsMetrics = useDisableRepostsMetrics() 671 const setDisableRepostsMetrics = useSetDisableRepostsMetrics() 672 673 const disableQuotesMetrics = useDisableQuotesMetrics() 674 const setDisableQuotesMetrics = useSetDisableQuotesMetrics() 675 676 const disableSavesMetrics = useDisableSavesMetrics() 677 const setDisableSavesMetrics = useSetDisableSavesMetrics() 678 679 const disableReplyMetrics = useDisableReplyMetrics() 680 const setDisableReplyMetrics = useSetDisableReplyMetrics() 681 682 const disableFollowersMetrics = useDisableFollowersMetrics() 683 const setDisableFollowersMetrics = useSetDisableFollowersMetrics() 684 685 const disableFollowingMetrics = useDisableFollowingMetrics() 686 const setDisableFollowingMetrics = useSetDisableFollowingMetrics() 687 688 const disableFollowedByMetrics = useDisableFollowedByMetrics() 689 const setDisableFollowedByMetrics = useSetDisableFollowedByMetrics() 690 691 const disablePostsMetrics = useDisablePostsMetrics() 692 const setDisablePostsMetrics = useSetDisablePostsMetrics() 693 694 const hideSimilarAccountsRecomm = useHideSimilarAccountsRecomm() 695 const setHideSimilarAccountsRecomm = useSetHideSimilarAccountsRecomm() 696 697 const hideUnreplyablePosts = useHideUnreplyablePosts() 698 const setHideUnreplyablePosts = useSetHideUnreplyablePosts() 699 700 const disableVerifyEmailReminder = useDisableVerifyEmailReminder() 701 const setDisableVerifyEmailReminder = useSetDisableVerifyEmailReminder() 702 703 const constellationInstance = useConstellationInstance() 704 const setConstellationInstanceControl = Dialog.useDialogControl() 705 706 const setTrustedVerifiersDialogControl = Dialog.useDialogControl() 707 708 const deerVerificationEnabled = useDeerVerificationEnabled() 709 const setDeerVerificationEnabled = useSetDeerVerificationEnabled() 710 711 const repostCarouselEnabled = useRepostCarouselEnabled() 712 const setRepostCarouselEnabled = useSetRepostCarouselEnabled() 713 714 const showLinkInHandle = useShowLinkInHandle() 715 const setShowLinkInHandle = useSetShowLinkInHandle() 716 717 const handleInLinks = useHandleInLinks() 718 const setHandleInLinks = useSetHandleInLinks() 719 720 const translationServicePreference = useTranslationServicePreference() 721 const setTranslationServicePreference = useSetTranslationServicePreference() 722 723 const setLibreTranslateInstanceControl = Dialog.useDialogControl() 724 725 const setPostReplacementDialogControl = Dialog.useDialogControl() 726 727 const setOpenRouterApiKeyControl = Dialog.useDialogControl() 728 const openRouterModel = useOpenRouterModel() 729 const setOpenRouterModelControl = Dialog.useDialogControl() 730 const openRouterConfigured = useOpenRouterConfigured() 731 732 return ( 733 <Layout.Screen> 734 <Layout.Header.Outer> 735 <Layout.Header.BackButton /> 736 <Layout.Header.Content> 737 <Layout.Header.TitleText> 738 <Trans>Runes</Trans> 739 </Layout.Header.TitleText> 740 </Layout.Header.Content> 741 <Layout.Header.Slot /> 742 </Layout.Header.Outer> 743 <Layout.Content> 744 <SettingsList.Container> 745 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 746 <SettingsList.ItemIcon icon={AtomIcon} /> 747 <SettingsList.ItemText> 748 <Trans>Redirects</Trans> 749 </SettingsList.ItemText> 750 <Toggle.Item 751 name="use_go_links" 752 label={_(msg`Redirect through go.bsky.app`)} 753 value={goLinksEnabled ?? false} 754 onChange={value => setGoLinksEnabled(value)} 755 style={[a.w_full]}> 756 <Toggle.LabelText style={[a.flex_1]}> 757 <Trans>Redirect through go.bsky.app</Trans> 758 </Toggle.LabelText> 759 <Toggle.Platform /> 760 </Toggle.Item> 761 <Toggle.Item 762 name="use_handle_in_links" 763 label={_(msg`Use handles in profile links instead of DIDs (requires restart)`)} 764 value={handleInLinks ?? false} 765 onChange={value => setHandleInLinks(value)} 766 style={[a.w_full]}> 767 <Toggle.LabelText style={[a.flex_1]}> 768 <Trans>Use handles in profile links instead of DIDs</Trans> 769 </Toggle.LabelText> 770 <Toggle.Platform /> 771 </Toggle.Item> 772 </SettingsList.Group> 773 774 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 775 <SettingsList.ItemIcon icon={VisibilityIcon} /> 776 <SettingsList.ItemText> 777 <Trans>Visibility</Trans> 778 </SettingsList.ItemText> 779 <Toggle.Item 780 name="direct_fetch_records" 781 label={_( 782 msg`Fetch records directly from PDS to see through quote blocks`, 783 )} 784 value={directFetchRecords} 785 onChange={value => setDirectFetchRecords(value)} 786 style={[a.w_full]}> 787 <Toggle.LabelText style={[a.flex_1]}> 788 <Trans> 789 Fetch records directly from PDS to see contents of blocked and 790 detached quotes 791 </Trans> 792 </Toggle.LabelText> 793 <Toggle.Platform /> 794 </Toggle.Item> 795 </SettingsList.Group> 796 797 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 798 <SettingsList.ItemIcon icon={ChainLinkIcon} /> 799 <SettingsList.ItemText> 800 <Trans>Bridging and Fediverse</Trans> 801 </SettingsList.ItemText> 802 <Toggle.Item 803 name="external_share_buttons" 804 label={_( 805 msg`Show "Open original post" and "Open post in PDSls" buttons`, 806 )} 807 value={showExternalShareButtons} 808 onChange={value => setShowExternalShareButtons(value)} 809 style={[a.w_full]}> 810 <Toggle.LabelText style={[a.flex_1]}> 811 <Trans> 812 Show "Open original post" and "Open post in PDSls" buttons 813 </Trans> 814 </Toggle.LabelText> 815 <Toggle.Platform /> 816 </Toggle.Item> 817 </SettingsList.Group> 818 819 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 820 <SettingsList.ItemIcon icon={VerifiedIcon} /> 821 <SettingsList.ItemText> 822 <Trans>Verification</Trans> 823 </SettingsList.ItemText> 824 <Toggle.Item 825 name="custom_verifications" 826 label={_( 827 msg`Select your own set of trusted verifiers, and operate as a verifier`, 828 )} 829 value={deerVerificationEnabled} 830 onChange={value => setDeerVerificationEnabled(value)} 831 style={[a.w_full]}> 832 <Toggle.LabelText style={[a.flex_1]}> 833 <Trans> 834 Select your own set of trusted verifiers, and operate as a 835 verifier 836 </Trans> 837 </Toggle.LabelText> 838 <Toggle.Platform /> 839 </Toggle.Item> 840 </SettingsList.Group> 841 842 <SettingsList.Item> 843 <Admonition type="warning" style={[a.flex_1]}> 844 <Trans> 845 May slow down the client or fail to find all labels. Revoke and 846 grant trust in the meatball menu on a profile.{' '} 847 {deerVerificationEnabled 848 ? 'You currently' 849 : 'If enabled, you would'}{' '} 850 trust the following verifiers: 851 </Trans> 852 </Admonition> 853 </SettingsList.Item> 854 855 <SettingsList.Item> 856 <SettingsList.ItemIcon icon={VerifiedIcon} /> 857 <SettingsList.ItemText> 858 <Trans>{`Trusted Verifiers`}</Trans> 859 </SettingsList.ItemText> 860 <SettingsList.BadgeButton 861 label={_(msg`View`)} 862 onPress={() => setTrustedVerifiersDialogControl.open()} 863 /> 864 </SettingsList.Item> 865 866 <SettingsList.Item> 867 <SettingsList.ItemIcon icon={StarIcon} /> 868 <SettingsList.ItemText> 869 <Trans>{`Constellation Instance`}</Trans> 870 </SettingsList.ItemText> 871 <SettingsList.BadgeButton 872 label={_(msg`Change`)} 873 onPress={() => setConstellationInstanceControl.open()} 874 /> 875 </SettingsList.Item> 876 <SettingsList.Item> 877 <Admonition type="info" style={[a.flex_1]}> 878 <Trans> 879 Constellation is used to supplement AppView responses for custom 880 verifications and nuclear block bypass, via backlinks. Current 881 instance:\u00A0 882 <InlineLinkText 883 to={constellationInstance} 884 label={constellationInstance}> 885 {constellationInstance} 886 </InlineLinkText> 887 </Trans> 888 </Admonition> 889 </SettingsList.Item> 890 891 <SettingsList.Divider /> 892 893 <SettingsList.Item> 894 <SettingsList.ItemIcon icon={PencilIcon} /> 895 <SettingsList.ItemText> 896 <Trans>{`Custom post phrase`}</Trans> 897 </SettingsList.ItemText> 898 <SettingsList.BadgeButton 899 label={_(msg`Change`)} 900 onPress={() => setPostReplacementDialogControl.open()} 901 /> 902 </SettingsList.Item> 903 904 <SettingsList.Divider /> 905 906 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 907 <SettingsList.ItemIcon icon={PaintRollerIcon} /> 908 <SettingsList.ItemText> 909 <Trans>Tweaks</Trans> 910 </SettingsList.ItemText> 911 <Toggle.Item 912 name="repost_carousel" 913 label={_(msg`Combine reposts into a horizontal carousel`)} 914 value={repostCarouselEnabled} 915 onChange={value => setRepostCarouselEnabled(value)} 916 style={[a.w_full]}> 917 <Toggle.LabelText style={[a.flex_1]}> 918 <Trans>Combine reposts into a horizontal carousel</Trans> 919 </Toggle.LabelText> 920 <Toggle.Platform /> 921 </Toggle.Item> 922 923 <Toggle.Item 924 name="show_link_in_handle" 925 label={_( 926 msg`On non-bsky.social handles, show a link to that URL`, 927 )} 928 value={showLinkInHandle} 929 onChange={value => setShowLinkInHandle(value)} 930 style={[a.w_full]}> 931 <Toggle.LabelText style={[a.flex_1]}> 932 <Trans> 933 On non-bsky.social handles, show a link to that URL 934 </Trans> 935 </Toggle.LabelText> 936 <Toggle.Platform /> 937 </Toggle.Item> 938 939 <Toggle.Item 940 name="no_discover_fallback" 941 label={_(msg`Do not fall back to discover feed`)} 942 value={noDiscoverFallback} 943 onChange={value => setNoDiscoverFallback(value)} 944 style={[a.w_full]}> 945 <Toggle.LabelText style={[a.flex_1]}> 946 <Trans>Do not fall back to discover feed</Trans> 947 </Toggle.LabelText> 948 <Toggle.Platform /> 949 </Toggle.Item> 950 951 <Toggle.Item 952 name="high_quality_images" 953 label={_(msg`Display images in higher quality`)} 954 value={highQualityImages} 955 onChange={value => setHighQualityImages(value)} 956 style={[a.w_full]}> 957 <Toggle.LabelText style={[a.flex_1]}> 958 <Trans>Display images in higher quality</Trans> 959 </Toggle.LabelText> 960 <Toggle.Platform /> 961 </Toggle.Item> 962 <Admonition type="info" style={[a.flex_1]}> 963 <Trans> 964 Images will be served as PNG instead of JPEG. Images will take 965 longer to load and use more bandwidth. 966 </Trans> 967 </Admonition> 968 969 <Toggle.Item 970 name="hide_feeds_promo_tab" 971 label={_(msg`Hide "Feeds ✨" tab when only one feed is selected`)} 972 value={hideFeedsPromoTab} 973 onChange={value => setHideFeedsPromoTab(value)} 974 style={[a.w_full]}> 975 <Toggle.LabelText style={[a.flex_1]}> 976 <Trans> 977 Hide "Feeds ✨" tab when only one feed is selected 978 </Trans> 979 </Toggle.LabelText> 980 <Toggle.Platform /> 981 </Toggle.Item> 982 983 <Toggle.Item 984 name="disable_via_repost_notification" 985 label={_(msg`Disable via repost notifications`)} 986 value={disableViaRepostNotification} 987 onChange={value => setDisableViaRepostNotification(value)} 988 style={[a.w_full]}> 989 <Toggle.LabelText style={[a.flex_1]}> 990 <Trans>Disable via repost notifications</Trans> 991 </Toggle.LabelText> 992 <Toggle.Platform /> 993 </Toggle.Item> 994 <Admonition type="info" style={[a.flex_1]}> 995 <Trans> 996 Forcefully disables the notifications other people receive when 997 you like/repost a post someone else has reposted for privacy. 998 </Trans> 999 </Admonition> 1000 1001 <Toggle.Item 1002 name="hide_similar_accounts_recommendations" 1003 label={_(msg`Hide similar accounts recommendations`)} 1004 value={hideSimilarAccountsRecomm} 1005 onChange={value => setHideSimilarAccountsRecomm(value)} 1006 style={[a.w_full]}> 1007 <Toggle.LabelText style={[a.flex_1]}> 1008 <Trans>Hide similar accounts recommendations</Trans> 1009 </Toggle.LabelText> 1010 <Toggle.Platform /> 1011 </Toggle.Item> 1012 1013 <Toggle.Item 1014 name="hide_unreplyable_posts" 1015 label={_(msg`Hide posts that cannot be replied to from feeds`)} 1016 value={hideUnreplyablePosts} 1017 onChange={value => setHideUnreplyablePosts(value)} 1018 style={[a.w_full]}> 1019 <Toggle.LabelText style={[a.flex_1]}> 1020 <Trans>Hide posts that cannot be replied to from feeds</Trans> 1021 </Toggle.LabelText> 1022 <Toggle.Platform /> 1023 </Toggle.Item> 1024 <Admonition type="info" style={[a.flex_1]}> 1025 <Trans> 1026 Hides posts from feeds where replies are disabled (e.g. due to 1027 postgates or other restrictions). Does not affect thread views. 1028 </Trans> 1029 </Admonition> 1030 1031 <Toggle.Item 1032 name="disable_composer_prompt" 1033 label={_(msg`Disable composer prompt`)} 1034 value={disableComposerPrompt} 1035 onChange={value => setDisableComposerPrompt(value)} 1036 style={[a.w_full]}> 1037 <Toggle.LabelText style={[a.flex_1]}> 1038 <Trans>Disable composer prompt</Trans> 1039 </Toggle.LabelText> 1040 <Toggle.Platform /> 1041 </Toggle.Item> 1042 1043 <Toggle.Item 1044 name="disable_verify_email_reminder" 1045 label={_(msg`Disable verify email reminder`)} 1046 value={disableVerifyEmailReminder} 1047 onChange={value => setDisableVerifyEmailReminder(value)} 1048 style={[a.w_full]}> 1049 <Toggle.LabelText style={[a.flex_1]}> 1050 <Trans>Disable verify email reminder</Trans> 1051 </Toggle.LabelText> 1052 <Toggle.Platform /> 1053 </Toggle.Item> 1054 <Admonition type="warning" style={[a.flex_1]}> 1055 <Trans> 1056 This only gets rid of the reminder on app launch, useful if your 1057 PDS does not have email verification setup.\nThis does NOT give 1058 access to features locked behind email verification. 1059 </Trans> 1060 </Admonition> 1061 1062 <Toggle.Item 1063 name="discover_context" 1064 label={_(msg`Show debug context for posts in Discover feed`)} 1065 value={discoverContextEnabled} 1066 onChange={value => setDiscoverContextEnabled(value)} 1067 style={[a.w_full]}> 1068 <Toggle.LabelText style={[a.flex_1]}> 1069 <Trans>Show debug context for posts in Discover feed</Trans> 1070 </Toggle.LabelText> 1071 <Toggle.Platform /> 1072 </Toggle.Item> 1073 </SettingsList.Group> 1074 1075 <SettingsList.Divider /> 1076 1077 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 1078 <SettingsList.ItemIcon icon={EarthIcon} /> 1079 <SettingsList.ItemText> 1080 <Trans>Post Translation Provider</Trans> 1081 </SettingsList.ItemText> 1082 1083 <Toggle.Item 1084 name="service_google" 1085 label={_(msg`Use Google Translate`)} 1086 value={translationServicePreference === 'google'} 1087 onChange={() => setTranslationServicePreference('google')} 1088 style={[a.w_full]}> 1089 <Toggle.LabelText style={[a.flex_1]}> 1090 <Trans>Use Google Translate</Trans> 1091 </Toggle.LabelText> 1092 <Toggle.Radio /> 1093 </Toggle.Item> 1094 1095 <Toggle.Item 1096 name="service_kagi" 1097 label={_(msg`Use Kagi Translate`)} 1098 value={translationServicePreference === 'kagi'} 1099 onChange={() => setTranslationServicePreference('kagi')} 1100 style={[a.w_full]}> 1101 <Toggle.LabelText style={[a.flex_1]}> 1102 <Trans>Use Kagi Translate</Trans> 1103 </Toggle.LabelText> 1104 <Toggle.Radio /> 1105 </Toggle.Item> 1106 1107 <Toggle.Item 1108 name="service_papago" 1109 label={_(msg`Use Naver Papago`)} 1110 value={translationServicePreference === 'papago'} 1111 onChange={() => setTranslationServicePreference('papago')} 1112 style={[a.w_full]}> 1113 <Toggle.LabelText style={[a.flex_1]}> 1114 <Trans>Use Naver Papago</Trans> 1115 </Toggle.LabelText> 1116 <Toggle.Radio /> 1117 </Toggle.Item> 1118 1119 <Toggle.Item 1120 name="service_libreTranslate" 1121 label={_(msg`Use LibreTranslate`)} 1122 value={translationServicePreference === 'libreTranslate'} 1123 onChange={() => setTranslationServicePreference('libreTranslate')} 1124 style={[a.w_full]}> 1125 <Toggle.LabelText style={[a.flex_1]}> 1126 <Trans>Use LibreTranslate</Trans> 1127 </Toggle.LabelText> 1128 <Toggle.Radio /> 1129 </Toggle.Item> 1130 </SettingsList.Group> 1131 1132 {translationServicePreference === 'libreTranslate' && ( 1133 <SettingsList.Item> 1134 <SettingsList.ItemIcon icon={EarthIcon} /> 1135 <SettingsList.ItemText> 1136 <Trans>{`LibreTranslate Instance`}</Trans> 1137 </SettingsList.ItemText> 1138 <SettingsList.BadgeButton 1139 label={_(msg`Change`)} 1140 onPress={() => setLibreTranslateInstanceControl.open()} 1141 /> 1142 </SettingsList.Item> 1143 )} 1144 1145 <SettingsList.Divider /> 1146 1147 <SettingsList.Item> 1148 <SettingsList.ItemIcon icon={_BeakerIcon} /> 1149 <SettingsList.ItemText> 1150 <Trans>OpenRouter API Key</Trans> 1151 </SettingsList.ItemText> 1152 <SettingsList.BadgeButton 1153 label={openRouterConfigured ? _(msg`Change`) : _(msg`Set`)} 1154 onPress={() => setOpenRouterApiKeyControl.open()} 1155 /> 1156 </SettingsList.Item> 1157 1158 <SettingsList.Item> 1159 <Admonition type="info" style={[a.flex_1]}> 1160 <Trans> 1161 Set your OpenRouter API key to enable AI-powered alt text 1162 generation for images in the composer. Get an API key at{' '} 1163 <InlineLinkText 1164 to="https://openrouter.ai" 1165 label="openrouter.ai"> 1166 openrouter.ai 1167 </InlineLinkText> 1168 </Trans> 1169 </Admonition> 1170 </SettingsList.Item> 1171 1172 {openRouterConfigured && ( 1173 <SettingsList.Item> 1174 <SettingsList.ItemIcon icon={_BeakerIcon} /> 1175 <SettingsList.ItemText> 1176 <Trans>{`OpenRouter Model`}</Trans> 1177 </SettingsList.ItemText> 1178 <SettingsList.BadgeButton 1179 label={_(msg`Change`)} 1180 onPress={() => setOpenRouterModelControl.open()} 1181 /> 1182 </SettingsList.Item> 1183 )} 1184 1185 {openRouterConfigured && ( 1186 <SettingsList.Item> 1187 <Admonition type="info" style={[a.flex_1]}> 1188 <Trans> 1189 Current model:{' '} 1190 {openRouterModel ?? DEFAULT_ALT_TEXT_AI_MODEL}.{' '} 1191 <InlineLinkText 1192 to="https://openrouter.ai/models?fmt=cards&input_modalities=image&order=most-popular" 1193 label="openrouter.ai"> 1194 Search models 1195 </InlineLinkText> 1196 </Trans> 1197 </Admonition> 1198 </SettingsList.Item> 1199 )} 1200 1201 <SettingsList.Divider /> 1202 1203 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 1204 <SettingsList.ItemIcon icon={VisibilityIcon} /> 1205 <SettingsList.ItemText> 1206 <Trans>Metrics</Trans> 1207 </SettingsList.ItemText> 1208 1209 <Toggle.Item 1210 name="disable_likes_metrics" 1211 label={_(msg`Disable likes metrics`)} 1212 value={disableLikesMetrics} 1213 onChange={value => setDisableLikesMetrics(value)} 1214 style={[a.w_full]}> 1215 <Toggle.LabelText style={[a.flex_1]}> 1216 <Trans>Disable likes metrics</Trans> 1217 </Toggle.LabelText> 1218 <Toggle.Platform /> 1219 </Toggle.Item> 1220 1221 <Toggle.Item 1222 name="disable_reposts_metrics" 1223 label={_(msg`Disable reposts metrics`)} 1224 value={disableRepostsMetrics} 1225 onChange={value => setDisableRepostsMetrics(value)} 1226 style={[a.w_full]}> 1227 <Toggle.LabelText style={[a.flex_1]}> 1228 <Trans>Disable reposts metrics</Trans> 1229 </Toggle.LabelText> 1230 <Toggle.Platform /> 1231 </Toggle.Item> 1232 1233 <Toggle.Item 1234 name="disable_quotes_metrics" 1235 label={_(msg`Disable quotes metrics`)} 1236 value={disableQuotesMetrics} 1237 onChange={value => setDisableQuotesMetrics(value)} 1238 style={[a.w_full]}> 1239 <Toggle.LabelText style={[a.flex_1]}> 1240 <Trans>Disable quotes metrics</Trans> 1241 </Toggle.LabelText> 1242 <Toggle.Platform /> 1243 </Toggle.Item> 1244 1245 <Toggle.Item 1246 name="disable_saves_metrics" 1247 label={_(msg`Disable saves metrics`)} 1248 value={disableSavesMetrics} 1249 onChange={value => setDisableSavesMetrics(value)} 1250 style={[a.w_full]}> 1251 <Toggle.LabelText style={[a.flex_1]}> 1252 <Trans>Disable saves metrics</Trans> 1253 </Toggle.LabelText> 1254 <Toggle.Platform /> 1255 </Toggle.Item> 1256 1257 <Toggle.Item 1258 name="disable_reply_metrics" 1259 label={_(msg`Disable reply metrics`)} 1260 value={disableReplyMetrics} 1261 onChange={value => setDisableReplyMetrics(value)} 1262 style={[a.w_full]}> 1263 <Toggle.LabelText style={[a.flex_1]}> 1264 <Trans>Disable reply metrics</Trans> 1265 </Toggle.LabelText> 1266 <Toggle.Platform /> 1267 </Toggle.Item> 1268 1269 <Toggle.Item 1270 name="disable_followers_metrics" 1271 label={_(msg`Disable followers metrics`)} 1272 value={disableFollowersMetrics} 1273 onChange={value => setDisableFollowersMetrics(value)} 1274 style={[a.w_full]}> 1275 <Toggle.LabelText style={[a.flex_1]}> 1276 <Trans>Disable followers metrics</Trans> 1277 </Toggle.LabelText> 1278 <Toggle.Platform /> 1279 </Toggle.Item> 1280 1281 <Toggle.Item 1282 name="disable_following_metrics" 1283 label={_(msg`Disable following metrics`)} 1284 value={disableFollowingMetrics} 1285 onChange={value => setDisableFollowingMetrics(value)} 1286 style={[a.w_full]}> 1287 <Toggle.LabelText style={[a.flex_1]}> 1288 <Trans>Disable following metrics</Trans> 1289 </Toggle.LabelText> 1290 <Toggle.Platform /> 1291 </Toggle.Item> 1292 1293 <Toggle.Item 1294 name="disable_followed_by_metrics" 1295 label={_(msg`Disable "followed by" metrics`)} 1296 value={disableFollowedByMetrics} 1297 onChange={value => setDisableFollowedByMetrics(value)} 1298 style={[a.w_full]}> 1299 <Toggle.LabelText style={[a.flex_1]}> 1300 <Trans>Disable "followed by" metrics</Trans> 1301 </Toggle.LabelText> 1302 <Toggle.Platform /> 1303 </Toggle.Item> 1304 1305 <Toggle.Item 1306 name="disable_posts_metrics" 1307 label={_(msg`Disable posts metrics`)} 1308 value={disablePostsMetrics} 1309 onChange={value => setDisablePostsMetrics(value)} 1310 style={[a.w_full]}> 1311 <Toggle.LabelText style={[a.flex_1]}> 1312 <Trans>Disable posts metrics</Trans> 1313 </Toggle.LabelText> 1314 <Toggle.Platform /> 1315 </Toggle.Item> 1316 </SettingsList.Group> 1317 1318 <SettingsList.Divider /> 1319 1320 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 1321 <SettingsList.ItemIcon icon={RaisingHandIcon} /> 1322 <SettingsList.ItemText> 1323 <Trans>Labelers</Trans> 1324 </SettingsList.ItemText> 1325 <Toggle.Item 1326 name="no_app_labelers" 1327 label={_(msg`Do not declare any app labelers`)} 1328 value={noAppLabelers} 1329 onChange={value => setNoAppLabelers(value)} 1330 style={[a.w_full]}> 1331 <Toggle.LabelText style={[a.flex_1]}> 1332 <Trans>Do not declare any default app labelers</Trans> 1333 </Toggle.LabelText> 1334 <Toggle.Platform /> 1335 </Toggle.Item> 1336 </SettingsList.Group> 1337 1338 <SettingsList.Item> 1339 <Admonition type="warning" style={[a.flex_1]}> 1340 <Trans>Restart the app after changing this setting.</Trans> 1341 </Admonition> 1342 </SettingsList.Item> 1343 <SettingsList.Item> 1344 <Admonition type="tip" style={[a.flex_1]}> 1345 <Trans> 1346 Some App Views will default to using an app labeler if you have 1347 no labelers, so consider subscribing to at least one labeler if 1348 you have issues. 1349 </Trans> 1350 </Admonition> 1351 </SettingsList.Item> 1352 <SettingsList.Item> 1353 <Admonition type="info" style={[a.flex_1]}> 1354 <Trans> 1355 App labelers are mandatory top-level labelers that can perform 1356 "takedowns". This setting does not influence geolocation-based 1357 labelers. 1358 </Trans> 1359 </Admonition> 1360 </SettingsList.Item> 1361 </SettingsList.Container> 1362 </Layout.Content> 1363 <ConstellationInstanceDialog control={setConstellationInstanceControl} /> 1364 <TrustedVerifiersDialog control={setTrustedVerifiersDialogControl} /> 1365 <LibreTranslateInstanceDialog 1366 control={setLibreTranslateInstanceControl} 1367 /> 1368 <PostReplacementDialog control={setPostReplacementDialogControl} /> 1369 <OpenRouterApiKeyDialog control={setOpenRouterApiKeyControl} /> 1370 <OpenRouterModelDialog control={setOpenRouterModelControl} /> 1371 </Layout.Screen> 1372 ) 1373} 1374 1375const styles = { 1376 textInput: { 1377 borderWidth: 1, 1378 borderRadius: 6, 1379 paddingHorizontal: 14, 1380 paddingVertical: 10, 1381 fontSize: 16, 1382 }, 1383}