ai/at
1
0

test gemini

This commit is contained in:
2025-12-07 18:41:05 +09:00
parent ba50f383fb
commit 6b4656b43d
6 changed files with 491 additions and 1031 deletions

View File

@@ -1,318 +0,0 @@
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
index 4f25468c9..95b183dcc 100644
--- a/src/view/com/posts/PostFeed.tsx
+++ b/src/view/com/posts/PostFeed.tsx
@@ -298,8 +298,13 @@ let PostFeed = ({
}
}
} catch (e) {
+ const errorMsg = String(e)
+ // Skip errors for missing repos (deleted accounts)
+ if (errorMsg.includes('Could not find repo')) {
+ return
+ }
if (!isNetworkError(e)) {
- logger.error('Poll latest failed', {feed, message: String(e)})
+ logger.error('Poll latest failed', {feed, message: errorMsg})
}
}
})
diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
index a89eaadc4..c3a8b6114 100644
--- a/src/view/screens/PrivacyPolicy.tsx
+++ b/src/view/screens/PrivacyPolicy.tsx
@@ -1,51 +1,95 @@
import React from 'react'
-import {View} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useFocusEffect} from '@react-navigation/native'
-
-import {usePalette} from '#/lib/hooks/usePalette'
-import {
- type CommonNavigatorParams,
- type NativeStackScreenProps,
-} from '#/lib/routes/types'
-import {s} from '#/lib/styles'
-import {useSetMinimalShellMode} from '#/state/shell'
-import {TextLink} from '#/view/com/util/Link'
-import {Text} from '#/view/com/util/text/Text'
-import {ScrollView} from '#/view/com/util/Views'
+import { ScrollView } from 'react-native'
import * as Layout from '#/components/Layout'
-import {ViewHeader} from '../com/util/ViewHeader'
-
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'>
-export const PrivacyPolicyScreen = (_props: Props) => {
- const pal = usePalette('default')
- const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode()
-
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
+import {atoms as a, useTheme} from '#/alf'
+import {Text} from '#/components/Typography'
+
+export function PrivacyPolicyScreen() {
+ useSetTitle('Privacy Policy')
+ const t = useTheme()
return (
<Layout.Screen>
- <ViewHeader title={_(msg`Privacy Policy`)} />
- <ScrollView style={[s.hContentRegion, pal.view]}>
- <View style={[s.p20]}>
- <Text style={pal.text}>
- <Trans>
- The Privacy Policy has been moved to{' '}
- <TextLink
- style={pal.link}
- href="https://bsky.social/about/support/privacy-policy"
- text="bsky.social/about/support/privacy-policy"
- />
- </Trans>
- </Text>
- </View>
- <View style={s.footerSpacer} />
+ <ScrollView
+ style={[a.flex_1, {backgroundColor: t.palette.white}]}
+ contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
+ <Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>Privacy Policy</Text>
+
+ <Text style={[a.mb_md]}>
+ This application (hereinafter referred to as "the App") respects user privacy and is committed to protecting personal information.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>1. Information We Collect</Text>
+ <Text style={[a.mb_md]}>
+ The App is a decentralized social network using the AT Protocol.
+ Information posted by users is stored on the selected server (PDS).
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>2. Use of Information</Text>
+ <Text style={[a.mb_md]}>
+ The collected information is used for the following purposes:
+ {'\n'}- Providing and operating the service
+ {'\n'}- User support
+ {'\n'}- Service improvement
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>3. Third-Party Disclosure</Text>
+ <Text style={[a.mb_md]}>
+ The App will not provide users' personal information to third parties without user consent, except as required by law.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>4. Security</Text>
+ <Text style={[a.mb_md]}>
+ The App takes appropriate security measures to protect personal information.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>5. Contact</Text>
+ <Text style={[a.mb_md]}>
+ For questions regarding this Privacy Policy, please contact us through the support link in the settings screen.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_5xl, a.mb_md]}>日本語訳(参考)</Text>
+
+ <Text style={[a.text_xl, a.font_bold, a.mb_lg]}>プライバシーポリシー</Text>
+
+ <Text style={[a.mb_md]}>
+ 本アプリケーション(以下「本アプリ」)は、ユーザーのプライバシーを尊重し、個人情報の保護に努めます。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>1. 収集する情報</Text>
+ <Text style={[a.mb_md]}>
+ 本アプリは、ATプロトコルを使用した分散型ソーシャルネットワークです。
+ ユーザーが投稿した情報は、選択したサーバーPDSに保存されます。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>2. 情報の利用目的</Text>
+ <Text style={[a.mb_md]}>
+ 収集した情報は、以下の目的で利用されます:
+ {'\n'}- サービスの提供・運営
+ {'\n'}- ユーザーサポート
+ {'\n'}- サービスの改善
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>3. 情報の第三者提供</Text>
+ <Text style={[a.mb_md]}>
+ 本アプリは、ユーザーの個人情報を、法令に基づく場合を除き、
+ ユーザーの同意なく第三者に提供することはありません。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>4. セキュリティ</Text>
+ <Text style={[a.mb_md]}>
+ 本アプリは、個人情報の保護のため、適切な安全対策を講じます。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>5. お問い合わせ</Text>
+ <Text style={[a.mb_md]}>
+ プライバシーポリシーに関するご質問は、設定画面のサポートリンクからお問い合わせください。
+ </Text>
+
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
+ Last Updated: December 7, 2025
+ </Text>
</ScrollView>
</Layout.Screen>
)
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
index d843c713c..3d2323f73 100644
--- a/src/view/screens/TermsOfService.tsx
+++ b/src/view/screens/TermsOfService.tsx
@@ -1,49 +1,110 @@
import React from 'react'
-import {View} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useFocusEffect} from '@react-navigation/native'
-
-import {usePalette} from '#/lib/hooks/usePalette'
-import {
- type CommonNavigatorParams,
- type NativeStackScreenProps,
-} from '#/lib/routes/types'
-import {s} from '#/lib/styles'
-import {useSetMinimalShellMode} from '#/state/shell'
-import {TextLink} from '#/view/com/util/Link'
-import {Text} from '#/view/com/util/text/Text'
-import {ScrollView} from '#/view/com/util/Views'
+import { ScrollView } from 'react-native'
import * as Layout from '#/components/Layout'
-import {ViewHeader} from '../com/util/ViewHeader'
-
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
-export const TermsOfServiceScreen = (_props: Props) => {
- const pal = usePalette('default')
- const setMinimalShellMode = useSetMinimalShellMode()
- const {_} = useLingui()
-
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
+import {atoms as a, useTheme} from '#/alf'
+import {Text} from '#/components/Typography'
+
+export function TermsOfServiceScreen() {
+ useSetTitle('Terms of Service')
+ const t = useTheme()
return (
<Layout.Screen>
- <ViewHeader title={_(msg`Terms of Service`)} />
- <ScrollView style={[s.hContentRegion, pal.view]}>
- <View style={[s.p20]}>
- <Text style={pal.text}>
- <Trans>The Terms of Service have been moved to</Trans>{' '}
- <TextLink
- style={pal.link}
- href="https://bsky.social/about/support/tos"
- text="bsky.social/about/support/tos"
- />
- </Text>
- </View>
- <View style={s.footerSpacer} />
+ <ScrollView
+ style={[a.flex_1, {backgroundColor: t.palette.white}]}
+ contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
+ <Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>Terms of Service</Text>
+
+ <Text style={[a.mb_md]}>
+ These Terms of Service (hereinafter referred to as "these Terms") set forth the conditions for using this application (hereinafter referred to as "the App").
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 1 (Application)</Text>
+ <Text style={[a.mb_md]}>
+ These Terms shall apply to all relationships between users and the provider of the App regarding the use of the App.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 2 (User Registration)</Text>
+ <Text style={[a.mb_md]}>
+ Use of the App requires an AT Protocol-compatible account.
+ Registration information must be kept accurate and up-to-date.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 3 (Prohibited Acts)</Text>
+ <Text style={[a.mb_md]}>
+ Users shall not engage in the following acts when using the App:
+ {'\n'}- Acts that violate laws or public order and morals
+ {'\n'}- Acts related to criminal activity
+ {'\n'}- Acts that infringe upon the rights of other users or third parties
+ {'\n'}- Acts that interfere with the operation of the App
+ {'\n'}- Unauthorized access or attempts thereof
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 4 (Disclaimer)</Text>
+ <Text style={[a.mb_md]}>
+ The App is a decentralized service using the AT Protocol.
+ The provider makes no warranties, express or implied, regarding the App.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 5 (Changes to Terms)</Text>
+ <Text style={[a.mb_md]}>
+ The provider may change these Terms without obtaining user consent when deemed necessary.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 6 (Contact)</Text>
+ <Text style={[a.mb_md]}>
+ For questions regarding these Terms, please contact us through the support link in the settings screen.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_5xl, a.mb_md]}>日本語訳(参考)</Text>
+
+ <Text style={[a.text_xl, a.font_bold, a.mb_lg]}>利用規約</Text>
+
+ <Text style={[a.mb_md]}>
+ 本利用規約(以下「本規約」)は、本アプリケーション(以下「本アプリ」)の利用条件を定めるものです。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第1条適用</Text>
+ <Text style={[a.mb_md]}>
+ 本規約は、ユーザーと本アプリの提供者との間の本アプリの利用に関わる一切の関係に適用されます。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第2条利用登録</Text>
+ <Text style={[a.mb_md]}>
+ 本アプリの利用にあたっては、ATプロトコル対応のアカウントが必要です。
+ 登録情報は正確かつ最新の状態に保つ必要があります。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第3条禁止事項</Text>
+ <Text style={[a.mb_md]}>
+ ユーザーは、本アプリの利用にあたり、以下の行為をしてはなりません:
+ {'\n'}- 法令または公序良俗に違反する行為
+ {'\n'}- 犯罪行為に関連する行為
+ {'\n'}- 他のユーザーまたは第三者の権利を侵害する行為
+ {'\n'}- 本アプリの運営を妨害する行為
+ {'\n'}- 不正アクセスまたはこれを試みる行為
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第4条免責事項</Text>
+ <Text style={[a.mb_md]}>
+ 本アプリは、ATプロトコルを使用した分散型サービスです。
+ 提供者は、本アプリに関して、明示的・黙示的を問わず、いかなる保証も行いません。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第5条規約の変更</Text>
+ <Text style={[a.mb_md]}>
+ 提供者は、必要と判断した場合、ユーザーの承諾を得ることなく本規約を変更できるものとします。
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第6条お問い合わせ</Text>
+ <Text style={[a.mb_md]}>
+ 本規約に関するご質問は、設定画面のサポートリンクからお問い合わせください。
+ </Text>
+
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
+ Last Updated: December 7, 2025
+ </Text>
</ScrollView>
</Layout.Screen>
)

