ai/at
1
0

add ios social-app

This commit is contained in:
2025-12-06 21:14:08 +09:00
parent 948551c185
commit 7c225b22fc
92 changed files with 5081 additions and 675 deletions

View File

@@ -0,0 +1,166 @@
diff --git a/app.config.js b/app.config.js
index 246d8abd3..ed8f7b2b2 100644
--- a/app.config.js
+++ b/app.config.js
@@ -18,10 +18,7 @@ module.exports = function (_config) {
const IS_DEV = !IS_TESTFLIGHT || !IS_PRODUCTION
const ASSOCIATED_DOMAINS = [
- 'applinks:bsky.app',
- 'applinks:staging.bsky.app',
- 'appclips:bsky.app',
- 'appclips:go.bsky.app', // Allows App Clip to work when scanning QR codes
+ 'applinks:syu.is',
// When testing local services, enter an ngrok (et al) domain here. It must use a standard HTTP/HTTPS port.
...(IS_DEV || IS_TESTFLIGHT ? [] : []),
]
@@ -33,27 +30,25 @@ module.exports = function (_config) {
return {
expo: {
version: VERSION,
- name: 'Bluesky',
- slug: 'bluesky',
- scheme: 'bluesky',
+ name: 'Aiat',
+ slug: 'aiat',
+ scheme: 'syui',
owner: 'blueskysocial',
runtimeVersion: {
policy: 'appVersion',
},
- icon: './assets/app-icons/ios_icon_default_next.png',
+ icon: './assets/logo.png',
userInterfaceStyle: 'automatic',
primaryColor: '#1083fe',
newArchEnabled: false,
ios: {
supportsTablet: false,
- bundleIdentifier: 'xyz.blueskyweb.app',
+ bundleIdentifier: 'ai.syui.at',
+ buildNumber: '__BUILD_NUMBER__',
config: {
usesNonExemptEncryption: false,
},
- icon:
- PLATFORM === 'web' // web build doesn't like .icon files
- ? './assets/app-icons/ios_icon_default_next.png'
- : './assets/app-icons/ios_icon_default.icon',
+ icon: './assets/logo.png',
infoPlist: {
UIBackgroundModes: ['remote-notification'],
NSCameraUsageDescription:
@@ -113,7 +107,7 @@ module.exports = function (_config) {
entitlements: {
'com.apple.developer.kernel.increased-memory-limit': true,
'com.apple.developer.kernel.extended-virtual-addressing': true,
- 'com.apple.security.application-groups': 'group.app.bsky',
+ 'com.apple.security.application-groups': 'group.ai.syui.at',
},
privacyManifests: {
NSPrivacyCollectedDataTypes: [
@@ -175,14 +169,14 @@ module.exports = function (_config) {
barStyle: 'light-content',
},
android: {
- icon: './assets/app-icons/android_icon_default_next.png',
+ icon: './assets/logo.png',
adaptiveIcon: {
foregroundImage: './assets/icon-android-foreground.png',
monochromeImage: './assets/icon-android-monochrome.png',
backgroundColor: '#006AFF',
},
googleServicesFile: './google-services.json',
- package: 'xyz.blueskyweb.app',
+ package: 'ai.syui.at',
intentFilters: [
{
action: 'VIEW',
@@ -190,7 +184,7 @@ module.exports = function (_config) {
data: [
{
scheme: 'https',
- host: 'bsky.app',
+ host: 'syu.is',
},
IS_DEV && {
scheme: 'http',
@@ -213,9 +207,9 @@ module.exports = function (_config) {
: undefined,
codeSigningMetadata: UPDATES_ENABLED
? {
- keyid: 'main',
- alg: 'rsa-v1_5-sha256',
- }
+ keyid: 'main',
+ alg: 'rsa-v1_5-sha256',
+ }
: undefined,
checkAutomatically: 'NEVER',
},
@@ -225,7 +219,7 @@ module.exports = function (_config) {
'expo-web-browser',
[
'react-native-edge-to-edge',
- {android: {enforceNavigationBarContrast: false}},
+ { android: { enforceNavigationBarContrast: false } },
],
USE_SENTRY && [
'@sentry/react-native/expo',
@@ -264,7 +258,6 @@ module.exports = function (_config) {
networkInstrumentation: true,
},
],
- './plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
'./plugins/withGradleJVMHeapSizeIncrease.js',
'./plugins/withAndroidManifestLargeHeapPlugin.js',
'./plugins/withAndroidManifestFCMIconPlugin.js',
@@ -272,8 +265,6 @@ module.exports = function (_config) {
'./plugins/withAndroidStylesAccentColorPlugin.js',
'./plugins/withAndroidDayNightThemePlugin.js',
'./plugins/withAndroidNoJitpackPlugin.js',
- './plugins/shareExtension/withShareExtensions.js',
- './plugins/notificationsExtension/withNotificationsExtension.js',
[
'expo-font',
{
@@ -386,7 +377,7 @@ module.exports = function (_config) {
},
},
],
- ['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}],
+ ['expo-screen-orientation', { initialOrientation: 'PORTRAIT_UP' }],
['expo-location'],
].filter(Boolean),
extra: {
@@ -394,30 +385,7 @@ module.exports = function (_config) {
build: {
experimental: {
ios: {
- appExtensions: [
- {
- targetName: 'Share-with-Bluesky',
- bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
- entitlements: {
- 'com.apple.security.application-groups': [
- 'group.app.bsky',
- ],
- },
- },
- {
- targetName: 'BlueskyNSE',
- bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE',
- entitlements: {
- 'com.apple.security.application-groups': [
- 'group.app.bsky',
- ],
- },
- },
- {
- targetName: 'BlueskyClip',
- bundleIdentifier: 'xyz.blueskyweb.app.AppClip',
- },
- ],
+ appExtensions: [],
},
},
},

View File

@@ -0,0 +1,217 @@
diff --git a/src/lib/api/feed/home.ts b/src/lib/api/feed/home.ts
index 7a0d72d91..93554dc3e 100644
--- a/src/lib/api/feed/home.ts
+++ b/src/lib/api/feed/home.ts
@@ -45,7 +45,7 @@ export class HomeFeedAPI implements FeedAPI {
this.following = new FollowingFeedAPI({agent})
this.discover = new CustomFeedAPI({
agent,
- feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
+ feedParams: {feed: PROD_DEFAULT_FEED('app')},
})
this.userInterests = userInterests
}
@@ -54,7 +54,7 @@ export class HomeFeedAPI implements FeedAPI {
this.following = new FollowingFeedAPI({agent: this.agent})
this.discover = new CustomFeedAPI({
agent: this.agent,
- feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
+ feedParams: {feed: PROD_DEFAULT_FEED('app')},
userInterests: this.userInterests,
})
this.usingDiscover = false
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 231447b4f..a44b3da05 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -7,12 +7,12 @@ import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
export const LOCAL_DEV_SERVICE =
Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583'
export const STAGING_SERVICE = 'https://staging.bsky.dev'
-export const BSKY_SERVICE = 'https://bsky.social'
-export const BSKY_SERVICE_DID = 'did:web:bsky.social'
-export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'
+export const BSKY_SERVICE = 'https://syu.is'
+export const BSKY_SERVICE_DID = 'did:web:syu.is'
+export const PUBLIC_BSKY_SERVICE = 'https://bsky.syu.is'
export const DEFAULT_SERVICE = BSKY_SERVICE
-const HELP_DESK_LANG = 'en-us'
-export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
+const HELP_DESK_LANG = 'ja-jp'
+export const HELP_DESK_URL = 'https://syu.is/about/support/help'
export const EMBED_SERVICE = 'https://embed.bsky.app'
export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download'
@@ -79,19 +79,17 @@ export function IS_PROD_SERVICE(url?: string) {
}
export const PROD_DEFAULT_FEED = (rkey: string) =>
- `at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/${rkey}`
+ `at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/${rkey}`
export const STAGING_DEFAULT_FEED = (rkey: string) =>
`at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/${rkey}`
export const PROD_FEEDS = [
- `feedgen|${PROD_DEFAULT_FEED('whats-hot')}`,
- `feedgen|${PROD_DEFAULT_FEED('thevids')}`,
+ `feedgen|${PROD_DEFAULT_FEED('app')}`,
]
export const STAGING_FEEDS = [
- `feedgen|${STAGING_DEFAULT_FEED('whats-hot')}`,
- `feedgen|${STAGING_DEFAULT_FEED('thevids')}`,
+ `feedgen|${STAGING_DEFAULT_FEED('app')}`,
]
export const POST_IMG_MAX = {
@@ -129,7 +127,7 @@ export const LANG_DROPDOWN_HITSLOP = {top: 10, bottom: 10, left: 4, right: 4}
export const BACK_HITSLOP = HITSLOP_30
export const MAX_POST_LINES = 25
-export const BSKY_APP_ACCOUNT_DID = 'did:plc:z72i7hdynmk6r22z27h6tvur'
+export const BSKY_APP_ACCOUNT_DID = 'did:plc:6qyecktefllvenje24fcxnie'
export const BSKY_FEED_OWNER_DIDS = [
BSKY_APP_ACCOUNT_DID,
@@ -138,9 +136,9 @@ export const BSKY_FEED_OWNER_DIDS = [
]
export const DISCOVER_FEED_URI =
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot'
+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
export const VIDEO_FEED_URI =
'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids'
export const STAGING_VIDEO_FEED_URI =
'at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/thevids'
export const VIDEO_FEED_URIS = [VIDEO_FEED_URI, STAGING_VIDEO_FEED_URI]
@@ -209,8 +207,8 @@ export const urls = {
},
}
-export const PUBLIC_APPVIEW = 'https://api.bsky.app'
-export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'
+export const PUBLIC_APPVIEW = 'https://bsky.syu.is'
+export const PUBLIC_APPVIEW_DID = 'did:web:bsky.syu.is'
export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
@@ -236,8 +234,8 @@ export const BLUESKY_MOD_SERVICE_HEADERS = {
}
export const webLinks = {
- tos: `https://bsky.social/about/support/tos`,
- privacy: `https://bsky.social/about/support/privacy-policy`,
+ tos: `https://syu.is/about/support/tos`,
+ privacy: `https://syu.is/about/support/privacy-policy`,
community: `https://bsky.social/about/support/community-guidelines`,
communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`,
}
diff --git a/src/lib/demo.ts b/src/lib/demo.ts
index 5ead62c9d..7c80dfe15 100644
--- a/src/lib/demo.ts
+++ b/src/lib/demo.ts
@@ -1,7 +1,7 @@
import {type AppBskyFeedGetFeed} from '@atproto/api'
import {subDays, subMinutes} from 'date-fns'
-const DID = `did:plc:z72i7hdynmk6r22z27h6tvur`
+const DID = `did:plc:6qyecktefllvenje24fcxnie`
const NOW = new Date()
const POST_1_DATE = subMinutes(NOW, 2).toISOString()
const POST_2_DATE = subMinutes(NOW, 4).toISOString()
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
index 6088e2806..0f6787a4d 100644
--- a/src/lib/strings/url-helpers.ts
+++ b/src/lib/strings/url-helpers.ts
@@ -53,7 +53,7 @@ export function toNiceDomain(url: string): string {
try {
const urlp = new URL(url)
if (`https://${urlp.host}` === BSKY_SERVICE) {
- return 'Bluesky Social'
+ return 'syu.is'
}
return urlp.host ? urlp.host : url
} catch (e) {
@@ -338,7 +338,7 @@ export function createProxiedUrl(url: string): string {
return url
}
- return `https://go.bsky.app/redirect?u=${encodeURIComponent(url)}`
+ return url
}
export function isShortLink(url: string): boolean {
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
index de1e92533..3d1566800 100644
--- a/src/state/queries/feed.ts
+++ b/src/state/queries/feed.ts
@@ -201,14 +201,6 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
// for the ones we know need it
// -prf
export const KNOWN_AUTHED_ONLY_FEEDS = [
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app
- 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed
- 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed
- 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow
- 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky
- 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz
- 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why
]
type GetPopularFeedsOptions = {limit?: number; enabled?: boolean}
diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts
index 0cf6ab546..399e592bc 100644
--- a/src/state/queries/preferences/index.ts
+++ b/src/state/queries/preferences/index.ts
@@ -270,7 +270,7 @@ export function useReplaceForYouWithDiscoverFeedMutation() {
await agent.addSavedFeeds([
{
type: 'feed',
- value: PROD_DEFAULT_FEED('whats-hot'),
+ value: PROD_DEFAULT_FEED('app'),
pinned: true,
},
])
diff --git a/src/view/com/posts/FeedShutdownMsg.tsx b/src/view/com/posts/FeedShutdownMsg.tsx
index 620382175..928480da2 100644
--- a/src/view/com/posts/FeedShutdownMsg.tsx
+++ b/src/view/com/posts/FeedShutdownMsg.tsx
@@ -32,7 +32,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
f => f.value === feedUri && f.pinned,
)
const discoverFeedConfig = preferences?.savedFeeds?.find(
- f => f.value === PROD_DEFAULT_FEED('whats-hot'),
+ f => f.value === PROD_DEFAULT_FEED('app'),
)
const hasFeedPinned = Boolean(feedConfig)
const hasDiscoverPinned = Boolean(discoverFeedConfig?.pinned)
@@ -44,7 +44,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
Toast.show(_(msg`Removed from your feeds`))
}
if (hasDiscoverPinned) {
- setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
+ setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('app')}`)
}
} catch (err: any) {
Toast.show(
@@ -63,7 +63,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
forYouFeedConfig: feedConfig,
discoverFeedConfig,
})
- setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
+ setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('app')}`)
Toast.show(_(msg`The feed has been replaced with Discover.`))
} catch (err: any) {
Toast.show(
@@ -100,7 +100,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
This feed is no longer online. We are showing{' '}
<InlineLinkText
label={_(msg`The Discover feed`)}
- to="/profile/bsky.app/feed/whats-hot"
+ to="/profile/did:plc:6qyecktefllvenje24fcxnie/feed/app"
style={[a.text_md]}>
Discover
</InlineLinkText>{' '}

View File

@@ -0,0 +1,213 @@
diff --git a/src/Splash.tsx b/src/Splash.tsx
index 47e70b375..616f351ed 100644
--- a/src/Splash.tsx
+++ b/src/Splash.tsx
@@ -15,8 +15,8 @@ import Animated, {
withTiming,
} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import Svg, {Path, type SvgProps} from 'react-native-svg'
import {Image} from 'expo-image'
+import {type SvgProps} from 'react-native-svg'
import * as SplashScreen from 'expo-splash-screen'
import {Logotype} from '#/view/icons/Logotype'
@@ -29,21 +29,18 @@ const darkSplashImageUri = RNImage.resolveAssetSource(
darkSplashImagePointer,
).uri
-export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) {
- const width = 1000
- const height = width * (67 / 64)
+export const Logo = React.forwardRef(function LogoImpl(props: SvgProps & {fill?: string}, ref) {
+ const size = 1000
+ // @ts-ignore
return (
- <Svg
- fill="none"
- // @ts-ignore it's fiiiiine
+ <Image
+ // @ts-ignore
ref={ref}
- viewBox="0 0 64 66"
- style={[{width, height}, props.style]}>
- <Path
- fill={props.fill || '#fff'}
- d="M13.873 3.77C21.21 9.243 29.103 20.342 32 26.3v15.732c0-.335-.13.043-.41.858-1.512 4.414-7.418 21.642-20.923 7.87-7.111-7.252-3.819-14.503 9.125-16.692-7.405 1.252-15.73-.817-18.014-8.93C1.12 22.804 0 8.431 0 6.488 0-3.237 8.579-.18 13.873 3.77ZM50.127 3.77C42.79 9.243 34.897 20.342 32 26.3v15.732c0-.335.13.043.41.858 1.512 4.414 7.418 21.642 20.923 7.87 7.111-7.252 3.819-14.503-9.125-16.692 7.405 1.252 15.73-.817 18.014-8.93C62.88 22.804 64 8.431 64 6.488 64-3.237 55.422-.18 50.127 3.77Z"
- />
- </Svg>
+ source={require('../assets/logo.png')}
+ style={[{width: size, height: size}, props.style]}
+ contentFit="contain"
+ accessibilityLabel="Logo"
+ />
)
})
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index 8a9e51a33..65d643b89 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -444,7 +444,7 @@ let EditableUserAvatar = ({
<HighPriorityImage
testID="userAvatarImage"
style={aviStyle}
- source={{uri: avatar}}
+ source={{ uri: hackModifyThumbnailPath(avatar, 1 > 0), }}
accessibilityRole="image"
/>
) : (
@@ -618,9 +618,8 @@ export {PreviewableUserAvatar}
// manually string-replace to use the smaller ones
// -prf
function hackModifyThumbnailPath(uri: string, isEnabled: boolean): string {
- return isEnabled
- ? uri.replace('/img/avatar/plain/', '/img/avatar_thumbnail/plain/')
- : uri
+ // syu.is: avatars are served directly from bsky.syu.is, no CDN transformation needed
+ return uri
}
const styles = StyleSheet.create({
diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx
index d7208df13..2763800ac 100644
--- a/src/view/icons/Logo.tsx
+++ b/src/view/icons/Logo.tsx
@@ -1,75 +1,17 @@
import React from 'react'
-import {type TextProps} from 'react-native'
-import Svg, {
- Defs,
- LinearGradient,
- Path,
- type PathProps,
- Stop,
- type SvgProps,
-} from 'react-native-svg'
import {Image} from 'expo-image'
+import {flatten} from '#/alf'
-import {useKawaiiMode} from '#/state/preferences/kawaii'
-import {flatten, useTheme} from '#/alf'
-
-const ratio = 57 / 64
-
-type Props = {
- fill?: PathProps['fill']
- style?: TextProps['style']
-} & Omit<SvgProps, 'style'>
-
-export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
- const t = useTheme()
- const {fill, ...rest} = props
- const gradient = fill === 'sky'
- const styles = flatten(props.style)
- const _fill = gradient
- ? 'url(#sky)'
- : fill || styles?.color || t.palette.primary_500
- // @ts-ignore it's fiiiiine
- const size = parseInt(rest.width || 32, 10)
-
- const isKawaii = useKawaiiMode()
-
- if (isKawaii) {
- return (
- <Image
- source={
- size > 100
- ? require('../../../assets/kawaii.png')
- : require('../../../assets/kawaii_smol.png')
- }
- accessibilityLabel="Bluesky"
- accessibilityHint=""
- accessibilityIgnoresInvertColors
- style={[{height: size, aspectRatio: 1.4}]}
- />
- )
- }
-
+export const Logo = React.forwardRef(function LogoImpl(props: any, ref) {
+ const {width, style} = props
+ // @ts-ignore
+ const size = parseInt(width || 32, 10)
return (
- <Svg
- fill="none"
- // @ts-ignore it's fiiiiine
- ref={ref}
- viewBox="0 0 64 57"
- {...rest}
- style={[{width: size, height: size * ratio}, styles]}>
- {gradient && (
- <Defs>
- <LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
- <Stop offset="0" stopColor="#0A7AFF" stopOpacity="1" />
- <Stop offset="1" stopColor="#59B9FF" stopOpacity="1" />
- </LinearGradient>
- </Defs>
- )}
-
- <Path
- fill={_fill}
- d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z"
- />
- </Svg>
+ <Image
+ source={require('../../../assets/logo.png')}
+ style={[{width: size, height: size}, flatten(style)]}
+ contentFit="contain"
+ accessibilityLabel="Logo"
+ />
)
})
diff --git a/src/view/icons/Logotype.tsx b/src/view/icons/Logotype.tsx
index 270c913fc..a60ffe07c 100644
--- a/src/view/icons/Logotype.tsx
+++ b/src/view/icons/Logotype.tsx
@@ -1,28 +1,22 @@
-import Svg, {Path, type PathProps, type SvgProps} from 'react-native-svg'
-
-import {usePalette} from '#/lib/hooks/usePalette'
-
-const ratio = 17 / 64
-
-export function Logotype({
- fill,
- ...rest
-}: {fill?: PathProps['fill']} & SvgProps) {
- const pal = usePalette('default')
- // @ts-ignore it's fiiiiine
- const size = parseInt(rest.width || 32)
+import React from 'react'
+import {Text} from 'react-native'
+import {useTheme, atoms as a} from '#/alf'
+export function Logotype({width, fill, style}: any) {
+ const t = useTheme()
+ const fontSize = width ? parseInt(width) / 3.5 : 22
+
return (
- <Svg
- fill="none"
- viewBox="0 0 64 17"
- {...rest}
- width={size}
- height={Number(size) * ratio}>
- <Path
- fill={fill || pal.text.color}
- d="M8.478 6.252c1.503.538 2.3 1.78 2.3 3.172 0 2.356-1.576 3.785-4.6 3.785H0V0h5.974c2.875 0 4.267 1.466 4.267 3.413 0 1.3-.594 2.245-1.763 2.839Zm-2.69-4.193H2.504v3.45h3.284c1.28 0 1.967-.667 1.967-1.78 0-1.02-.705-1.67-1.967-1.67Zm-3.284 9.072h3.544c1.41 0 2.17-.65 2.17-1.818 0-1.224-.723-1.837-2.17-1.837H2.504v3.655ZM14.251 13.209h-2.337V0h2.337v13.209ZM22.001 8.998V3.636h2.338v9.573h-2.263v-1.392c-.724 1.076-1.726 1.614-3.006 1.614-2.022 0-3.34-1.224-3.34-3.45V3.636h2.338v5.955c0 1.206.594 1.818 1.8 1.818 1.132 0 2.133-.835 2.133-2.411ZM34.979 8.59v.556h-7.161c.167 1.651 1.076 2.467 2.486 2.467 1.076 0 1.8-.463 2.189-1.372h2.244c-.5 1.947-2.17 3.19-4.452 3.19-1.428 0-2.579-.463-3.45-1.372-.872-.91-1.318-2.115-1.318-3.637 0-1.502.427-2.708 1.299-3.636.872-.909 2.004-1.372 3.432-1.372 1.447 0 2.597.482 3.45 1.428.854.946 1.28 2.208 1.28 3.747Zm-4.75-3.358c-1.28 0-2.17.742-2.393 2.281h4.805c-.204-1.391-1.057-2.281-2.411-2.281ZM40.16 13.469c-2.783 0-4.249-1.095-4.379-3.303h2.282c.13 1.188.724 1.633 2.134 1.633 1.261 0 1.892-.39 1.892-1.15 0-.687-.445-1.02-1.874-1.262l-1.094-.185c-2.097-.353-3.136-1.318-3.136-2.894 0-1.8 1.429-2.894 3.97-2.894 2.728 0 4.138 1.075 4.23 3.246h-2.207c-.056-1.169-.742-1.577-2.023-1.577-1.113 0-1.67.371-1.67 1.113 0 .668.483.965 1.596 1.169l1.206.186c2.32.426 3.32 1.28 3.32 2.912 0 1.93-1.557 3.006-4.247 3.006ZM54.667 13.209h-2.671l-2.783-4.453-1.447 1.447v3.006h-2.3V0h2.3v7.606l3.896-3.97h2.783l-3.618 3.618 3.84 5.955ZM60.772 6.048l.78-2.412H64l-3.692 10.352c-.39 1.057-.872 1.818-1.484 2.245-.612.426-1.484.63-2.634.63-.39 0-.724-.018-1.02-.055V14.97h.89c1.057 0 1.577-.65 1.577-1.54 0-.445-.149-1.094-.446-1.929l-2.746-7.866h2.487l.779 2.393c.575 1.8 1.076 3.58 1.521 5.343.408-1.521.928-3.302 1.54-5.324Z"
- />
- </Svg>
+ <Text style={[
+ a.font_bold,
+ {
+ fontSize,
+ color: fill || t.palette.primary_500,
+ letterSpacing: -0.5
+ },
+ style
+ ]}>
+ Aiat
+ </Text>
)
}

View File

@@ -0,0 +1,72 @@
diff --git a/src/App.native.tsx b/src/App.native.tsx
index fb3008627..539ebc055 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -92,7 +92,7 @@ if (isAndroid) {
* Begin geolocation ASAP
*/
Geo.resolve()
-prefetchAgeAssuranceConfig()
+// // // prefetchAgeAssuranceConfig()
function InnerApp() {
const [isReady, setIsReady] = React.useState(false)
diff --git a/src/routes.ts b/src/routes.ts
index 1ed913bb2..c80340edb 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -71,8 +71,8 @@ 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',
CommunityGuidelines: '/support/community-guidelines',
CopyrightPolicy: '/support/copyright',
// hashtags
diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts
index 5c8ce3b97..ee85beb08 100644
--- a/src/state/session/agent.ts
+++ b/src/state/session/agent.ts
@@ -47,7 +47,8 @@ export function createPublicAgent() {
configureModerationForGuest() // Side effect but only relevant for tests
const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE})
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent
}
@@ -88,7 +89,8 @@ export async function createAgentAndResume(
// after session is attached
const aa = prefetchAgeAssuranceData({agent})
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent.prepare({
resolvers: [gates, moderation, aa],
@@ -127,7 +129,8 @@ export async function createAgentAndLogin(
const moderation = configureModerationForAccount(agent, account)
const aa = prefetchAgeAssuranceData({agent})
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent.prepare({
resolvers: [gates, moderation, aa],
@@ -299,7 +302,8 @@ export async function createAgentAndCreateAccount(
logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`})
}
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent.prepare({
resolvers: [gates, moderation, aa],

View File

@@ -0,0 +1,593 @@
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 6b8257b91..48ba7909e 100644
--- a/src/screens/Settings/AboutSettings.tsx
+++ b/src/screens/Settings/AboutSettings.tsx
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
<Layout.Content>
<SettingsList.Container>
<SettingsList.LinkItem
- to="https://bsky.social/about/support/tos"
+ to="https://syu.is/about/support/tos"
label={_(msg`Terms of Service`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText>
@@ -88,7 +88,7 @@ export function AboutSettingsScreen({}: Props) {
</SettingsList.ItemText>
</SettingsList.LinkItem>
<SettingsList.LinkItem
- to="https://bsky.social/about/support/privacy-policy"
+ to="https://syu.is/about/support/privacy-policy"
label={_(msg`Privacy Policy`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText>
diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx
index 77f219e55..53f5e0cc0 100644
--- a/src/screens/Takendown.tsx
+++ b/src/screens/Takendown.tsx
@@ -217,10 +217,10 @@ export function Takendown() {
<Trans>
Your account was found to be in violation of the{' '}
<SimpleInlineLinkText
- label={_(msg`Bluesky Social Terms of Service`)}
- to="https://bsky.social/about/support/tos"
+ label={_(msg`syu.is Terms of Service`)}
+ to="https://syu.is/about/support/tos"
style={[a.text_md, a.leading_normal]}>
- Bluesky Social Terms of Service
+ syu.is Terms of Service
</SimpleInlineLinkText>
. You have been sent an email outlining the specific violation
and suspension period, if applicable. You can appeal this
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index e058e2883..8daf41089 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,23 +1,16 @@
import React from 'react'
import {ActivityIndicator, StyleSheet} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
-
import {PROD_DEFAULT_FEED} from '#/lib/constants'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {useOTAUpdates} from '#/lib/hooks/useOTAUpdates'
import {useSetTitle} from '#/lib/hooks/useSetTitle'
import {useRequestNotificationsPermission} from '#/lib/notifications/notifications'
-import {
- type HomeTabNavigatorParams,
- type NativeStackScreenProps,
-} from '#/lib/routes/types'
+import {type HomeTabNavigatorParams, type NativeStackScreenProps} from '#/lib/routes/types'
import {logEvent} from '#/lib/statsig/statsig'
import {isWeb} from '#/platform/detection'
import {emitSoftReset} from '#/state/events'
-import {
- type SavedFeedSourceInfo,
- usePinnedFeedsInfos,
-} from '#/state/queries/feed'
+import {type SavedFeedSourceInfo, usePinnedFeedsInfos} from '#/state/queries/feed'
import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {type UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
@@ -27,11 +20,7 @@ import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
import {FeedPage} from '#/view/com/feeds/FeedPage'
import {HomeHeader} from '#/view/com/home/HomeHeader'
-import {
- Pager,
- type PagerRef,
- type RenderTabBarFnProps,
-} from '#/view/com/pager/Pager'
+import {Pager, type PagerRef, type RenderTabBarFnProps} from '#/view/com/pager/Pager'
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
@@ -39,97 +28,90 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
import * as Layout from '#/components/Layout'
import {useDemoMode} from '#/storage/hooks/demo-mode'
+const SYU_IS_FEED_URI = 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
+
+const DEFAULT_PINNED_FEEDS: any[] = [{
+ feedDescriptor: 'following',
+ displayName: 'Following',
+ id: 'following',
+ uri: 'following',
+ type: 'feed',
+ savedFeed: undefined,
+ pinned: true,
+ route: { href: '/', name: 'Home', params: {} },
+ cid: '',
+ avatar: '',
+ creatorDid: '',
+ creatorHandle: '',
+}, {
+ feedDescriptor: `feedgen|${SYU_IS_FEED_URI}`,
+ displayName: 'Feeds',
+ id: SYU_IS_FEED_URI,
+ uri: SYU_IS_FEED_URI,
+ type: 'feed',
+ savedFeed: {
+ type: 'feed',
+ value: SYU_IS_FEED_URI,
+ pinned: true,
+ },
+ pinned: true,
+ route: { href: '/', name: 'Home', params: {} },
+ cid: '',
+ avatar: '',
+ creatorDid: '',
+ creatorHandle: '',
+}]
+
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
export function HomeScreen(props: Props) {
const {setShowLoggedOut} = useLoggedOutViewControls()
const {data: preferences} = usePreferencesQuery()
const {currentAccount} = useSession()
- const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
- usePinnedFeedsInfos()
+ const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
+
+ const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any
+ // Use user's pinned feeds when logged in and available, otherwise use defaults
+ const safePinnedFeedInfos = !currentAccount
+ ? DEFAULT_PINNED_FEEDS.filter(f => f.feedDescriptor !== 'following')
+ : (pinnedFeedInfos && pinnedFeedInfos.length > 0)
+ ? pinnedFeedInfos
+ : DEFAULT_PINNED_FEEDS
React.useEffect(() => {
if (isWeb && !currentAccount) {
const getParams = new URLSearchParams(window.location.search)
const splash = getParams.get('splash')
- if (splash === 'true') {
- setShowLoggedOut(true)
- return
- }
+ if (splash === 'true') { setShowLoggedOut(true); return }
}
-
const params = props.route.params
- if (
- currentAccount &&
- props.route.name === 'Start' &&
- params?.name &&
- params?.rkey
- ) {
- props.navigation.navigate('StarterPack', {
- rkey: params.rkey,
- name: params.name,
- })
+ if (currentAccount && props.route.name === 'Start' && params?.name && params?.rkey) {
+ props.navigation.navigate('StarterPack', { rkey: params.rkey, name: params.name })
}
- }, [
- currentAccount,
- props.navigation,
- props.route.name,
- props.route.params,
- setShowLoggedOut,
- ])
+ }, [currentAccount, props.navigation, props.route.name, props.route.params, setShowLoggedOut])
- if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) {
- return (
- <Layout.Screen testID="HomeScreen">
- <HomeScreenReady
- {...props}
- preferences={preferences}
- pinnedFeedInfos={pinnedFeedInfos}
- />
- </Layout.Screen>
- )
- } else {
- return (
- <Layout.Screen>
- <Layout.Center style={styles.loading}>
- <ActivityIndicator size="large" />
- </Layout.Center>
- </Layout.Screen>
- )
- }
+ return (
+ <Layout.Screen testID="HomeScreen">
+ <HomeScreenReady {...props} preferences={safePreferences} pinnedFeedInfos={safePinnedFeedInfos as any} />
+ </Layout.Screen>
+ )
}
-function HomeScreenReady({
- preferences,
- pinnedFeedInfos,
-}: Props & {
- preferences: UsePreferencesQueryResponse
- pinnedFeedInfos: SavedFeedSourceInfo[]
-}) {
- const allFeeds = React.useMemo(
- () => pinnedFeedInfos.map(f => f.feedDescriptor),
- [pinnedFeedInfos],
- )
- const maybeRawSelectedFeed: FeedDescriptor | undefined =
- useSelectedFeed() ?? allFeeds[0]
+function HomeScreenReady({preferences, pinnedFeedInfos}: any) {
+ const allFeeds = React.useMemo(() => pinnedFeedInfos.map(f => f.feedDescriptor), [pinnedFeedInfos])
+ const maybeRawSelectedFeed = useSelectedFeed() ?? allFeeds[0]
const setSelectedFeed = useSetSelectedFeed()
const maybeFoundIndex = allFeeds.indexOf(maybeRawSelectedFeed)
const selectedIndex = Math.max(0, maybeFoundIndex)
- const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex]
+ const maybeSelectedFeed = allFeeds[selectedIndex]
const requestNotificationsPermission = useRequestNotificationsPermission()
useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName)
useOTAUpdates()
-
- React.useEffect(() => {
- requestNotificationsPermission('Home')
- }, [requestNotificationsPermission])
+ React.useEffect(() => { requestNotificationsPermission('Home') }, [requestNotificationsPermission])
const pagerRef = React.useRef<PagerRef>(null)
const lastPagerReportedIndexRef = React.useRef(selectedIndex)
React.useLayoutEffect(() => {
- // Since the pager is not a controlled component, adjust it imperatively
- // if the selected index gets out of sync with what it last reported.
- // This is supposed to only happen on the web when you use the right nav.
if (selectedIndex !== lastPagerReportedIndexRef.current) {
lastPagerReportedIndexRef.current = selectedIndex
pagerRef.current?.setPage(selectedIndex)
@@ -138,205 +120,43 @@ function HomeScreenReady({
const {hasSession} = useSession()
const setMinimalShellMode = useSetMinimalShellMode()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode]))
- useFocusEffect(
- useNonReactiveCallback(() => {
- if (maybeSelectedFeed) {
- logEvent('home:feedDisplayed', {
- index: selectedIndex,
- feedType: maybeSelectedFeed.split('|')[0],
- feedUrl: maybeSelectedFeed,
- reason: 'focus',
- })
- }
- }),
- )
-
- const onPageSelected = React.useCallback(
- (index: number) => {
- setMinimalShellMode(false)
- const maybeFeed = allFeeds[index]
+ const onPageSelected = React.useCallback((index) => {
+ setMinimalShellMode(false)
+ const maybeFeed = allFeeds[index]
+ lastPagerReportedIndexRef.current = index
+ setSelectedFeed(maybeFeed)
+ }, [setSelectedFeed, setMinimalShellMode, allFeeds])
- // Mutate the ref before setting state to avoid the imperative syncing effect
- // above from starting a loop on Android when swiping back and forth.
- lastPagerReportedIndexRef.current = index
- setSelectedFeed(maybeFeed)
-
- if (maybeFeed) {
- logEvent('home:feedDisplayed', {
- index,
- feedType: maybeFeed.split('|')[0],
- feedUrl: maybeFeed,
- })
- }
- },
- [setSelectedFeed, setMinimalShellMode, allFeeds],
- )
-
- const onPressSelected = React.useCallback(() => {
- emitSoftReset()
- }, [])
-
- const onPageScrollStateChanged = React.useCallback(
- (state: 'idle' | 'dragging' | 'settling') => {
- 'worklet'
- if (state === 'dragging') {
- setMinimalShellMode(false)
- }
- },
- [setMinimalShellMode],
- )
+ const onPressSelected = React.useCallback(() => { emitSoftReset() }, [])
+ const onPageScrollStateChanged = React.useCallback((state) => {
+ 'worklet'
+ if (state === 'dragging') setMinimalShellMode(false)
+ }, [setMinimalShellMode])
const [demoMode] = useDemoMode()
+ const renderTabBar = React.useCallback((props) => {
+ return <HomeHeader key="FEEDS_TAB_BAR" {...props} testID="homeScreenFeedTabs" onPressSelected={onPressSelected} feeds={pinnedFeedInfos} />
+ }, [onPressSelected, pinnedFeedInfos])
- const renderTabBar = React.useCallback(
- (props: RenderTabBarFnProps) => {
- if (demoMode) {
- return (
- <HomeHeader
- key="FEEDS_TAB_BAR"
- {...props}
- testID="homeScreenFeedTabs"
- onPressSelected={onPressSelected}
- // @ts-ignore
- feeds={[{displayName: 'Following'}, {displayName: 'Discover'}]}
- />
- )
- }
- return (
- <HomeHeader
- key="FEEDS_TAB_BAR"
- {...props}
- testID="homeScreenFeedTabs"
- onPressSelected={onPressSelected}
- feeds={pinnedFeedInfos}
- />
- )
- },
- [onPressSelected, pinnedFeedInfos, demoMode],
- )
-
- const renderFollowingEmptyState = React.useCallback(() => {
- return <FollowingEmptyState />
- }, [])
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, [])
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
- const renderCustomFeedEmptyState = React.useCallback(() => {
- return <CustomFeedEmptyState />
- }, [])
+ const homeFeedParams = React.useMemo(() => ({
+ mergeFeedEnabled: false, mergeFeedSources: []
+ }), [preferences])
- const homeFeedParams = React.useMemo<FeedParams>(() => {
- return {
- mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
- mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
- ? preferences.savedFeeds
- .filter(f => f.type === 'feed' || f.type === 'list')
- .map(f => f.value)
- : [],
- }
- }, [preferences])
-
- if (demoMode) {
- return (
- <Pager
- ref={pagerRef}
- testID="homeScreen"
- onPageSelected={onPageSelected}
- onPageScrollStateChanged={onPageScrollStateChanged}
- renderTabBar={renderTabBar}
- initialPage={selectedIndex}>
- <FeedPage
- testID="demoFeedPage"
- isPageFocused
- isPageAdjacent={false}
- feed="demo"
- renderEmptyState={renderCustomFeedEmptyState}
- feedInfo={pinnedFeedInfos[0]}
- />
- <FeedPage
- testID="customFeedPage"
- isPageFocused
- isPageAdjacent={false}
- feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
- renderEmptyState={renderCustomFeedEmptyState}
- feedInfo={pinnedFeedInfos[0]}
- />
- </Pager>
- )
- }
-
- return hasSession ? (
- <Pager
- key={allFeeds.join(',')}
- ref={pagerRef}
- testID="homeScreen"
- initialPage={selectedIndex}
- onPageSelected={onPageSelected}
- onPageScrollStateChanged={onPageScrollStateChanged}
- renderTabBar={renderTabBar}>
- {pinnedFeedInfos.length ? (
- pinnedFeedInfos.map((feedInfo, index) => {
+ return (
+ <Pager ref={pagerRef} testID="homeScreen" initialPage={selectedIndex} onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}>
+ {pinnedFeedInfos.map((feedInfo, index) => {
const feed = feedInfo.feedDescriptor
if (feed === 'following') {
- return (
- <FeedPage
- key={feed}
- testID="followingFeedPage"
- isPageFocused={maybeSelectedFeed === feed}
- isPageAdjacent={Math.abs(selectedIndex - index) === 1}
- feed={feed}
- feedParams={homeFeedParams}
- renderEmptyState={renderFollowingEmptyState}
- renderEndOfFeed={FollowingEndOfFeed}
- feedInfo={feedInfo}
- />
- )
+ return <FeedPage key={feed} testID="followingFeedPage" isPageFocused={maybeSelectedFeed === feed} isPageAdjacent={Math.abs(selectedIndex - index) === 1} feed={feed} feedParams={homeFeedParams} renderEmptyState={renderFollowingEmptyState} renderEndOfFeed={FollowingEndOfFeed} feedInfo={feedInfo} />
}
- const savedFeedConfig = feedInfo.savedFeed
- return (
- <FeedPage
- key={feed}
- testID="customFeedPage"
- isPageFocused={maybeSelectedFeed === feed}
- isPageAdjacent={Math.abs(selectedIndex - index) === 1}
- feed={feed}
- renderEmptyState={renderCustomFeedEmptyState}
- savedFeedConfig={savedFeedConfig}
- feedInfo={feedInfo}
- />
- )
- })
- ) : (
- <NoFeedsPinned preferences={preferences} />
- )}
- </Pager>
- ) : (
- <Pager
- testID="homeScreen"
- onPageSelected={onPageSelected}
- onPageScrollStateChanged={onPageScrollStateChanged}
- renderTabBar={renderTabBar}>
- <FeedPage
- testID="customFeedPage"
- isPageFocused
- isPageAdjacent={false}
- feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
- renderEmptyState={renderCustomFeedEmptyState}
- feedInfo={pinnedFeedInfos[0]}
- />
+ return <FeedPage key={feed} testID="customFeedPage" isPageFocused={maybeSelectedFeed === feed} isPageAdjacent={Math.abs(selectedIndex - index) === 1} feed={feed} renderEmptyState={renderCustomFeedEmptyState} savedFeedConfig={feedInfo.savedFeed} feedInfo={feedInfo} />
+ })}
</Pager>
)
}
-
-const styles = StyleSheet.create({
- loading: {
- height: '100%',
- alignContent: 'center',
- justifyContent: 'center',
- paddingBottom: 100,
- },
-})
+const styles = StyleSheet.create({ loading: { height: '100%', alignContent: 'center', justifyContent: 'center', paddingBottom: 100 } })
diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
index a89eaadc4..1da393f03 100644
--- a/src/view/screens/PrivacyPolicy.tsx
+++ b/src/view/screens/PrivacyPolicy.tsx
@@ -1,52 +1,13 @@
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 { WebView } from 'react-native-webview'
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'
+export function PrivacyPolicyScreen() {
+ useSetTitle('Privacy Policy')
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>
+ <WebView source={{ uri: 'https://syu.is/about/support/privacy-policy' }} style={{ flex: 1 }} />
</Layout.Screen>
)
}
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
index d843c713c..b81767bd5 100644
--- a/src/view/screens/TermsOfService.tsx
+++ b/src/view/screens/TermsOfService.tsx
@@ -1,50 +1,13 @@
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 { WebView } from 'react-native-webview'
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'
+export function TermsOfServiceScreen() {
+ useSetTitle('Terms of Service')
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>
+ <WebView source={{ uri: 'https://syu.is/about/support/tos' }} style={{ flex: 1 }} />
</Layout.Screen>
)
}

View File

@@ -0,0 +1,56 @@
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index f76147ccf..36b4d7de1 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -292,17 +292,11 @@ 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}
@@ -357,17 +351,7 @@ let DrawerFooter = ({
),
},
]}>
- <Button
- label={_(msg`Send feedback`)}
- size="small"
- variant="solid"
- color="secondary"
- onPress={onPressFeedback}>
- <ButtonIcon icon={Message} position="left" />
- <ButtonText>
- <Trans>Feedback</Trans>
- </ButtonText>
- </Button>
+{/* Feedback button removed for syu.is */}
<Button
label={_(msg`Get help`)}
size="small"
@@ -695,12 +679,12 @@ function ExtraLinks() {
<InlineLinkText
style={[a.text_md]}
label={_(msg`Terms of Service`)}
- to="https://bsky.social/about/support/tos">
+ to="https://syu.is/about/support/tos">
<Trans>Terms of Service</Trans>
</InlineLinkText>
<InlineLinkText
style={[a.text_md]}
- to="https://bsky.social/about/support/privacy-policy"
+ to="https://syu.is/about/support/privacy-policy"
label={_(msg`Privacy Policy`)}>
<Trans>Privacy Policy</Trans>
</InlineLinkText>

View File

@@ -0,0 +1,32 @@
diff --git a/plugins/notificationsExtension/withNotificationsExtension.js b/plugins/notificationsExtension/withNotificationsExtension.js
index 6a00cfd23..f91decc08 100644
--- a/plugins/notificationsExtension/withNotificationsExtension.js
+++ b/plugins/notificationsExtension/withNotificationsExtension.js
@@ -10,7 +10,7 @@ const EXTENSION_NAME = 'BlueskyNSE'
const EXTENSION_CONTROLLER_NAME = 'NotificationService'
const withNotificationsExtension = config => {
- const soundFiles = ['dm.aiff']
+ const soundFiles = []
return withPlugins(config, [
// IOS
diff --git a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
index 8365057e8..59c8506a2 100644
--- a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
+++ b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
@@ -26,12 +26,12 @@ export function Content({state}: {state: PolicyUpdateState}) {
const links = {
terms: {
overridePresentation: false,
- to: `https://bsky.social/about/support/tos`,
+ to: `https://syu.is/about/support/tos`,
label: _(msg`Terms of Service`),
},
privacy: {
overridePresentation: false,
- to: `https://bsky.social/about/support/privacy-policy`,
+ to: `https://syu.is/about/support/privacy-policy`,
label: _(msg`Privacy Policy`),
},
copyright: {

View File

@@ -0,0 +1,174 @@
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
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -39,6 +39,7 @@ export type CommonNavigatorParams = {
Support: undefined
PrivacyPolicy: undefined
TermsOfService: undefined
+ License: undefined
CommunityGuidelines: undefined
CopyrightPolicy: undefined
LanguageSettings: undefined
diff --git a/src/view/screens/License.tsx b/src/view/screens/License.tsx
new file mode 100644
index 000000000..87f52a972
--- /dev/null
+++ b/src/view/screens/License.tsx
@@ -0,0 +1,132 @@
+import React from 'react'
+import { ScrollView, Text as RNText, StyleSheet } from 'react-native'
+import * as Layout from '#/components/Layout'
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
+import {atoms as a, useTheme} from '#/alf'
+
+export function LicenseScreen() {
+ useSetTitle('License')
+ const t = useTheme()
+
+ return (
+ <Layout.Screen>
+ <Layout.Header.Outer>
+ <Layout.Header.BackButton />
+ <Layout.Header.Content>
+ <Layout.Header.TitleText>License</Layout.Header.TitleText>
+ </Layout.Header.Content>
+ <Layout.Header.Slot />
+ </Layout.Header.Outer>
+ <Layout.Content>
+ <ScrollView
+ style={[a.flex_1]}
+ contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
+ <RNText style={styles.text}>
+ This application is based on Bluesky Social App.
+ </RNText>
+
+ <RNText style={styles.link}>
+ https://github.com/bluesky-social/social-app
+ </RNText>
+
+ <RNText style={styles.sectionTitle}>MIT License</RNText>
+
+ <RNText style={styles.mono}>
+ Copyright (c) 2022-2025 Bluesky PBC
+ </RNText>
+
+ <RNText style={styles.text}>
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ </RNText>
+
+ <RNText style={styles.text}>
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ </RNText>
+
+ <RNText style={styles.text}>
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ </RNText>
+
+ <RNText style={styles.sectionTitle2}>日本語訳(参考)</RNText>
+
+ <RNText style={styles.text}>
+ 本ソフトウェアおよび関連文書ファイル(以下「ソフトウェア」)のコピーを取得する
+ すべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、
+ ソフトウェアのコピーを使用、複製、変更、結合、公開、配布、サブライセンス、
+ および/または販売する権利、ならびにソフトウェアを提供する相手にそうした行為を
+ 許可する権利が含まれますが、これらに限定されません。
+ </RNText>
+
+ <RNText style={styles.text}>
+ 上記の著作権表示および本許諾表示を、ソフトウェアのすべてのコピーまたは
+ 重要な部分に記載するものとします。
+ </RNText>
+
+ <RNText style={styles.text}>
+ ソフトウェアは「現状のまま」で提供され、明示黙示を問わず、商品性、特定目的への
+ 適合性、および権利非侵害についての保証を含む、いかなる種類の保証もなされません。
+ いかなる場合においても、作者または著作権者は、契約行為、不法行為、またはそれ以外で
+ あろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用または
+ その他の扱いによって生じる一切の請求、損害、その他の義務について責任を負わないものとします。
+ </RNText>
+
+ <RNText style={styles.footer}>
+ Original License: https://github.com/bluesky-social/social-app/blob/main/LICENSE
+ </RNText>
+ </ScrollView>
+ </Layout.Content>
+ </Layout.Screen>
+ )
+}
+
+const styles = StyleSheet.create({
+ title: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ marginBottom: 16,
+ },
+ text: {
+ fontSize: 14,
+ marginBottom: 12,
+ lineHeight: 20,
+ },
+ link: {
+ fontSize: 14,
+ marginBottom: 12,
+ color: '#0066cc',
+ },
+ sectionTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginTop: 16,
+ marginBottom: 12,
+ },
+ sectionTitle2: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginTop: 24,
+ marginBottom: 12,
+ },
+ mono: {
+ fontSize: 14,
+ marginBottom: 12,
+ fontFamily: 'monospace',
+ },
+ footer: {
+ fontSize: 12,
+ marginTop: 24,
+ color: '#666666',
+ },
+})

View File

@@ -0,0 +1,25 @@
diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx
index aa6cd4156..37c7a38b0 100644
--- a/src/screens/Signup/index.tsx
+++ b/src/screens/Signup/index.tsx
@@ -211,20 +211,6 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
a.align_center,
]}>
<AppLanguageDropdown />
- <Text
- style={[
- a.flex_1,
- t.atoms.text_contrast_medium,
- !gtMobile && a.text_md,
- ]}>
- <Trans>Having trouble?</Trans>{' '}
- <InlineLinkText
- label={_(msg`Contact support`)}
- to={FEEDBACK_FORM_URL({email: state.email})}
- style={[!gtMobile && a.text_md]}>
- <Trans>Contact support</Trans>
- </InlineLinkText>
- </Text>
</View>
</View>
</ScreenTransition>

View File

@@ -0,0 +1,77 @@
diff --git a/src/routes.ts b/src/routes.ts
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -74,6 +74,7 @@ export const router = new Router<AllNavigatableRoutes>({
PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
TermsOfService: 'https://syu.is/about/support/tos',
CommunityGuidelines: '/support/community-guidelines',
+ License: 'https://syu.is/about/support/license',
CopyrightPolicy: '/support/copyright',
// hashtags
Hashtag: '/hashtag/:tag',
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
--- a/src/view/com/auth/SplashScreen.tsx
+++ b/src/view/com/auth/SplashScreen.tsx
@@ -1,4 +1,5 @@
import {View} from 'react-native'
+import {Pressable, Linking} from 'react-native'
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg, Trans} from '@lingui/macro'
@@ -40,16 +41,6 @@ export const SplashScreen = ({
<View style={[a.pb_sm, a.pt_5xl]}>
<Logotype width={161} fill={t.atoms.text.color} />
</View>
-
- <Text
- style={[
- a.text_md,
- a.font_semi_bold,
- t.atoms.text_contrast_medium,
- a.text_center,
- ]}>
- <Trans>What's up?</Trans>
- </Text>
</View>
<View
@@ -102,6 +93,21 @@ export const SplashScreen = ({
<AppLanguageDropdown />
</View>
</View>
+ <View style={[a.pb_sm, a.justify_center, a.align_center]}>
+ <Pressable onPress={() => Linking.openURL('https://syu.is/about/support/license')}>
+ <Text
+ style={[
+ a.text_xs,
+ t.atoms.text_contrast_low,
+ {textDecorationLine: 'underline'},
+ ]}>
+ License
+ </Text>
+ </Pressable>
+ </View>
+ <View style={[a.pb_xl, a.justify_center, a.align_center]}>
+ <Text style={[a.text_xs, t.atoms.text_contrast_low]}>© syui</Text>
+ </View>
<View style={{height: insets.bottom}} />
</ErrorBoundary>
</Animated.View>
diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
--- a/src/view/com/auth/SplashScreen.web.tsx
+++ b/src/view/com/auth/SplashScreen.web.tsx
@@ -94,14 +94,6 @@ export const SplashScreen = ({
</View>
)}
- <Text
- style={[
- a.text_md,
- a.font_semi_bold,
- t.atoms.text_contrast_medium,
- ]}>
- <Trans>What's up?</Trans>
- </Text>
</View>
<View

View File

@@ -0,0 +1,48 @@
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 6b0e184c0..42b609c9e 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -203,24 +203,8 @@ export function SettingsScreen({}: Props) {
<Trans>Notifications</Trans>
</SettingsList.ItemText>
</SettingsList.LinkItem>
- <SettingsList.LinkItem
- to="/settings/content-and-media"
- label={_(msg`Content and media`)}>
- <SettingsList.ItemIcon icon={WindowIcon} />
- <SettingsList.ItemText>
- <Trans>Content and media</Trans>
- </SettingsList.ItemText>
- </SettingsList.LinkItem>
- {isNative && findContactsEnabled && (
- <SettingsList.LinkItem
- to="/settings/find-contacts"
- label={_(msg`Find friends from contacts`)}>
- <SettingsList.ItemIcon icon={ContactsIcon} />
- <SettingsList.ItemText>
- <Trans>Find friends from contacts</Trans>
- </SettingsList.ItemText>
- </SettingsList.LinkItem>
- )}
+{/* Content and media removed for syu.is */}
+{/* Find friends from contacts removed for syu.is */}
<SettingsList.LinkItem
to="/settings/appearance"
label={_(msg`Appearance`)}>
@@ -245,16 +229,6 @@ export function SettingsScreen({}: Props) {
<Trans>Languages</Trans>
</SettingsList.ItemText>
</SettingsList.LinkItem>
- <SettingsList.PressableItem
- onPress={() => Linking.openURL(HELP_DESK_URL)}
- label={_(msg`Help`)}
- accessibilityHint={_(msg`Opens helpdesk in browser`)}>
- <SettingsList.ItemIcon icon={CircleQuestionIcon} />
- <SettingsList.ItemText>
- <Trans>Help</Trans>
- </SettingsList.ItemText>
- <SettingsList.Chevron />
- </SettingsList.PressableItem>
<SettingsList.LinkItem to="/settings/about" label={_(msg`About`)}>
<SettingsList.ItemIcon icon={BubbleInfoIcon} />
<SettingsList.ItemText>

View File

@@ -0,0 +1,31 @@
diff --git a/plugins/withCodeSignEntitlements.js b/plugins/withCodeSignEntitlements.js
new file mode 100644
index 000000000..b03b6bd68
--- /dev/null
+++ b/plugins/withCodeSignEntitlements.js
@@ -0,0 +1,25 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const { withXcodeProject } = require('@expo/config-plugins')
+
+const withCodeSignEntitlements = (config) => {
+ return withXcodeProject(config, (config) => {
+ const xcodeProject = config.modResults
+ const configurations = xcodeProject.pbxXCBuildConfigurationSection()
+
+ for (const key in configurations) {
+ const configuration = configurations[key]
+ if (
+ configuration.buildSettings &&
+ configuration.comment &&
+ !configuration.comment.includes('TEST')
+ ) {
+ configuration.buildSettings.CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION =
+ 'YES'
+ }
+ }
+
+ return config
+ })
+}
+
+module.exports = withCodeSignEntitlements

View File

@@ -0,0 +1,30 @@
diff --git a/src/ageAssurance/index.tsx b/src/ageAssurance/index.tsx
index 9a0a9c9d5..5a6563e52 100644
--- a/src/ageAssurance/index.tsx
+++ b/src/ageAssurance/index.tsx
@@ -88,19 +88,16 @@ function InnerProvider({children}: {children: React.ReactNode}) {
return (
<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,
- state,
+ state: {
+ ...state,
+ access: AgeAssuranceAccess.Full,
+ },
flags: {
- adultContentDisabled,
- chatDisabled,
+ adultContentDisabled: false,
+ chatDisabled: false,
},
}
}, [state, data])}>

View File

@@ -0,0 +1,247 @@
diff --git a/src/view/com/posts/DiscoverFallbackHeader.tsx b/src/view/com/posts/DiscoverFallbackHeader.tsx
index e35a33aaf..a36f84ae0 100644
--- a/src/view/com/posts/DiscoverFallbackHeader.tsx
+++ b/src/view/com/posts/DiscoverFallbackHeader.tsx
@@ -7,37 +7,5 @@ import {TextLink} from '../util/Link'
import {Text} from '../util/text/Text'
export function DiscoverFallbackHeader() {
- const pal = usePalette('default')
- return (
- <View
- style={[
- {
- flexDirection: 'row',
- alignItems: 'center',
- paddingVertical: 12,
- paddingHorizontal: 12,
- borderTopWidth: 1,
- },
- pal.border,
- pal.viewLight,
- ]}>
- <View style={{width: 68, paddingLeft: 12}}>
- <InfoCircleIcon size={36} style={pal.textLight} strokeWidth={1.5} />
- </View>
- <View style={{flex: 1}}>
- <Text type="md" style={pal.text}>
- <Trans>
- We ran out of posts from your follows. Here's the latest from{' '}
- <TextLink
- type="md-medium"
- href="/profile/bsky.app/feed/whats-hot"
- text="Discover"
- style={pal.link}
- />
- .
- </Trans>
- </Text>
- </View>
- </View>
- )
+ return null
}
diff --git a/src/view/com/posts/FollowingEmptyState.tsx b/src/view/com/posts/FollowingEmptyState.tsx
index 352cc1dc0..f477521af 100644
--- a/src/view/com/posts/FollowingEmptyState.tsx
+++ b/src/view/com/posts/FollowingEmptyState.tsx
@@ -1,37 +1,14 @@
import React from 'react'
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 {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')
- const palInverted = usePalette('inverted')
- const navigation = useNavigation<NavigationProp>()
-
- const onPressFindAccounts = React.useCallback(() => {
- if (isWeb) {
- navigation.navigate('Search', {})
- } else {
- navigation.navigate('SearchTab')
- navigation.popToTop()
- }
- }, [navigation])
-
- const onPressDiscoverFeeds = React.useCallback(() => {
- navigation.navigate('Feeds')
- }, [navigation])
return (
<View style={styles.container}>
@@ -45,36 +22,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>
)
@@ -98,13 +45,4 @@ const styles = StyleSheet.create({
marginLeft: 'auto',
marginRight: 'auto',
},
- emptyBtn: {
- marginVertical: 20,
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingVertical: 18,
- paddingHorizontal: 24,
- borderRadius: 30,
- },
})
diff --git a/src/view/com/posts/FollowingEndOfFeed.tsx b/src/view/com/posts/FollowingEndOfFeed.tsx
index e3c84d782..efb55d406 100644
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
@@ -1,36 +1,13 @@
import React from 'react'
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 {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')
- const palInverted = usePalette('inverted')
- const navigation = useNavigation<NavigationProp>()
-
- const onPressFindAccounts = React.useCallback(() => {
- if (isWeb) {
- navigation.navigate('Search', {})
- } else {
- navigation.navigate('SearchTab')
- navigation.popToTop()
- }
- }, [navigation])
-
- const onPressDiscoverFeeds = React.useCallback(() => {
- navigation.navigate('Feeds')
- }, [navigation])
return (
<View
@@ -41,41 +18,8 @@ export function FollowingEndOfFeed() {
]}>
<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>
+ <Trans>You've reached the end of your feed!</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>
)
@@ -93,13 +37,4 @@ const styles = StyleSheet.create({
width: '100%',
maxWidth: 460,
},
- emptyBtn: {
- marginVertical: 20,
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingVertical: 18,
- paddingHorizontal: 24,
- borderRadius: 30,
- },
})
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
index 4f25468c9..a72a10b80 100644
--- a/src/view/com/posts/PostFeed.tsx
+++ b/src/view/com/posts/PostFeed.tsx
@@ -766,7 +766,7 @@ let PostFeed = ({
} else if (row.type === 'feedShutdownMsg') {
return <FeedShutdownMsg feedUri={feedUriOrActorDid} />
} else if (row.type === 'interstitialFollows') {
- return <SuggestedFollows feed={feed} />
+ return null
} else if (row.type === 'interstitialProgressGuide') {
return <ProgressGuide />
} else if (row.type === 'ageAssuranceBanner') {

View File

@@ -0,0 +1,51 @@
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index 790f211ee..ec05a8bcd 100644
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -317,6 +317,12 @@ func serve(cctx *cli.Context) error {
e.GET("/support/tos", server.WebGeneric)
e.GET("/support/community-guidelines", server.WebGeneric)
e.GET("/support/copyright", server.WebGeneric)
+ // about/support pages (syu.is specific)
+ e.GET("/about/support/tos", server.WebAboutTOS)
+ e.GET("/about/support/privacy-policy", server.WebAboutPrivacy)
+ e.GET("/about/support/help", server.WebAboutHelp)
+ e.GET("/about/support/license", server.WebAboutLicense)
+ e.GET("/about/support/app", server.WebAboutApp)
e.GET("/intent/compose", server.WebGeneric)
e.GET("/intent/verify-email", server.WebGeneric)
e.GET("/intent/age-assurance", server.WebGeneric)
@@ -825,3 +831,33 @@ func (srv *Server) serveSitemapRequest(c echo.Context, url, sitemapType string)
return nil
}
+
+// Handler for About TOS page (syu.is specific)
+func (srv *Server) WebAboutTOS(c echo.Context) error {
+ data := srv.NewTemplateContext()
+ return c.Render(http.StatusOK, "about-tos.html", data)
+}
+
+// Handler for About Privacy Policy page (syu.is specific)
+func (srv *Server) WebAboutPrivacy(c echo.Context) error {
+ data := srv.NewTemplateContext()
+ return c.Render(http.StatusOK, "about-privacy.html", data)
+}
+
+// Handler for About Help page (syu.is specific)
+func (srv *Server) WebAboutHelp(c echo.Context) error {
+ data := srv.NewTemplateContext()
+ return c.Render(http.StatusOK, "about-help.html", data)
+}
+
+// Handler for About License page (syu.is specific)
+func (srv *Server) WebAboutLicense(c echo.Context) error {
+ data := srv.NewTemplateContext()
+ return c.Render(http.StatusOK, "about-license.html", data)
+}
+
+// Handler for About App page (syu.is specific)
+func (srv *Server) WebAboutApp(c echo.Context) error {
+ data := srv.NewTemplateContext()
+ return c.Render(http.StatusOK, "about-app.html", data)
+}

View File

@@ -0,0 +1,70 @@
diff --git a/src/state/messages/events/index.tsx b/src/state/messages/events/index.tsx
index 2ff0784ae..dc314ecc5 100644
--- a/src/state/messages/events/index.tsx
+++ b/src/state/messages/events/index.tsx
@@ -10,13 +10,7 @@ const MessagesEventBusContext = React.createContext<MessagesEventBus | null>(
MessagesEventBusContext.displayName = 'MessagesEventBusContext'
export function useMessagesEventBus() {
- const ctx = React.useContext(MessagesEventBusContext)
- if (!ctx) {
- throw new Error(
- 'useMessagesEventBus must be used within a MessagesEventBusProvider',
- )
- }
- return ctx
+ return React.useContext(MessagesEventBusContext)
}
export function MessagesEventBusProvider({
@@ -24,18 +18,11 @@ export function MessagesEventBusProvider({
}: {
children: React.ReactNode
}) {
- const {currentAccount} = useSession()
-
- if (!currentAccount) {
- return (
- <MessagesEventBusContext.Provider value={null}>
- {children}
- </MessagesEventBusContext.Provider>
- )
- }
-
+ // DM functionality is disabled for syu.is
return (
- <MessagesEventBusProviderInner>{children}</MessagesEventBusProviderInner>
+ <MessagesEventBusContext.Provider value={null}>
+ {children}
+ </MessagesEventBusContext.Provider>
)
}
diff --git a/src/state/queries/messages/list-conversations.tsx b/src/state/queries/messages/list-conversations.tsx
index c5457d1cb..5bc37bdce 100644
--- a/src/state/queries/messages/list-conversations.tsx
+++ b/src/state/queries/messages/list-conversations.tsx
@@ -74,17 +74,12 @@ export function useListConvos() {
const empty = {accepted: [], request: []}
export function ListConvosProvider({children}: {children: React.ReactNode}) {
- const {hasSession} = useSession()
-
- if (!hasSession) {
- return (
- <ListConvosContext.Provider value={empty}>
- {children}
- </ListConvosContext.Provider>
- )
- }
-
- return <ListConvosProviderInner>{children}</ListConvosProviderInner>
+ // DM functionality is disabled for syu.is - always return empty
+ return (
+ <ListConvosContext.Provider value={empty}>
+ {children}
+ </ListConvosContext.Provider>
+ )
}
export function ListConvosProviderInner({

View File

@@ -0,0 +1,15 @@
diff --git a/src/env/common.ts b/src/env/common.ts
--- a/src/env/common.ts
+++ b/src/env/common.ts
@@ -107,9 +107,8 @@ export const GCP_PROJECT_ID: number =
/**
* URLs for the app config web worker. Can be a
* locally running server, see `env.example` for more.
+ * Disabled for self-hosted environment to avoid CORS errors
*/
export const BAPP_CONFIG_DEV_URL = process.env.BAPP_CONFIG_DEV_URL
export const BAPP_CONFIG_PROD_URL = `https://ip.bsky.app`
-export const BAPP_CONFIG_URL = IS_DEV
- ? (BAPP_CONFIG_DEV_URL ?? BAPP_CONFIG_PROD_URL)
- : BAPP_CONFIG_PROD_URL
+export const BAPP_CONFIG_URL = null

View File

@@ -0,0 +1,91 @@
diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
--- a/bskyweb/templates/base.html
+++ b/bskyweb/templates/base.html
@@ -7,9 +7,9 @@
<!--
Preconnect to essential domains
-->
- <link rel="preconnect" href="https://bsky.social">
- <link rel="preconnect" href="https://go.bsky.app">
- <title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
+ <link rel="preconnect" href="https://syu.is">
+ <link rel="preconnect" href="https://bsky.syu.is">
+ <title>{%- block head_title -%}syu.is{%- endblock -%}</title>
<!-- Hello Humans! API docs at https://atproto.com -->
@@ -121,7 +121,7 @@
<noscript>
<h1 lang="en">JavaScript Required</h1>
<p lang="en">This is a heavily interactive web application, and JavaScript is required. Simple HTML interfaces are possible, but that is not what this is.
- <p lang="en">Learn more about Bluesky at <a href="https://bsky.social">bsky.social</a> and <a href="https://atproto.com">atproto.com</a>.
+ <p lang="en">Learn more at <a href="https://syu.is">syu.is</a> and <a href="https://atproto.com">atproto.com</a>.
{% block noscript_extra %}{% endblock %}
</noscript>
{% endblock -%}
diff --git a/bskyweb/templates/home.html b/bskyweb/templates/home.html
--- a/bskyweb/templates/home.html
+++ b/bskyweb/templates/home.html
@@ -1,17 +1,17 @@
{% extends "base.html" %}
-{% block head_title %}Bluesky{% endblock %}
+{% block head_title %}syu.is{% endblock %}
{% block html_head_extra -%}
- <meta property="og:title" content="Bluesky" />
- <meta name="twitter:title" content="Bluesky" />
+ <meta property="og:title" content="syu.is" />
+ <meta name="twitter:title" content="syu.is" />
<meta name="description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
<meta name="og:description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
<meta name="twitter:description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
- <meta property="og:url" content="https://bsky.app" />
- <meta name="twitter:url" content="https://bsky.app" />
- <link rel="canonical" href="https://bsky.app" />
+ <meta property="og:url" content="https://syu.is" />
+ <meta name="twitter:url" content="https://syu.is" />
+ <link rel="canonical" href="https://syu.is" />
<meta property="og:image" content="https://bsky.app/static/social-card-default-gradient.png" />
diff --git a/bskyweb/templates/error.html b/bskyweb/templates/error.html
--- a/bskyweb/templates/error.html
+++ b/bskyweb/templates/error.html
@@ -1,6 +1,6 @@
{% extends "base.html" %}
-{% block head_title %}Error {{ statusCode }} - Bluesky{% endblock %}
+{% block head_title %}Error {{ statusCode }} - syu.is{% endblock %}
{% block noscript_extra %}
{%- if statusCode == 404 %}
diff --git a/bskyweb/templates/starterpack.html b/bskyweb/templates/starterpack.html
--- a/bskyweb/templates/starterpack.html
+++ b/bskyweb/templates/starterpack.html
@@ -17,8 +17,8 @@
<meta property="og:title" content="{{ title }}" />
<meta name="twitter:title" content="{{ title }}" />
{%- else -%}
- <meta property="og:title" content="Bluesky" />
- <meta name="twitter:title" content="Bluesky" />
+ <meta property="og:title" content="syu.is" />
+ <meta name="twitter:title" content="syu.is" />
{% endif -%}
<meta name="description" content="Join the conversation" />
<meta name="og:description" content="Join the conversation" />
diff --git a/web/index.html b/web/index.html
--- a/web/index.html
+++ b/web/index.html
@@ -14,8 +14,8 @@
<!--
Preconnect to essential domains
-->
- <link rel="preconnect" href="https://bsky.social">
- <link rel="preconnect" href="https://go.bsky.app">
+ <link rel="preconnect" href="https://syu.is">
+ <link rel="preconnect" href="https://bsky.syu.is">
<title>%WEB_TITLE%</title>
<link rel="preload" as="font" type="font/woff2" href="/static/media/InterVariable.c504db5c06caaf7cdfba.woff2" crossorigin>

View File

@@ -0,0 +1,101 @@
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
--- a/src/screens/Signup/StepInfo/index.tsx
+++ b/src/screens/Signup/StepInfo/index.tsx
@@ -7,11 +7,9 @@
import {isEmailMaybeInvalid} from '#/lib/strings/email'
import {logger} from '#/logger'
-import {is13, is18, useSignupContext} from '#/screens/Signup/state'
+import {useSignupContext} from '#/screens/Signup/state'
import {Policies} from '#/screens/Signup/StepInfo/Policies'
import {atoms as a, native} from '#/alf'
-import * as DateField from '#/components/forms/DateField'
-import {type DateFieldRef} from '#/components/forms/DateField/types'
import {FormError} from '#/components/forms/FormError'
import {HostingProvider} from '#/components/forms/HostingProvider'
import * as TextField from '#/components/forms/TextField'
@@ -22,16 +20,6 @@
import {usePreemptivelyCompleteActivePolicyUpdate} from '#/components/PolicyUpdateOverlay/usePreemptivelyCompleteActivePolicyUpdate'
import {BackNextButtons} from '../BackNextButtons'
-function sanitizeDate(date: Date): Date {
- if (!date || date.toString() === 'Invalid Date') {
- logger.error(`Create account: handled invalid date for birthDate`, {
- hasDate: !!date,
- })
- return new Date()
- }
- return date
-}
-
export function StepInfo({
onPressBack,
isServerError,
@@ -55,7 +43,6 @@
const emailInputRef = useRef<TextInput>(null)
const passwordInputRef = useRef<TextInput>(null)
- const birthdateInputRef = useRef<DateFieldRef>(null)
const [hasWarnedEmail, setHasWarnedEmail] = React.useState<boolean>(false)
@@ -76,10 +63,6 @@
const emailChanged = prevEmailValueRef.current !== email
const password = passwordValueRef.current
- if (!is13(state.dateOfBirth)) {
- return
- }
-
if (state.serviceDescription?.inviteCodeRequired && !inviteCode) {
return dispatch({
type: 'setError',
@@ -246,44 +229,21 @@
secureTextEntry
autoComplete="new-password"
autoCapitalize="none"
- returnKeyType="next"
- submitBehavior={native('blurAndSubmit')}
- onSubmitEditing={native(() =>
- birthdateInputRef.current?.focus(),
- )}
+ returnKeyType="done"
passwordRules="minlength: 8;"
/>
</TextField.Root>
</View>
- <View>
- <DateField.LabelText>
- <Trans>Your birth date</Trans>
- </DateField.LabelText>
- <DateField.DateField
- testID="date"
- inputRef={birthdateInputRef}
- value={state.dateOfBirth}
- onChangeDate={date => {
- dispatch({
- type: 'setDateOfBirth',
- value: sanitizeDate(new Date(date)),
- })
- }}
- label={_(msg`Date of birth`)}
- accessibilityHint={_(msg`Select your date of birth`)}
- maximumDate={new Date()}
- />
- </View>
<Policies
serviceDescription={state.serviceDescription}
- needsGuardian={!is18(state.dateOfBirth)}
- under13={!is13(state.dateOfBirth)}
+ needsGuardian={false}
+ under13={false}
/>
</>
) : undefined}
</View>
<BackNextButtons
- hideNext={!is13(state.dateOfBirth)}
+ hideNext={false}
showRetry={isServerError}
isLoading={state.isLoading}
onBackPress={onPressBack}

View File

@@ -0,0 +1,16 @@
diff --git a/src/screens/Search/Explore.tsx b/src/screens/Search/Explore.tsx
--- a/src/screens/Search/Explore.tsx
+++ b/src/screens/Search/Explore.tsx
@@ -687,12 +687,7 @@ export function Explore({
if (useFullExperience) {
i.push(trendingTopicsModule)
- i.push(...suggestedFeedsModule)
- i.push(...suggestedFollowsModule)
- i.push(...suggestedStarterPacksModule)
i.push(...feedPreviewsModule)
- } else {
- i.push(...suggestedFollowsModule)
}
return i

View File

@@ -0,0 +1,84 @@
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
--- a/src/view/screens/Feeds.tsx
+++ b/src/view/screens/Feeds.tsx
@@ -288,80 +288,7 @@ export function FeedsScreen(_props: Props) {
}
}
- if (!hasSession || (hasSession && canShowDiscoverSection)) {
- slices.push({
- key: 'popularFeedsHeader',
- type: 'popularFeedsHeader',
- })
- if (popularFeedsError || searchError) {
- slices.push({
- key: 'popularFeedsError',
- type: 'error',
- error: cleanError(
- popularFeedsError?.toString() ?? searchError?.toString() ?? '',
- ),
- })
- } else {
- if (isUserSearching) {
- if (isSearchPending || !searchResults) {
- slices.push({
- key: 'popularFeedsLoading',
- type: 'popularFeedsLoading',
- })
- } else {
- if (!searchResults || searchResults?.length === 0) {
- slices.push({
- key: 'popularFeedsNoResults',
- type: 'popularFeedsNoResults',
- })
- } else {
- slices = slices.concat(
- searchResults.map(feed => ({
- key: `popularFeed:${feed.uri}`,
- type: 'popularFeed',
- feedUri: feed.uri,
- feed,
- })),
- )
- }
- }
- } else {
- if (isPopularFeedsFetching && !popularFeeds?.pages) {
- slices.push({
- key: 'popularFeedsLoading',
- type: 'popularFeedsLoading',
- })
- } else {
- if (!popularFeeds?.pages) {
- slices.push({
- key: 'popularFeedsNoResults',
- type: 'popularFeedsNoResults',
- })
- } else {
- for (const page of popularFeeds.pages || []) {
- slices = slices.concat(
- page.feeds.map(feed => ({
- key: `popularFeed:${feed.uri}`,
- type: 'popularFeed',
- feedUri: feed.uri,
- feed,
- })),
- )
- }
-
- if (isPopularFeedsFetchingNextPage) {
- slices.push({
- key: 'popularFeedsLoadingMore',
- type: 'popularFeedsLoadingMore',
- })
- }
- }
- }
- }
- }
- }
-
return slices
}, [
hasSession,

View File

@@ -0,0 +1,361 @@
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -63,6 +63,7 @@ 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 {AppInfoScreen} from '#/view/screens/AppInfo'
import {ProfileScreen} from '#/view/screens/Profile'
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
import {Storybook} from '#/view/screens/Storybook'
@@ -341,6 +342,11 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
getComponent={() => LicenseScreen}
options={{title: title(msg`License`)}}
/>
+ <Stack.Screen
+ name="AppInfo"
+ getComponent={() => AppInfoScreen}
+ options={{title: title(msg`App Info`)}}
+ />
<Stack.Screen
name="CommunityGuidelines"
getComponent={() => CommunityGuidelinesScreen}
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -40,6 +40,7 @@ export type CommonNavigatorParams = {
PrivacyPolicy: undefined
TermsOfService: undefined
License: undefined
+ AppInfo: undefined
CommunityGuidelines: undefined
CopyrightPolicy: undefined
LanguageSettings: undefined
diff --git a/src/routes.ts b/src/routes.ts
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -75,6 +75,7 @@ export const router = new Router<AllNavigatableRoutes>({
TermsOfService: 'https://syu.is/about/support/tos',
CommunityGuidelines: '/support/community-guidelines',
License: 'https://syu.is/about/support/license',
+ AppInfo: 'https://syu.is/about/support/app',
CopyrightPolicy: '/support/copyright',
// hashtags
Hashtag: '/hashtag/:tag',
diff --git a/src/view/screens/AppInfo.tsx b/src/view/screens/AppInfo.tsx
new file mode 100644
index 000000000..000000001
--- /dev/null
+++ b/src/view/screens/AppInfo.tsx
@@ -0,0 +1,310 @@
+import React, {useState} from 'react'
+import {
+ View,
+ ScrollView,
+ StyleSheet,
+ Pressable,
+ Image,
+} from 'react-native'
+import * as Clipboard from 'expo-clipboard'
+import * as WebBrowser from 'expo-web-browser'
+import {Trans} from '@lingui/macro'
+
+import * as Layout from '#/components/Layout'
+import {Text} from '#/components/Typography'
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
+import {atoms as a, useTheme} from '#/alf'
+
+const APP_VERSION = '1.111.0'
+const APP_NAME = 'Aiat'
+const BITCOIN_ADDRESS = '3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr'
+
+export function AppInfoScreen() {
+ useSetTitle('App Info')
+ const t = useTheme()
+ const [copied, setCopied] = useState(false)
+
+ const copyToClipboard = async (text: string) => {
+ try {
+ await Clipboard.setStringAsync(text)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ } catch (e) {
+ console.log('Clipboard not available')
+ }
+ }
+
+ const openUrl = (url: string) => {
+ WebBrowser.openBrowserAsync(url)
+ }
+
+ return (
+ <Layout.Screen>
+ <Layout.Header.Outer>
+ <Layout.Header.BackButton />
+ <Layout.Header.Content>
+ <Layout.Header.TitleText>
+ <Trans>App Info</Trans>
+ </Layout.Header.TitleText>
+ </Layout.Header.Content>
+ <Layout.Header.Slot />
+ </Layout.Header.Outer>
+ <Layout.Content>
+ <ScrollView
+ style={[a.flex_1]}
+ contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
+ {/* App Header */}
+ <View style={styles.appHeader}>
+ <View style={[styles.appIconContainer, t.atoms.bg_contrast_25]}>
+ <Image
+ source={require('../../../assets/logo.png')}
+ style={styles.appIcon}
+ resizeMode="cover"
+ />
+ </View>
+ <Text style={[styles.appName, t.atoms.text]}>
+ {APP_NAME}
+ </Text>
+ <Text style={[styles.appVersion, t.atoms.text_contrast_medium]}>
+ v{APP_VERSION}
+ </Text>
+ </View>
+
+ {/* Description Section */}
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
+ <Text style={[styles.description, t.atoms.text]}>
+ {APP_NAME} is a social networking application based on AT Protocol.
+ Connect with your community on syu.is.
+ </Text>
+ </View>
+
+ {/* App Information Section */}
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
+ App Information
+ </Text>
+ <View style={styles.infoGrid}>
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
+ Version
+ </Text>
+ <Text style={[styles.infoValue, t.atoms.text]}>
+ {APP_VERSION}
+ </Text>
+ </View>
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
+ Category
+ </Text>
+ <Text style={[styles.infoValue, t.atoms.text]}>
+ Social
+ </Text>
+ </View>
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
+ Supported OS
+ </Text>
+ <Text style={[styles.infoValue, t.atoms.text]}>
+ iOS 26.0+
+ </Text>
+ </View>
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
+ Price
+ </Text>
+ <Text style={[styles.infoValue, t.atoms.text]}>
+ Free
+ </Text>
+ </View>
+ </View>
+ </View>
+
+ {/* Developer Section */}
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
+ Developer
+ </Text>
+ <View style={styles.developerCard}>
+ <Text style={[styles.developerName, t.atoms.text]}>syui</Text>
+ </View>
+
+ <Pressable
+ onPress={() => openUrl('https://github.com/syui')}
+ style={[styles.linkRow, t.atoms.border_contrast_low]}>
+ <Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
+ GitHub
+ </Text>
+ <Text style={[styles.linkValue, {color: '#0084ff'}]}>
+ github.com/syui
+ </Text>
+ <Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
+ </Pressable>
+
+ <Pressable
+ onPress={() => openUrl('https://syu.is/syui')}
+ style={[styles.linkRow, t.atoms.border_contrast_low]}>
+ <Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
+ ATProto
+ </Text>
+ <Text style={[styles.linkValue, {color: '#0084ff'}]}>
+ syu.is/syui
+ </Text>
+ <Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
+ </Pressable>
+ </View>
+
+ {/* Bitcoin Section */}
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
+ Bitcoin
+ </Text>
+ <Pressable
+ onPress={() => copyToClipboard(BITCOIN_ADDRESS)}
+ style={styles.bitcoinRow}>
+ <Text style={styles.bitcoinLabel}>₿</Text>
+ <Text style={[styles.bitcoinAddress, t.atoms.text_contrast_medium]}>
+ {BITCOIN_ADDRESS}
+ </Text>
+ <Text style={[styles.copyHint, copied && styles.copiedHint]}>
+ {copied ? 'copied!' : 'copy'}
+ </Text>
+ </Pressable>
+ </View>
+
+ {/* Copyright */}
+ <View style={styles.copyright}>
+ <Text style={[styles.copyrightText, t.atoms.text_contrast_low]}>
+ © syui
+ </Text>
+ </View>
+ </ScrollView>
+ </Layout.Content>
+ </Layout.Screen>
+ )
+}
+
+const styles = StyleSheet.create({
+ appHeader: {
+ alignItems: 'center',
+ marginBottom: 24,
+ },
+ appIconContainer: {
+ width: 80,
+ height: 80,
+ borderRadius: 18,
+ overflow: 'hidden',
+ marginBottom: 12,
+ },
+ appIcon: {
+ width: '100%',
+ height: '100%',
+ },
+ appName: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ marginBottom: 4,
+ },
+ appVersion: {
+ fontSize: 14,
+ },
+ section: {
+ marginBottom: 16,
+ borderRadius: 16,
+ padding: 16,
+ },
+ sectionTitle: {
+ fontSize: 13,
+ fontWeight: '600',
+ textTransform: 'uppercase',
+ letterSpacing: 0.5,
+ marginBottom: 12,
+ },
+ description: {
+ fontSize: 15,
+ lineHeight: 22,
+ },
+ infoGrid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: 8,
+ },
+ infoItem: {
+ flex: 1,
+ minWidth: '45%',
+ alignItems: 'center',
+ borderRadius: 12,
+ padding: 12,
+ },
+ infoLabel: {
+ fontSize: 11,
+ textTransform: 'uppercase',
+ letterSpacing: 0.5,
+ marginBottom: 4,
+ },
+ infoValue: {
+ fontSize: 16,
+ fontWeight: '600',
+ textAlign: 'center',
+ },
+ developerCard: {
+ marginBottom: 12,
+ },
+ developerName: {
+ fontSize: 18,
+ fontWeight: '600',
+ },
+ linkRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 12,
+ borderTopWidth: 1,
+ },
+ linkIcon: {
+ fontSize: 14,
+ fontWeight: '600',
+ width: 70,
+ },
+ linkValue: {
+ flex: 1,
+ fontSize: 14,
+ },
+ linkArrow: {
+ fontSize: 16,
+ },
+ bitcoinRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(247, 147, 26, 0.08)',
+ borderRadius: 12,
+ padding: 14,
+ gap: 10,
+ },
+ bitcoinLabel: {
+ fontSize: 18,
+ fontWeight: '600',
+ color: '#f7931a',
+ },
+ bitcoinAddress: {
+ flex: 1,
+ fontSize: 11,
+ fontFamily: 'monospace',
+ },
+ copyHint: {
+ fontSize: 12,
+ color: '#999999',
+ minWidth: 50,
+ textAlign: 'right',
+ },
+ copiedHint: {
+ color: '#4CAF50',
+ fontWeight: '600',
+ },
+ copyright: {
+ alignItems: 'center',
+ marginTop: 20,
+ marginBottom: 20,
+ },
+ copyrightText: {
+ fontSize: 12,
+ },
+})

View File

@@ -0,0 +1,71 @@
diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts
index 18bb8c8f0..bab286d7a 100644
--- a/src/lib/api/feed/custom.ts
+++ b/src/lib/api/feed/custom.ts
@@ -5,6 +5,7 @@ import {
jsonStringToLex,
} from '@atproto/api'
+import {PUBLIC_APPVIEW} from '#/lib/constants'
import {
getAppLanguageAsContentLanguage,
getContentLanguages,
@@ -12,6 +13,17 @@ import {
import {type FeedAPI, type FeedAPIResponse} from './types'
import {createBskyTopicsHeader, isBlueskyOwnedFeed} from './utils'
+// Check if the feed is hosted on syu.is network
+function isSyuIsFeed(feedUri: string): boolean {
+ return feedUri.includes('did:plc:6qyecktefllvenje24fcxnie') || feedUri.includes('syu.is')
+}
+
+// Check if the agent is connected to syu.is
+function isAgentOnSyuIs(agent: BskyAgent): boolean {
+ const serviceUrl = agent.service?.toString() || ''
+ return serviceUrl.includes('syu.is')
+}
+
export class CustomFeedAPI implements FeedAPI {
agent: BskyAgent
params: GetCustomFeed.QueryParams
@@ -54,8 +66,12 @@ export class CustomFeedAPI implements FeedAPI {
const agent = this.agent
const isBlueskyOwned = isBlueskyOwnedFeed(this.params.feed)
- const res = agent.did
- ? await this.agent.app.bsky.feed.getFeed(
+ // For syu.is feeds accessed from non-syu.is accounts, use PUBLIC_APPVIEW
+ const needsPublicAppView = isSyuIsFeed(this.params.feed) && !isAgentOnSyuIs(agent)
+
+ const res = !agent.did || needsPublicAppView
+ ? await loggedOutFetch({...this.params, cursor, limit})
+ : await this.agent.app.bsky.feed.getFeed(
{
...this.params,
cursor,
@@ -70,7 +86,6 @@ export class CustomFeedAPI implements FeedAPI {
},
},
)
- : await loggedOutFetch({...this.params, cursor, limit})
if (res.success) {
// NOTE
// some custom feeds fail to enforce the pagination limit
@@ -120,7 +135,7 @@ async function loggedOutFetch({
// manually construct fetch call so we can add the `lang` cache-busting param
let res = await fetch(
- `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
+ `${PUBLIC_APPVIEW}/xrpc/app.bsky.feed.getFeed?feed=${encodeURIComponent(feed)}${
cursor ? `&cursor=${cursor}` : ''
}&limit=${limit}&lang=${contentLangs}`,
{
@@ -140,7 +155,7 @@ async function loggedOutFetch({
// no data, try again with language headers removed
res = await fetch(
- `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
+ `${PUBLIC_APPVIEW}/xrpc/app.bsky.feed.getFeed?feed=${encodeURIComponent(feed)}${
cursor ? `&cursor=${cursor}` : ''
}&limit=${limit}`,
{method: 'GET', headers: {'Accept-Language': '', ...labelersHeader}},

View File

@@ -0,0 +1,23 @@
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 123456789..987654321 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -218,13 +218,13 @@ function ProfileScreenLoaded({
const showPostsTab = true
const showRepliesTab = hasSession
const showMediaTab = !hasLabeler
- const showVideosTab = !hasLabeler
+ const showVideosTab = false
const showLikesTab = isMe
const feedGenCount = profile.associated?.feedgens || 0
- const showFeedsTab = isMe || feedGenCount > 0
+ const showFeedsTab = feedGenCount > 0
const starterPackCount = profile.associated?.starterPacks || 0
- const showStarterPacksTab = isMe || starterPackCount > 0
+ const showStarterPacksTab = false
// subtract starterpack count from list count, since starterpacks are a type of list
const listCount = (profile.associated?.lists || 0) - starterPackCount
- const showListsTab = hasSession && (isMe || listCount > 0)
+ const showListsTab = false
const sectionTitles = [

View File

@@ -0,0 +1,28 @@
diff --git a/src/view/com/home/HomeHeader.tsx b/src/view/com/home/HomeHeader.tsx
--- a/src/view/com/home/HomeHeader.tsx
+++ b/src/view/com/home/HomeHeader.tsx
@@ -3,7 +3,6 @@ import {useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types'
import {type FeedSourceInfo} from '#/state/queries/feed'
-import {useSession} from '#/state/session'
import {type RenderTabBarFnProps} from '#/view/com/pager/Pager'
import {TabBar} from '../pager/TabBar'
import {HomeHeaderLayout} from './HomeHeaderLayout'
@@ -16,17 +15,15 @@ export function HomeHeader(
) {
const {feeds, onSelect: onSelectProp} = props
- const {hasSession} = useSession()
const navigation = useNavigation<NavigationProp>()
const hasPinnedCustom = React.useMemo<boolean>(() => {
- if (!hasSession) return false
return feeds.some(tab => {
const isFollowing = tab.uri === 'following'
return !isFollowing
})
- }, [feeds, hasSession])
+ }, [feeds])
const items = React.useMemo(() => {
const pinnedNames = feeds.map(f => f.displayName)

View File

@@ -0,0 +1,13 @@
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
index 63e11a7f4..70fa993cf 100644
--- a/src/components/dialogs/nuxs/index.tsx
+++ b/src/components/dialogs/nuxs/index.tsx
@@ -46,7 +46,7 @@ const queuedNuxs: {
enabled: ({currentProfile}) => {
return (
isNative &&
- isExistingUserAsOf('2025-12-16T00:00:00.000Z', currentProfile.createdAt)
+ isExistingUserAsOf('2099-12-16T00:00:00.000Z', currentProfile.createdAt)
)
},
},

310
ios/patching/AppInfo.tsx Normal file
View File

@@ -0,0 +1,310 @@
import React, {useState} from 'react'
import {
View,
ScrollView,
StyleSheet,
Pressable,
Image,
} from 'react-native'
import * as Clipboard from 'expo-clipboard'
import * as WebBrowser from 'expo-web-browser'
import {Trans} from '@lingui/macro'
import * as Layout from '#/components/Layout'
import {Text} from '#/components/Typography'
import {useSetTitle} from '#/lib/hooks/useSetTitle'
import {atoms as a, useTheme} from '#/alf'
const APP_VERSION = '1.111.0'
const APP_NAME = 'Aiat'
const BITCOIN_ADDRESS = '3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr'
export function AppInfoScreen() {
useSetTitle('App Info')
const t = useTheme()
const [copied, setCopied] = useState(false)
const copyToClipboard = async (text: string) => {
try {
await Clipboard.setStringAsync(text)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (e) {
console.log('Clipboard not available')
}
}
const openUrl = (url: string) => {
WebBrowser.openBrowserAsync(url)
}
return (
<Layout.Screen>
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>App Info</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<ScrollView
style={[a.flex_1]}
contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
{/* App Header */}
<View style={styles.appHeader}>
<View style={[styles.appIconContainer, t.atoms.bg_contrast_25]}>
<Image
source={require('../../../assets/logo.png')}
style={styles.appIcon}
resizeMode="cover"
/>
</View>
<Text style={[styles.appName, t.atoms.text]}>
{APP_NAME}
</Text>
<Text style={[styles.appVersion, t.atoms.text_contrast_medium]}>
v{APP_VERSION}
</Text>
</View>
{/* Description Section */}
<View style={[styles.section, t.atoms.bg_contrast_25]}>
<Text style={[styles.description, t.atoms.text]}>
{APP_NAME} is a social networking application based on AT Protocol.
Connect with your community on syu.is.
</Text>
</View>
{/* App Information Section */}
<View style={[styles.section, t.atoms.bg_contrast_25]}>
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
App Information
</Text>
<View style={styles.infoGrid}>
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
Version
</Text>
<Text style={[styles.infoValue, t.atoms.text]}>
{APP_VERSION}
</Text>
</View>
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
Category
</Text>
<Text style={[styles.infoValue, t.atoms.text]}>
Social
</Text>
</View>
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
Supported OS
</Text>
<Text style={[styles.infoValue, t.atoms.text]}>
iOS 26.0+
</Text>
</View>
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
Price
</Text>
<Text style={[styles.infoValue, t.atoms.text]}>
Free
</Text>
</View>
</View>
</View>
{/* Developer Section */}
<View style={[styles.section, t.atoms.bg_contrast_25]}>
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
Developer
</Text>
<View style={styles.developerCard}>
<Text style={[styles.developerName, t.atoms.text]}>syui</Text>
</View>
<Pressable
onPress={() => openUrl('https://git.syui.ai/syui')}
style={[styles.linkRow, t.atoms.border_contrast_low]}>
<Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
Git
</Text>
<Text style={[styles.linkValue, {color: '#0084ff'}]}>
git.syui.ai/syui
</Text>
<Text style={[styles.linkArrow, t.atoms.text_contrast_low]}></Text>
</Pressable>
<Pressable
onPress={() => openUrl('https://syu.is/syui')}
style={[styles.linkRow, t.atoms.border_contrast_low]}>
<Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
ATProto
</Text>
<Text style={[styles.linkValue, {color: '#0084ff'}]}>
syu.is/syui
</Text>
<Text style={[styles.linkArrow, t.atoms.text_contrast_low]}></Text>
</Pressable>
</View>
{/* Bitcoin Section */}
<View style={[styles.section, t.atoms.bg_contrast_25]}>
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
Bitcoin
</Text>
<Pressable
onPress={() => copyToClipboard(BITCOIN_ADDRESS)}
style={styles.bitcoinRow}>
<Text style={styles.bitcoinLabel}></Text>
<Text style={[styles.bitcoinAddress, t.atoms.text_contrast_medium]}>
{BITCOIN_ADDRESS}
</Text>
<Text style={[styles.copyHint, copied && styles.copiedHint]}>
{copied ? 'copied!' : 'copy'}
</Text>
</Pressable>
</View>
{/* Copyright */}
<View style={styles.copyright}>
<Text style={[styles.copyrightText, t.atoms.text_contrast_low]}>
© syui
</Text>
</View>
</ScrollView>
</Layout.Content>
</Layout.Screen>
)
}
const styles = StyleSheet.create({
appHeader: {
alignItems: 'center',
marginBottom: 24,
},
appIconContainer: {
width: 80,
height: 80,
borderRadius: 18,
overflow: 'hidden',
marginBottom: 12,
},
appIcon: {
width: '100%',
height: '100%',
},
appName: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 4,
},
appVersion: {
fontSize: 14,
},
section: {
marginBottom: 16,
borderRadius: 16,
padding: 16,
},
sectionTitle: {
fontSize: 13,
fontWeight: '600',
textTransform: 'uppercase',
letterSpacing: 0.5,
marginBottom: 12,
},
description: {
fontSize: 15,
lineHeight: 22,
},
infoGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
},
infoItem: {
flex: 1,
minWidth: '45%',
alignItems: 'center',
borderRadius: 12,
padding: 12,
},
infoLabel: {
fontSize: 11,
textTransform: 'uppercase',
letterSpacing: 0.5,
marginBottom: 4,
},
infoValue: {
fontSize: 16,
fontWeight: '600',
textAlign: 'center',
},
developerCard: {
marginBottom: 12,
},
developerName: {
fontSize: 18,
fontWeight: '600',
},
linkRow: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
borderTopWidth: 1,
},
linkIcon: {
fontSize: 14,
fontWeight: '600',
width: 70,
},
linkValue: {
flex: 1,
fontSize: 14,
},
linkArrow: {
fontSize: 16,
},
bitcoinRow: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(247, 147, 26, 0.08)',
borderRadius: 12,
padding: 14,
gap: 10,
},
bitcoinLabel: {
fontSize: 18,
fontWeight: '600',
color: '#f7931a',
},
bitcoinAddress: {
flex: 1,
fontSize: 11,
fontFamily: 'monospace',
},
copyHint: {
fontSize: 12,
color: '#999999',
minWidth: 50,
textAlign: 'right',
},
copiedHint: {
color: '#4CAF50',
fontWeight: '600',
},
copyright: {
alignItems: 'center',
marginTop: 20,
marginBottom: 20,
},
copyrightText: {
fontSize: 12,
},
})

86
ios/patching/License.tsx Normal file
View File

@@ -0,0 +1,86 @@
import React from 'react'
import { ScrollView } from 'react-native'
import * as Layout from '#/components/Layout'
import {useSetTitle} from '#/lib/hooks/useSetTitle'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
export function LicenseScreen() {
useSetTitle('License')
const t = useTheme()
return (
<Layout.Screen>
<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]}>License</Text>
<Text style={[a.mb_md]}>
This application is based on Bluesky Social App.
</Text>
<Text style={[a.text_md, a.mb_md, {color: t.palette.primary_500}]}>
https://github.com/bluesky-social/social-app
</Text>
<Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>MIT License</Text>
<Text style={[a.mb_md, {fontFamily: 'monospace'}]}>
Copyright (c) 2022-2025 Bluesky PBC
</Text>
<Text style={[a.mb_md]}>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
</Text>
<Text style={[a.mb_md]}>
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
</Text>
<Text style={[a.mb_md]}>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</Text>
<Text style={[a.text_lg, a.font_bold, a.mt_xl, a.mb_md]}></Text>
<Text style={[a.mb_md]}>
使
/
</Text>
<Text style={[a.mb_md]}>
</Text>
<Text style={[a.mb_md]}>
使
</Text>
<Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
Original License: https://github.com/bluesky-social/social-app/blob/main/LICENSE
</Text>
</ScrollView>
</Layout.Screen>
)
}

62
ios/patching/README.md Normal file
View File

@@ -0,0 +1,62 @@
# iOS Social App Patches
このディレクトリには、iOS版social-appのカスタマイズパッチが含まれています。
## パッチファイル一覧
- `001-social-app-ios-config.patch` - app.config.js の設定変更アプリ名、Bundle ID、アイコンパス、ドメイン等
- `002-social-app-ios-lib.patch` - lib/constants.ts, lib/statsig, lib/url-helpers の変更
- `003-social-app-ios-view.patch` - Logo, Logotype, UserAvatar, Splash.tsx の UI 変更Bluesky 蝶ロゴを logo.png に変更)
- `004-social-app-ios-core.patch` - agent.ts, App.native.tsx, routes.ts のコア変更
- `005-social-app-ios-screens.patch` - Settings, Home, Privacy, TOS 画面の変更
- `006-social-app-ios-shell.patch` - Drawer, BottomBar, RightNav, ServerInput などシェル変更
- `007-social-app-ios-misc.patch` - notifications, ageAssurance, PolicyUpdate などその他変更
- `008-social-app-ios-policy-tos-error.patch` - プライバシーポリシー・利用規約をネイティブコンポーネントで表示WebView から ScrollView + Text へ変更)
- `009-social-app-ios-license.patch` - ライセンスページの追加Drawer, Navigation, routes, types
- `010-social-app-ios-remove-contact-support.patch` - アカウント作成時の「Contact support」リンクを削除
- `011-social-app-ios-splash-license-footer.patch` - スプラッシュ画面の「What's up?」削除、© syui フッター追加
- `012-social-app-ios-settings-about-help.patch` - About 設定と routes.ts のリンクを内部ルートに変更(/support/tos, /support/privacy-policy, /support/license
- `013-social-app-ios-settings-remove-help.patch` - Settings から Help 項目を削除
- `019-social-app-ios-entitlements-plugin.patch` - iOS entitlements プラグイン設定
- `020-social-app-ios-bypass-age-assurance.patch` - 年齢確認を完全に無効化access を Full に固定、chatDisabled と adultContentDisabled を false に固定)
- `021-social-app-ios-clean-feed.patch` - Following フィードのシンプル化DiscoverFallbackHeader の (i) アイコンと Discover リンク削除、SuggestedFollows インタースティシャル無効化、おすすめボタン削除)
- `License.tsx` - ライセンス表示画面(新規ファイル)
## 使用方法
### パッチの適用
```bash
cd /Users/syui/ai/at/ios
./setup.zsh patch
```
**注意**: setup.zsh が自動的に以下を実行します:
- パッチファイルの適用
- License.tsx のコピー
- Xcode AppIcon を logo.png から 1024x1024 にリサイズして配置GraphicsMagick または sips を使用)
### リポジトリのリセット
```bash
cd /Users/syui/ai/at/ios
./setup.zsh reset
```
### すべてのパッチを適用(デフォルト)
```bash
cd /Users/syui/ai/at/ios
./setup.zsh
```
## パッチの更新方法
repos/social-app で変更を加えた後:
```bash
cd /Users/syui/ai/at/repos/social-app
git diff [ファイル名] > /Users/syui/ai/at/ios/patching/新しいパッチ.patch
```
その後、`setup.zsh``PATCH_FILES_IOS` 配列に新しいパッチファイル名を追加してください。