View File

@@ -1,681 +1,177 @@
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index fa33a9d56..13af087c2 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -62,6 +62,7 @@ import {NotFoundScreen} from '#/view/screens/NotFound'
import {NotificationsScreen} from '#/view/screens/Notifications'
import {PostThreadScreen} from '#/view/screens/PostThread'
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
+import {LicenseScreen} from '#/view/screens/License'
import {ProfileScreen} from '#/view/screens/Profile'
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
import {Storybook} from '#/view/screens/Storybook'
@@ -335,6 +336,11 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
getComponent={() => TermsOfServiceScreen}
options={{title: title(msg`Terms of Service`)}}
/>
+ <Stack.Screen
+ name="License"
+ getComponent={() => LicenseScreen}
+ options={{title: title(msg`License`)}}
+ />
<Stack.Screen
name="CommunityGuidelines"
getComponent={() => CommunityGuidelinesScreen}
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
index c315a8341..9b2f50a83 100644
index c315a8341..6d97a6928 100644
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -39,6 +39,7 @@ export type CommonNavigatorParams = {
Support: undefined
PrivacyPolicy: undefined
@@ -1,9 +1,9 @@
-import {type NavigationState, type PartialState} from '@react-navigation/native'
-import {type NativeStackNavigationProp} from '@react-navigation/native-stack'
+import { type NavigationState, type PartialState } from '@react-navigation/native'
+import { type NativeStackNavigationProp } from '@react-navigation/native-stack'
-import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types'
+import { type VideoFeedSourceContext } from '#/screens/VideoFeed/types'
-export type {NativeStackScreenProps} from '@react-navigation/native-stack'
+export type { NativeStackScreenProps } from '@react-navigation/native-stack'
export type CommonNavigatorParams = {
NotFound: undefined
@@ -15,23 +15,23 @@ export type CommonNavigatorParams = {
ModerationInteractionSettings: undefined
ModerationVerificationSettings: undefined
Settings: undefined
- Profile: {name: string; hideBackButton?: boolean}
- ProfileFollowers: {name: string}
- ProfileFollows: {name: string}
- ProfileKnownFollowers: {name: string}
- ProfileSearch: {name: string; q?: string}
- ProfileList: {name: string; rkey: string}
- PostThread: {name: string; rkey: string}
- PostLikedBy: {name: string; rkey: string}
- PostRepostedBy: {name: string; rkey: string}
- PostQuotes: {name: string; rkey: string}
+ Profile: { name: string; hideBackButton?: boolean }
+ ProfileFollowers: { name: string }
+ ProfileFollows: { name: string }
+ ProfileKnownFollowers: { name: string }
+ ProfileSearch: { name: string; q?: string }
+ ProfileList: { name: string; rkey: string }
+ PostThread: { name: string; rkey: string }
+ PostLikedBy: { name: string; rkey: string }
+ PostRepostedBy: { name: string; rkey: string }
+ PostQuotes: { name: string; rkey: string }
ProfileFeed: {
name: string
rkey: string
feedCacheKey?: 'discover' | 'explore' | undefined
}
- ProfileFeedLikedBy: {name: string; rkey: string}
- ProfileLabelerLikedBy: {name: string}
+ ProfileFeedLikedBy: { name: string; rkey: string }
+ ProfileLabelerLikedBy: { name: string }
Debug: undefined
DebugMod: undefined
SharedPreferencesTester: undefined
@@ -41,6 +41,7 @@ export type CommonNavigatorParams = {
TermsOfService: undefined
+ License: undefined
CommunityGuidelines: undefined
CopyrightPolicy: undefined
+ License: undefined
LanguageSettings: undefined
AppPasswords: undefined
SavedFeeds: undefined
@@ -67,24 +68,24 @@ export type CommonNavigatorParams = {
InterestsSettings: undefined
AboutSettings: undefined
AppIconSettings: undefined
- Search: {q?: string; tab?: 'user' | 'profile' | 'feed'}
- Hashtag: {tag: string; author?: string}
- Topic: {topic: string}
- MessagesConversation: {conversation: string; embed?: string; accept?: true}
+ Search: { q?: string; tab?: 'user' | 'profile' | 'feed' }
+ Hashtag: { tag: string; author?: string }
+ Topic: { topic: string }
+ MessagesConversation: { conversation: string; embed?: string; accept?: true }
MessagesSettings: undefined
MessagesInbox: undefined
- NotificationsActivityList: {posts: string}
+ NotificationsActivityList: { posts: string }
LegacyNotificationSettings: undefined
Feeds: undefined
- Start: {name: string; rkey: string}
- StarterPack: {name: string; rkey: string; new?: boolean}
- StarterPackShort: {code: string}
+ Start: { name: string; rkey: string }
+ StarterPack: { name: string; rkey: string; new?: boolean }
+ StarterPackShort: { code: string }
StarterPackWizard: {
fromDialog?: boolean
targetDid?: string
onSuccess?: () => void
}
- StarterPackEdit: {rkey?: string}
+ StarterPackEdit: { rkey?: string }
VideoFeed: VideoFeedSourceContext
Bookmarks: undefined
}
@@ -102,7 +103,7 @@ export type HomeTabNavigatorParams = CommonNavigatorParams & {
}
export type SearchTabNavigatorParams = CommonNavigatorParams & {
- Search: {q?: string; tab?: 'user' | 'profile' | 'feed'}
+ Search: { q?: string; tab?: 'user' | 'profile' | 'feed' }
}
export type NotificationsTabNavigatorParams = CommonNavigatorParams & {
@@ -110,32 +111,32 @@ export type NotificationsTabNavigatorParams = CommonNavigatorParams & {
}
export type MyProfileTabNavigatorParams = CommonNavigatorParams & {
- MyProfile: {name: 'me'; hideBackButton: true}
+ MyProfile: { name: 'me'; hideBackButton: true }
}
export type MessagesTabNavigatorParams = CommonNavigatorParams & {
- Messages: {pushToConversation?: string; animation?: 'push' | 'pop'}
+ Messages: { pushToConversation?: string; animation?: 'push' | 'pop' }
}
export type FlatNavigatorParams = CommonNavigatorParams & {
Home: undefined
- Search: {q?: string; tab?: 'user' | 'profile' | 'feed'}
+ Search: { q?: string; tab?: 'user' | 'profile' | 'feed' }
Feeds: undefined
Notifications: undefined
- Messages: {pushToConversation?: string; animation?: 'push' | 'pop'}
+ Messages: { pushToConversation?: string; animation?: 'push' | 'pop' }
}
export type AllNavigatorParams = CommonNavigatorParams & {
HomeTab: undefined
Home: undefined
SearchTab: undefined
- Search: {q?: string; tab?: 'user' | 'profile' | 'feed'}
+ Search: { q?: string; tab?: 'user' | 'profile' | 'feed' }
Feeds: undefined
NotificationsTab: undefined
Notifications: undefined
MyProfileTab: undefined
MessagesTab: undefined
- Messages: {animation?: 'push' | 'pop'}
+ Messages: { animation?: 'push' | 'pop' }
}
// NOTE
@@ -149,7 +150,7 @@ export type State =
| Omit<PartialState<NavigationState>, 'stale'>
export type RouteParams = Record<string, string>
-export type MatchResult = {params: RouteParams}
+export type MatchResult = { params: RouteParams }
export type Route = {
match: (path: string) => MatchResult | undefined
build: (params?: Record<string, any>) => string
diff --git a/src/routes.ts b/src/routes.ts
index 1ed913bb2..aa6fccc4e 100644
index 77ea6deca..b57955ed2 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -1,5 +1,5 @@
-import {Router} from '#/lib/routes/router'
-import {type FlatNavigatorParams} from './lib/routes/types'
+import { Router } from '#/lib/routes/router'
+import { type FlatNavigatorParams } from './lib/routes/types'
type AllNavigatableRoutes = Omit<
FlatNavigatorParams,
@@ -71,8 +71,9 @@ export const router = new Router<AllNavigatableRoutes>({
MiscellaneousNotificationSettings: '/settings/notifications/miscellaneous',
// support
Support: '/support',
- PrivacyPolicy: '/support/privacy',
- TermsOfService: '/support/tos',
+ PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
+ TermsOfService: 'https://syu.is/about/support/tos',
- PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
- TermsOfService: 'https://syu.is/about/support/tos',
+ PrivacyPolicy: '/support/privacy',
+ TermsOfService: '/support/tos',
+ License: '/support/license',
CommunityGuidelines: '/support/community-guidelines',
CopyrightPolicy: '/support/copyright',
// hashtags
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index ed2a6cfb7..982c40b55 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -1,60 +1,50 @@
-import React, {type ComponentProps, type JSX} from 'react'
-import {Linking, ScrollView, TouchableOpacity, View} from 'react-native'
-import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import {msg, Plural, plural, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {StackActions, useNavigation} from '@react-navigation/native'
-
-import {useActorStatus} from '#/lib/actor-status'
-import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
-import {type PressableScale} from '#/lib/custom-animations/PressableScale'
-import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
-import {getTabState, TabState} from '#/lib/routes/helpers'
-import {type NavigationProp} from '#/lib/routes/types'
-import {sanitizeHandle} from '#/lib/strings/handles'
-import {colors} from '#/lib/styles'
-import {isWeb} from '#/platform/detection'
-import {emitSoftReset} from '#/state/events'
-import {useKawaiiMode} from '#/state/preferences/kawaii'
-import {useUnreadNotifications} from '#/state/queries/notifications/unread'
-import {useProfileQuery} from '#/state/queries/profile'
-import {type SessionAccount, useSession} from '#/state/session'
-import {useSetDrawerOpen} from '#/state/shell'
-import {formatCount} from '#/view/com/util/numeric/format'
-import {UserAvatar} from '#/view/com/util/UserAvatar'
-import {NavSignupCard} from '#/view/shell/NavSignupCard'
-import {atoms as a, tokens, useTheme, web} from '#/alf'
-import {Button, ButtonIcon, ButtonText} from '#/components/Button'
-import {Divider} from '#/components/Divider'
+import React, { type ComponentProps, type JSX } from 'react'
+import { Linking, ScrollView, TouchableOpacity, View } from 'react-native'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { msg, Plural, plural, Trans } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import { StackActions, useNavigation } from '@react-navigation/native'
+
+import { useActorStatus } from '#/lib/actor-status'
+import { FEEDBACK_FORM_URL, HELP_DESK_URL } from '#/lib/constants'
+import { type PressableScale } from '#/lib/custom-animations/PressableScale'
+import { useNavigationTabState } from '#/lib/hooks/useNavigationTabState'
+import { getTabState, TabState } from '#/lib/routes/helpers'
+import { type NavigationProp } from '#/lib/routes/types'
+import { sanitizeHandle } from '#/lib/strings/handles'
+import { colors } from '#/lib/styles'
+import { isWeb } from '#/platform/detection'
+import { emitSoftReset } from '#/state/events'
+import { useKawaiiMode } from '#/state/preferences/kawaii'
+import { useUnreadNotifications } from '#/state/queries/notifications/unread'
+import { useProfileQuery } from '#/state/queries/profile'
+import { type SessionAccount, useSession } from '#/state/session'
+import { useSetDrawerOpen } from '#/state/shell'
+import { formatCount } from '#/view/com/util/numeric/format'
+import { UserAvatar } from '#/view/com/util/UserAvatar'
+import { NavSignupCard } from '#/view/shell/NavSignupCard'
+import { atoms as a, tokens, useTheme, web } from '#/alf'
+import { Button } from '#/components/Button'
+import { Divider } from '#/components/Divider'
import {
Bell_Filled_Corner0_Rounded as BellFilled,
Bell_Stroke2_Corner0_Rounded as Bell,
} from '#/components/icons/Bell'
-import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
-import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList'
-import {
- Hashtag_Filled_Corner0_Rounded as HashtagFilled,
- Hashtag_Stroke2_Corner0_Rounded as Hashtag,
-} from '#/components/icons/Hashtag'
import {
HomeOpen_Filled_Corner0_Rounded as HomeFilled,
HomeOpen_Stoke2_Corner0_Rounded as Home,
} from '#/components/icons/HomeOpen'
-import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass'
-import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2'
-import {
- Message_Stroke2_Corner0_Rounded as Message,
- Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
-} from '#/components/icons/Message'
-import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2'
+import { MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled } from '#/components/icons/MagnifyingGlass'
+import { MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass } from '#/components/icons/MagnifyingGlass2'
+import { SettingsGear2_Stroke2_Corner0_Rounded as Settings } from '#/components/icons/SettingsGear2'
import {
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
} from '#/components/icons/UserCircle'
-import {InlineLinkText} from '#/components/Link'
-import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
+import { InlineLinkText } from '#/components/Link'
+import { Text } from '#/components/Typography'
+import { useSimpleVerificationState } from '#/components/verification'
+import { VerificationCheck } from '#/components/verification/VerificationCheck'
const iconWidth = 26
@@ -65,11 +55,11 @@ let DrawerProfileCard = ({
account: SessionAccount
onPressProfile: () => void
}): React.ReactNode => {
- const {_, i18n} = useLingui()
+ const { _, i18n } = useLingui()
const t = useTheme()
- const {data: profile} = useProfileQuery({did: account.did})
- const verification = useSimpleVerificationState({profile})
- const {isActive: live} = useActorStatus(profile)
+ const { data: profile } = useProfileQuery({ did: account.did })
+ const verification = useSimpleVerificationState({ profile })
+ const { isActive: live } = useActorStatus(profile)
return (
<TouchableOpacity
@@ -81,7 +71,6 @@ let DrawerProfileCard = ({
<UserAvatar
size={52}
avatar={profile?.avatar}
- // See https://github.com/bluesky-social/social-app/pull/1801:
usePlainRNImage={true}
type={profile?.associated?.labeler ? 'labeler' : 'user'}
live={live}
@@ -140,9 +129,9 @@ let DrawerProfileCard = ({
)
}
DrawerProfileCard = React.memo(DrawerProfileCard)
-export {DrawerProfileCard}
+export { DrawerProfileCard }
-let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
+let DrawerContent = ({ }: React.PropsWithoutRef<{}>): React.ReactNode => {
const t = useTheme()
const insets = useSafeAreaInsets()
const setDrawerOpen = useSetDrawerOpen()
@@ -150,27 +139,20 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
const {
isAtHome,
isAtSearch,
- isAtFeeds,
- isAtBookmarks,
isAtNotifications,
isAtMyProfile,
- isAtMessages,
} = useNavigationTabState()
- const {hasSession, currentAccount} = useSession()
-
- // events
- // =
+ const { hasSession, currentAccount } = useSession()
const onPressTab = React.useCallback(
(tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => {
const state = navigation.getState()
setDrawerOpen(false)
if (isWeb) {
- // hack because we have flat navigator for web and MyProfile does not exist on the web navigator -ansh
if (tab === 'MyProfile') {
- navigation.navigate('Profile', {name: currentAccount!.handle})
+ navigation.navigate('Profile', { name: currentAccount!.handle })
} else {
- // @ts-expect-error struggles with string unions, apparently
+ // @ts-expect-error struggles with string unions
navigation.navigate(tab)
}
} else {
@@ -178,21 +160,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
if (tabState === TabState.InsideAtRoot) {
emitSoftReset()
} else if (tabState === TabState.Inside) {
- // find the correct navigator in which to pop-to-top
- const target = state.routes.find(route => route.name === `${tab}Tab`)
- ?.state?.key
+ const target = state.routes.find(route => route.name === `${tab}Tab`)?.state?.key
if (target) {
- // if we found it, trigger pop-to-top
- navigation.dispatch({
- ...StackActions.popToTop(),
- target,
- })
+ navigation.dispatch({ ...StackActions.popToTop(), target })
} else {
- // fallback: reset navigation
- navigation.reset({
- index: 0,
- routes: [{name: `${tab}Tab`}],
- })
+ navigation.reset({ index: 0, routes: [{ name: `${tab}Tab` }] })
}
} else {
navigation.navigate(`${tab}Tab`)
@@ -203,76 +175,21 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
)
const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
-
- const onPressSearch = React.useCallback(
- () => onPressTab('Search'),
- [onPressTab],
- )
-
- const onPressMessages = React.useCallback(
- () => onPressTab('Messages'),
- [onPressTab],
- )
-
- const onPressNotifications = React.useCallback(
- () => onPressTab('Notifications'),
- [onPressTab],
- )
-
- const onPressProfile = React.useCallback(() => {
- onPressTab('MyProfile')
- }, [onPressTab])
-
- const onPressMyFeeds = React.useCallback(() => {
- navigation.navigate('Feeds')
- setDrawerOpen(false)
- }, [navigation, setDrawerOpen])
-
- const onPressLists = React.useCallback(() => {
- navigation.navigate('Lists')
- setDrawerOpen(false)
- }, [navigation, setDrawerOpen])
-
- const onPressBookmarks = React.useCallback(() => {
- navigation.navigate('Bookmarks')
- setDrawerOpen(false)
- }, [navigation, setDrawerOpen])
-
+ const onPressSearch = React.useCallback(() => onPressTab('Search'), [onPressTab])
+ const onPressNotifications = React.useCallback(() => onPressTab('Notifications'), [onPressTab])
+ const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab])
const onPressSettings = React.useCallback(() => {
navigation.navigate('Settings')
setDrawerOpen(false)
}, [navigation, setDrawerOpen])
- const onPressFeedback = React.useCallback(() => {
- Linking.openURL(
- FEEDBACK_FORM_URL({
- email: currentAccount?.email,
- handle: currentAccount?.handle,
- }),
- )
- }, [currentAccount])
-
- const onPressHelp = React.useCallback(() => {
- Linking.openURL(HELP_DESK_URL)
- }, [])
-
- // rendering
- // =
-
return (
<View
testID="drawer"
style={[a.flex_1, a.border_r, t.atoms.bg, t.atoms.border_contrast_low]}>
<ScrollView
style={[a.flex_1]}
- contentContainerStyle={[
- {
- paddingTop: Math.max(
- insets.top + a.pt_xl.paddingTop,
- a.pt_xl.paddingTop,
- ),
- },
- ]}>
+ contentContainerStyle={[{ paddingTop: Math.max(insets.top + a.pt_xl.paddingTop, a.pt_xl.paddingTop) }]}>
<View style={[a.px_xl]}>
{hasSession && currentAccount ? (
<DrawerProfileCard
@@ -284,7 +201,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
<NavSignupCard />
</View>
)}
-
<Divider style={[a.mt_xl, a.mb_sm]} />
</View>
@@ -292,17 +208,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
<>
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
- <ChatMenuItem isActive={isAtMessages} onPress={onPressMessages} />
<NotificationsMenuItem
isActive={isAtNotifications}
onPress={onPressNotifications}
/>
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
- <ListsMenuItem onPress={onPressLists} />
- <BookmarksMenuItem
- isActive={isAtBookmarks}
- onPress={onPressBookmarks}
- />
<ProfileMenuItem
isActive={isAtMyProfile}
onPress={onPressProfile}
@@ -312,7 +221,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
) : (
<>
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
</>
)}
@@ -322,69 +230,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
<ExtraLinks />
</View>
</ScrollView>
-
- <DrawerFooter
- onPressFeedback={onPressFeedback}
- onPressHelp={onPressHelp}
- />
</View>
)
}
DrawerContent = React.memo(DrawerContent)
-export {DrawerContent}
-
-let DrawerFooter = ({
- onPressFeedback,
- onPressHelp,
-}: {
- onPressFeedback: () => void
- onPressHelp: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const insets = useSafeAreaInsets()
- return (
- <View
- style={[
- a.flex_row,
- a.gap_sm,
- a.flex_wrap,
- a.pl_xl,
- a.pt_md,
- {
- paddingBottom: Math.max(
- insets.bottom + tokens.space.xs,
- tokens.space.xl,
- ),
- },
- ]}>
- <Button
- label={_(msg`Send feedback`)}
- size="small"
- variant="solid"
- color="secondary"
- onPress={onPressFeedback}>
- <ButtonIcon icon={Message} position="left" />
- <ButtonText>
- <Trans>Feedback</Trans>
- </ButtonText>
- </Button>
- <Button
- label={_(msg`Get help`)}
- size="small"
- variant="outline"
- color="secondary"
- onPress={onPressHelp}
- style={{
- backgroundColor: 'transparent',
- }}>
- <ButtonText>
- <Trans>Help</Trans>
- </ButtonText>
- </Button>
- </View>
- )
-}
-DrawerFooter = React.memo(DrawerFooter)
+export { DrawerContent }
interface MenuItemProps extends ComponentProps<typeof PressableScale> {
icon: JSX.Element
@@ -400,7 +250,7 @@ let SearchMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -426,7 +276,7 @@ let HomeMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -445,32 +295,6 @@ let HomeMenuItem = ({
}
HomeMenuItem = React.memo(HomeMenuItem)
-let ChatMenuItem = ({
- isActive,
- onPress,
-}: {
- isActive: boolean
- onPress: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
- return (
- <MenuItem
- icon={
- isActive ? (
- <MessageFilled style={[t.atoms.text]} width={iconWidth} />
- ) : (
- <Message style={[t.atoms.text]} width={iconWidth} />
- )
- }
- label={_(msg`Chat`)}
- bold={isActive}
- onPress={onPress}
- />
- )
-}
-ChatMenuItem = React.memo(ChatMenuItem)
-
let NotificationsMenuItem = ({
isActive,
onPress,
@@ -478,7 +302,7 @@ let NotificationsMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
const numUnreadNotifications = useUnreadNotifications()
return (
@@ -495,11 +319,11 @@ let NotificationsMenuItem = ({
numUnreadNotifications === ''
? ''
: _(
- msg`${plural(numUnreadNotifications ?? 0, {
- one: '# unread item',
- other: '# unread items',
- })}` || '',
- )
+ msg`${plural(numUnreadNotifications ?? 0, {
+ one: '# unread item',
+ other: '# unread items',
+ })}` || '',
+ )
}
count={numUnreadNotifications}
bold={isActive}
@@ -509,72 +333,6 @@ let NotificationsMenuItem = ({
}
NotificationsMenuItem = React.memo(NotificationsMenuItem)
-let FeedsMenuItem = ({
- isActive,
- onPress,
-}: {
- isActive: boolean
- onPress: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
- return (
- <MenuItem
- icon={
- isActive ? (
- <HashtagFilled width={iconWidth} style={[t.atoms.text]} />
- ) : (
- <Hashtag width={iconWidth} style={[t.atoms.text]} />
- )
- }
- label={_(msg`Feeds`)}
- bold={isActive}
- onPress={onPress}
- />
- )
-}
-FeedsMenuItem = React.memo(FeedsMenuItem)
-
-let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
-
- return (
- <MenuItem
- icon={<List style={[t.atoms.text]} width={iconWidth} />}
- label={_(msg`Lists`)}
- onPress={onPress}
- />
- )
-}
-ListsMenuItem = React.memo(ListsMenuItem)
-
-let BookmarksMenuItem = ({
- isActive,
- onPress,
-}: {
- isActive: boolean
- onPress: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
-
- return (
- <MenuItem
- icon={
- isActive ? (
- <BookmarkFilled style={[t.atoms.text]} width={iconWidth} />
- ) : (
- <Bookmark style={[t.atoms.text]} width={iconWidth} />
- )
- }
- label={_(msg({message: 'Saved', context: 'link to bookmarks screen'}))}
- onPress={onPress}
- />
- )
-}
-BookmarksMenuItem = React.memo(BookmarksMenuItem)
-
let ProfileMenuItem = ({
isActive,
onPress,
@@ -582,7 +340,7 @@ let ProfileMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -600,8 +358,8 @@ let ProfileMenuItem = ({
}
ProfileMenuItem = React.memo(ProfileMenuItem)
-let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
- const {_} = useLingui()
+let SettingsMenuItem = ({ onPress }: { onPress: () => void }): React.ReactNode => {
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -613,7 +371,7 @@ let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
}
SettingsMenuItem = React.memo(SettingsMenuItem)
-function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
+function MenuItem({ icon, label, count, bold, onPress }: MenuItemProps) {
const t = useTheme()
return (
<Button
@@ -621,7 +379,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
onPress={onPress}
accessibilityRole="tab"
label={label}>
- {({hovered, pressed}) => (
+ {({ hovered, pressed }) => (
<View
style={[
a.flex_1,
@@ -640,7 +398,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
a.absolute,
a.inset_0,
a.align_end,
- {top: -4, right: a.gap_sm.gap * -1},
+ { top: -4, right: a.gap_sm.gap * -1 },
]}>
<View
style={[
@@ -686,37 +444,28 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
}
function ExtraLinks() {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
const kawaii = useKawaiiMode()
+ const navigation = useNavigation<NavigationProp>()
return (
<View style={[a.flex_col, a.gap_md, a.flex_wrap]}>
- <InlineLinkText
- style={[a.text_md]}
- label={_(msg`Terms of Service`)}
- to="https://bsky.social/about/support/tos">
- <Trans>Terms of Service</Trans>
- </InlineLinkText>
- <InlineLinkText
- style={[a.text_md]}
- to="https://bsky.social/about/support/privacy-policy"
- label={_(msg`Privacy Policy`)}>
- <Trans>Privacy Policy</Trans>
- </InlineLinkText>
- {kawaii && (
- <Text style={t.atoms.text_contrast_medium}>
- <Trans>
- Logo by{' '}
- <InlineLinkText
- style={[a.text_md]}
- to="/profile/sawaratsuki.bsky.social"
- label="@sawaratsuki.bsky.social">
- @sawaratsuki.bsky.social
- </InlineLinkText>
- </Trans>
+ <TouchableOpacity onPress={() => navigation.navigate('TermsOfService')}>
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
+ <Trans>Terms of Service</Trans>
</Text>
- )}
+ </TouchableOpacity>
+ <TouchableOpacity onPress={() => navigation.navigate('PrivacyPolicy')}>
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
+ <Trans>Privacy Policy</Trans>
+ </Text>
+ </TouchableOpacity>
+ <TouchableOpacity onPress={() => navigation.navigate('License')}>
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
+ <Trans>License</Trans>
+ </Text>
+ </TouchableOpacity>
</View>
)
}

View File

@@ -1,31 +1,139 @@
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 6b8257b91..bed851753 100644
index 48ba7909e..68dbfc8ec 100644
--- a/src/screens/Settings/AboutSettings.tsx
+++ b/src/screens/Settings/AboutSettings.tsx
@@ -1,40 +1,40 @@
-import {useMemo} from 'react'
-import {Platform} from 'react-native'
-import {setStringAsync} from 'expo-clipboard'
+import { useMemo } from 'react'
+import { Platform } from 'react-native'
+import { setStringAsync } from 'expo-clipboard'
import * as FileSystem from 'expo-file-system/legacy'
-import {Image} from 'expo-image'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {type NativeStackScreenProps} from '@react-navigation/native-stack'
-import {useMutation} from '@tanstack/react-query'
-import {Statsig} from 'statsig-react-native-expo'
+import { Image } from 'expo-image'
+import { msg, Trans } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import { type NativeStackScreenProps } from '@react-navigation/native-stack'
+import { useMutation } from '@tanstack/react-query'
+import { Statsig } from 'statsig-react-native-expo'
-import {STATUS_PAGE_URL} from '#/lib/constants'
-import {type CommonNavigatorParams} from '#/lib/routes/types'
-import {isAndroid, isIOS, isNative} from '#/platform/detection'
+import { STATUS_PAGE_URL } from '#/lib/constants'
+import { type CommonNavigatorParams } from '#/lib/routes/types'
+import { isAndroid, isIOS, isNative } from '#/platform/detection'
import * as Toast from '#/view/com/util/Toast'
import * as SettingsList from '#/screens/Settings/components/SettingsList'
-import {Atom_Stroke2_Corner0_Rounded as AtomIcon} from '#/components/icons/Atom'
-import {BroomSparkle_Stroke2_Corner2_Rounded as BroomSparkleIcon} from '#/components/icons/BroomSparkle'
-import {CodeLines_Stroke2_Corner2_Rounded as CodeLinesIcon} from '#/components/icons/CodeLines'
-import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe'
-import {Newspaper_Stroke2_Corner2_Rounded as NewspaperIcon} from '#/components/icons/Newspaper'
-import {Wrench_Stroke2_Corner2_Rounded as WrenchIcon} from '#/components/icons/Wrench'
+import { Atom_Stroke2_Corner0_Rounded as AtomIcon } from '#/components/icons/Atom'
+import { BroomSparkle_Stroke2_Corner2_Rounded as BroomSparkleIcon } from '#/components/icons/BroomSparkle'
+import { CodeLines_Stroke2_Corner2_Rounded as CodeLinesIcon } from '#/components/icons/CodeLines'
+import { Globe_Stroke2_Corner0_Rounded as GlobeIcon } from '#/components/icons/Globe'
+import { Newspaper_Stroke2_Corner2_Rounded as NewspaperIcon } from '#/components/icons/Newspaper'
+import { Wrench_Stroke2_Corner2_Rounded as WrenchIcon } from '#/components/icons/Wrench'
import * as Layout from '#/components/Layout'
-import {Loader} from '#/components/Loader'
+import { Loader } from '#/components/Loader'
import * as env from '#/env'
-import {useDemoMode} from '#/storage/hooks/demo-mode'
-import {useDevMode} from '#/storage/hooks/dev-mode'
-import {OTAInfo} from './components/OTAInfo'
+import { useDemoMode } from '#/storage/hooks/demo-mode'
+import { useDevMode } from '#/storage/hooks/dev-mode'
+import { OTAInfo } from './components/OTAInfo'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AboutSettings'>
-export function AboutSettingsScreen({}: Props) {
- const {_, i18n} = useLingui()
+export function AboutSettingsScreen({ }: Props) {
+ const { _, i18n } = useLingui()
const [devModeEnabled, setDevModeEnabled] = useDevMode()
const [demoModeEnabled, setDemoModeEnabled] = useDemoMode()
const stableID = useMemo(() => Statsig.getStableID(), [])
- const {mutate: onClearImageCache, isPending: isClearingImageCache} =
+ const { mutate: onClearImageCache, isPending: isClearingImageCache } =
useMutation({
mutationFn: async () => {
const freeSpaceBefore = await FileSystem.getFreeDiskStorageAsync()
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
<Layout.Content>
<SettingsList.Container>
<SettingsList.LinkItem
- to="https://bsky.social/about/support/tos"
+ to="/about/support/tos"
- to="https://syu.is/about/support/tos"
+ to="/support/tos"
label={_(msg`Terms of Service`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText>
@@ -88,7 +88,7 @@ export function AboutSettingsScreen({}: Props) {
@@ -88,13 +88,21 @@ export function AboutSettingsScreen({}: Props) {
</SettingsList.ItemText>
</SettingsList.LinkItem>
<SettingsList.LinkItem
- to="https://bsky.social/about/support/privacy-policy"
+ to="/about/support/privacy-policy"
- to="https://syu.is/about/support/privacy-policy"
+ to="/support/privacy"
label={_(msg`Privacy Policy`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText>
@@ -96,7 +96,7 @@ export function AboutSettingsScreen({}: Props) {
<Trans>Privacy Policy</Trans>
</SettingsList.ItemText>
</SettingsList.LinkItem>
+ <SettingsList.LinkItem
+ to="/support/license"
+ label={_(msg`License`)}>
+ <SettingsList.ItemIcon icon={NewspaperIcon} />
+ <SettingsList.ItemText>
+ <Trans>License</Trans>
+ </SettingsList.ItemText>
+ </SettingsList.LinkItem>
<SettingsList.LinkItem
- to={STATUS_PAGE_URL}
+ to="https://syu.is/"
to={STATUS_PAGE_URL}
label={_(msg`Status Page`)}>
<SettingsList.ItemIcon icon={GlobeIcon} />
<SettingsList.ItemText>
@@ -131,17 +139,17 @@ export function AboutSettingsScreen({}: Props) {
Toast.show(
newDevModeEnabled
? _(
- msg({
- message: 'Developer mode enabled',
- context: 'toast',
- }),
- )
+ msg({
+ message: 'Developer mode enabled',
+ context: 'toast',
+ }),
+ )
: _(
- msg({
- message: 'Developer mode disabled',
- context: 'toast',
- }),
- ),
+ msg({
+ message: 'Developer mode disabled',
+ context: 'toast',
+ }),
+ ),
)
}}
onPress={() => {
@@ -166,7 +174,7 @@ export function AboutSettingsScreen({}: Props) {
setDemoModeEnabled(newDemoModeEnabled)
Toast.show(
'Demo mode ' +
- (newDemoModeEnabled ? 'enabled' : 'disabled'),
+ (newDemoModeEnabled ? 'enabled' : 'disabled'),
)
}}
label={

View File

@@ -1,32 +1,33 @@
--- a/b/src/ageAssurance/index.tsx 2025-12-07 15:18:15
+++ b/src/ageAssurance/index.tsx 2025-12-07 15:18:16
@@ -1,10 +1,10 @@
diff --git a/src/ageAssurance/index.tsx b/src/ageAssurance/index.tsx
index 9a0a9c9d5..7c25a2291 100644
--- a/src/ageAssurance/index.tsx
+++ b/src/ageAssurance/index.tsx
@@ -1,20 +1,13 @@
-import {createContext, useCallback, useContext, useEffect, useMemo} from 'react'
+import { createContext, useCallback, useContext, useEffect, useMemo } from 'react'
+import { createContext, useContext, useMemo } from 'react'
-import {useGetAndRegisterPushToken} from '#/lib/notifications/notifications'
-import {Provider as RedirectOverlayProvider} from '#/ageAssurance/components/RedirectOverlay'
-import {AgeAssuranceDataProvider} from '#/ageAssurance/data'
-import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
-import {logger} from '#/ageAssurance/logger'
+import { useGetAndRegisterPushToken } from '#/lib/notifications/notifications'
-import {
- useAgeAssuranceState,
- useOnAgeAssuranceAccessUpdate,
-} from '#/ageAssurance/state'
+import { Provider as RedirectOverlayProvider } from '#/ageAssurance/components/RedirectOverlay'
+import { AgeAssuranceDataProvider } from '#/ageAssurance/data'
+import { useAgeAssuranceDataContext } from '#/ageAssurance/data'
+import { logger } from '#/ageAssurance/logger'
import {
useAgeAssuranceState,
useOnAgeAssuranceAccessUpdate,
@@ -14,7 +14,7 @@
AgeAssuranceAccess,
type AgeAssuranceState,
AgeAssuranceStatus,
} from '#/ageAssurance/types'
-import {isUserUnderAdultAge} from '#/ageAssurance/util'
+import { isUserUnderAdultAge } from '#/ageAssurance/util'
export {
prefetchConfig as prefetchAgeAssuranceConfig,
@@ -23,7 +23,7 @@
@@ -23,7 +16,7 @@ export {
usePatchOtherRequiredData as usePatchAgeAssuranceOtherRequiredData,
usePatchServerState as usePatchAgeAssuranceServerState,
} from '#/ageAssurance/data'
@@ -35,7 +36,7 @@
const AgeAssuranceStateContext = createContext<{
Access: typeof AgeAssuranceAccess
@@ -56,7 +56,7 @@
@@ -56,7 +49,7 @@ export function useAgeAssurance() {
return useContext(AgeAssuranceStateContext)
}
@@ -44,52 +45,69 @@
return (
<AgeAssuranceDataProvider>
<InnerProvider>
@@ -66,9 +66,9 @@
@@ -66,44 +59,31 @@ export function Provider({children}: {children: React.ReactNode}) {
)
}
-function InnerProvider({children}: {children: React.ReactNode}) {
+function InnerProvider({ children }: { children: React.ReactNode }) {
const state = useAgeAssuranceState()
- const state = useAgeAssuranceState()
- const {data} = useAgeAssuranceDataContext()
+ const { data } = useAgeAssuranceDataContext()
const getAndRegisterPushToken = useGetAndRegisterPushToken()
- const getAndRegisterPushToken = useGetAndRegisterPushToken()
-
- const handleAccessUpdate = useCallback(
- (s: AgeAssuranceState) => {
- getAndRegisterPushToken({
- isAgeRestricted: s.access !== AgeAssuranceAccess.Full,
- })
- },
- [getAndRegisterPushToken],
+function InnerProvider({ children }: { children: React.ReactNode }) {
+ // Completely ignore internal state and force Full access
+ const flags = useMemo(
+ () => ({
+ adultContentDisabled: false,
+ chatDisabled: false,
+ }),
+ [],
)
- useOnAgeAssuranceAccessUpdate(handleAccessUpdate)
const handleAccessUpdate = useCallback(
@@ -82,28 +82,25 @@
useOnAgeAssuranceAccessUpdate(handleAccessUpdate)
useEffect(() => {
- useEffect(() => {
- logger.debug(`useAgeAssuranceState`, {state})
+ logger.debug(`useAgeAssuranceState`, { state })
}, [state])
- }, [state])
+ const contextValue = useMemo(() => {
+ return {
+ Access: AgeAssuranceAccess,
+ Status: AgeAssuranceStatus,
+ state: {
+ lastInitiatedAt: undefined,
+ status: AgeAssuranceStatus.Unknown,
+ access: AgeAssuranceAccess.Full,
+ },
+ flags,
+ }
+ }, [flags])
return (
<AgeAssuranceStateContext.Provider
value={useMemo(() => {
- <AgeAssuranceStateContext.Provider
- value={useMemo(() => {
- const chatDisabled = state.access !== AgeAssuranceAccess.Full
- const isUnderage = data?.birthdate
- ? isUserUnderAdultAge(data.birthdate)
- : true
- const adultContentDisabled =
- state.access !== AgeAssuranceAccess.Full || isUnderage
return {
Access: AgeAssuranceAccess,
Status: AgeAssuranceStatus,
- return {
- Access: AgeAssuranceAccess,
- Status: AgeAssuranceStatus,
- state,
+ state: {
+ ...state,
+ access: AgeAssuranceAccess.Full,
+ },
flags: {
- flags: {
- adultContentDisabled,
- chatDisabled,
+ adultContentDisabled: false,
+ chatDisabled: false,
},
}
- },
- }
- }, [state, data])}>
+ }, [state])}>
+ <AgeAssuranceStateContext.Provider value={contextValue}>
{children}
</AgeAssuranceStateContext.Provider>
)

View File

@@ -0,0 +1,155 @@
diff --git a/src/view/com/posts/FollowingEmptyState.tsx b/src/view/com/posts/FollowingEmptyState.tsx
index 352cc1dc0..a1bae1b05 100644
--- a/src/view/com/posts/FollowingEmptyState.tsx
+++ b/src/view/com/posts/FollowingEmptyState.tsx
@@ -1,19 +1,19 @@
import React from 'react'
-import {StyleSheet, View} from 'react-native'
+import { StyleSheet, View } from 'react-native'
import {
FontAwesomeIcon,
type FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
-import {Trans} from '@lingui/macro'
-import {useNavigation} from '@react-navigation/native'
+import { Trans } from '@lingui/macro'
+import { useNavigation } from '@react-navigation/native'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {MagnifyingGlassIcon} from '#/lib/icons'
-import {type NavigationProp} from '#/lib/routes/types'
-import {s} from '#/lib/styles'
-import {isWeb} from '#/platform/detection'
-import {Button} from '../util/forms/Button'
-import {Text} from '../util/text/Text'
+import { usePalette } from '#/lib/hooks/usePalette'
+import { MagnifyingGlassIcon } from '#/lib/icons'
+import { type NavigationProp } from '#/lib/routes/types'
+import { s } from '#/lib/styles'
+import { isWeb } from '#/platform/detection'
+import { Button } from '../util/forms/Button'
+import { Text } from '../util/text/Text'
export function FollowingEmptyState() {
const pal = usePalette('default')
@@ -45,36 +45,6 @@ export function FollowingEmptyState() {
happening.
</Trans>
</Text>
- <Button
- type="inverted"
- style={styles.emptyBtn}
- onPress={onPressFindAccounts}>
- <Text type="lg-medium" style={palInverted.text}>
- <Trans>Find accounts to follow</Trans>
- </Text>
- <FontAwesomeIcon
- icon="angle-right"
- style={palInverted.text as FontAwesomeIconStyle}
- size={14}
- />
- </Button>
-
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
- </Text>
- <Button
- type="inverted"
- style={[styles.emptyBtn, s.mt10]}
- onPress={onPressDiscoverFeeds}>
- <Text type="lg-medium" style={palInverted.text}>
- <Trans>Discover new custom feeds</Trans>
- </Text>
- <FontAwesomeIcon
- icon="angle-right"
- style={palInverted.text as FontAwesomeIconStyle}
- size={14}
- />
- </Button>
</View>
</View>
)
diff --git a/src/view/com/posts/FollowingEndOfFeed.tsx b/src/view/com/posts/FollowingEndOfFeed.tsx
index e3c84d782..86de5f747 100644
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
@@ -1,18 +1,18 @@
import React from 'react'
-import {Dimensions, StyleSheet, View} from 'react-native'
+import { Dimensions, StyleSheet, View } from 'react-native'
import {
FontAwesomeIcon,
type FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
-import {Trans} from '@lingui/macro'
-import {useNavigation} from '@react-navigation/native'
+import { Trans } from '@lingui/macro'
+import { useNavigation } from '@react-navigation/native'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {type NavigationProp} from '#/lib/routes/types'
-import {s} from '#/lib/styles'
-import {isWeb} from '#/platform/detection'
-import {Button} from '../util/forms/Button'
-import {Text} from '../util/text/Text'
+import { usePalette } from '#/lib/hooks/usePalette'
+import { type NavigationProp } from '#/lib/routes/types'
+import { s } from '#/lib/styles'
+import { isWeb } from '#/platform/detection'
+import { Button } from '../util/forms/Button'
+import { Text } from '../util/text/Text'
export function FollowingEndOfFeed() {
const pal = usePalette('default')
@@ -33,49 +33,9 @@ export function FollowingEndOfFeed() {
}, [navigation])
return (
- <View
- style={[
- styles.container,
- pal.border,
- {minHeight: Dimensions.get('window').height * 0.75},
- ]}>
+ <View style={[styles.container, pal.border]}>
<View style={styles.inner}>
- <Text type="xl-medium" style={[s.textCenter, pal.text]}>
- <Trans>
- You've reached the end of your feed! Find some more accounts to
- follow.
- </Trans>
- </Text>
- <Button
- type="inverted"
- style={styles.emptyBtn}
- onPress={onPressFindAccounts}>
- <Text type="lg-medium" style={palInverted.text}>
- <Trans>Find accounts to follow</Trans>
- </Text>
- <FontAwesomeIcon
- icon="angle-right"
- style={palInverted.text as FontAwesomeIconStyle}
- size={14}
- />
- </Button>
-
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
- </Text>
- <Button
- type="inverted"
- style={[styles.emptyBtn, s.mt10]}
- onPress={onPressDiscoverFeeds}>
- <Text type="lg-medium" style={palInverted.text}>
- <Trans>Discover new custom feeds</Trans>
- </Text>
- <FontAwesomeIcon
- icon="angle-right"
- style={palInverted.text as FontAwesomeIconStyle}
- size={14}
- />
- </Button>
+ {/* User requested strict cleanup: Remove all Discover/Find Accounts buttons */}
</View>
</View>
)

View File

@@ -26,6 +26,7 @@ PATCH_FILES_IOS=(
"013-social-app-ios-settings-remove-help.patch"
"019-social-app-ios-entitlements-plugin.patch"
"020-social-app-ios-bypass-age-assurance.patch"
"021-social-app-ios-clean-feed.patch"
)
function ios-env() {