ai/at
1
0

Compare commits

..

5 Commits

Author SHA1 Message Date
fa790ef90a fix 2025-12-07 14:27:04 +09:00
ce6900d623 fix 2025-12-07 14:26:48 +09:00
0564f93ad8 fix patch cmd 2025-12-07 14:23:14 +09:00
5b2255b3f6 add ios patch 2025-12-07 12:01:31 +09:00
b15994e5c0 add ios social-app 2025-12-07 10:20:07 +09:00
168 changed files with 2878 additions and 17504 deletions

View File

@@ -1,40 +0,0 @@
name: Deploy to Cloudflare Pages
on:
push:
branches:
- main
paths:
- 'web/**'
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install
working-directory: web
- name: Build
run: npm run build
working-directory: web
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy web/dist/aiat --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}

8
.gitignore vendored
View File

@@ -2,11 +2,3 @@ repos
.claude .claude
deploy.yml deploy.yml
claude.md claude.md
embedded.mobileprovision
.env
k8s/secrets.env
k8s/deploy.yml
web/dist
node_modules
package-lock.json
/tmp

View File

@@ -1,15 +1,18 @@
# at # at
- https://github.com/bluesky-social/atproto - https://github.com/bluesky-social/atproto
- https://github.com/bluesky-social/social-app - https://github.com/bluesky-social/atproto/discussions/2026
|name|type|example| |word|name|example|
|---|---|---| |---|---|---|
|at|uri|at://example.com| |at|uri|at://example.com|
|@|user|@example.com| |@|user|@example.com|
|[at]proto|repo|`git@github.com:bluesky-social/atproto`|
|[at]mosphere|system|pds, bsky(appview), ozone, bgs, plc| |[at]mosphere|system|pds, bsky(appview), ozone, bgs, plc|
|[a]uthenticated [t]ransfer|protocol|[did](https://www.w3.org/TR/did-core/)| |[a]uthenticated [t]ransfer|protocol|[did](https://www.w3.org/TR/did-core/)|
- https://atproto.com/ja/guides/glossary
## account ## account
- [ai@syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie) - [ai@syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie)
@@ -19,14 +22,27 @@
```sh ```sh
$ curl -sL syu.is/xrpc/_health $ curl -sL syu.is/xrpc/_health
# latest
# https://github.com/bluesky-social/atproto/blob/main/packages/pds/package.json
$ curl -sL https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/packages/pds/package.json |jq -r .version
```
```sh
$ handle=ai.syui.ai
$ curl -sL "syu.is/xrpc/com.atproto.repo.describeRepo?repo=${handle}" |jq -r .did
did:plc:6qyecktefllvenje24fcxnie
$ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=${handle}&collection=app.bsky.feed.post&reverse=true&limit=1"
{"records":[{"uri":"at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.post/3l6s2riuouk2j","cid":"bafyreibjohl7va4upkibw5twaxdd4jg3l6rmfatu4dpjjfd5xkb2ijtlx4","value":{"text":"hello","$type":"app.bsky.feed.post","langs":["ja"],"createdAt":"2024-10-18T13:21:39.809Z"}}],"cursor":"3l6s2riuouk2j"}
``` ```
## feed ## feed
> at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app > at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app
- https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie/feed/app - https://syu.is/profile/ai.syui.ai/feed/app
- https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app - https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/app
```json ```json
{ {
@@ -49,23 +65,3 @@ $ curl -sL syu.is/xrpc/_health
} }
} }
``` ```
## build
```sh
# build
./install.zsh
# build social-app
./install.zsh pull;./install.zsh patch;./install.zsh build social-app;./install.zsh push social-app
---
# server
./install.zsh
---
# social-app ios
# https://appstoreconnect.apple.com/
# https://developer.apple.com/account/resources/profiles/list
./install.zsh pull;./ios/setup.zsh
./ios/build.zsh
```

View File

@@ -77,7 +77,7 @@ services:
- 2470:2470 - 2470:2470
build: build:
context: ./repos/indigo/ context: ./repos/indigo/
dockerfile: cmd/relay/Dockerfile dockerfile: cmd/bigsky/Dockerfile
restart: always restart: always
env_file: env_file:
- ./envs/bgs - ./envs/bgs
@@ -86,7 +86,6 @@ services:
depends_on: depends_on:
database: database:
condition: service_healthy condition: service_healthy
#command: ["/bigsky", "--crawl-insecure-ws"]
social-app: social-app:
ports: ports:
@@ -155,5 +154,3 @@ services:
- ./envs/feed - ./envs/feed
volumes: volumes:
- ./data/feed:/data/ - ./data/feed:/data/
depends_on:
- jetstream

View File

@@ -1,20 +0,0 @@
FROM node:20-alpine
WORKDIR /app
# Install dependencies for better-sqlite3
RUN apk add --no-cache python3 make g++
# Copy package files and install
COPY package.json yarn.lock ./
RUN yarn install
# Copy source
COPY . .
# Build TypeScript
RUN yarn build
EXPOSE 3000
CMD ["node", "dist/index.js"]

View File

@@ -3,5 +3,5 @@ FEEDGEN_LISTENHOST=0.0.0.0
FEEDGEN_SQLITE_LOCATION=/data/db.sqlite FEEDGEN_SQLITE_LOCATION=/data/db.sqlite
FEEDGEN_HOSTNAME=feed.syu.is FEEDGEN_HOSTNAME=feed.syu.is
FEEDGEN_PUBLISHER_DID=did:plc:6qyecktefllvenje24fcxnie FEEDGEN_PUBLISHER_DID=did:plc:6qyecktefllvenje24fcxnie
FEEDGEN_SUBSCRIPTION_ENDPOINT=ws://bgs:2470
FEEDGEN_SERVICE_DID=did:web:feed.syu.is FEEDGEN_SERVICE_DID=did:web:feed.syu.is
FEEDGEN_JETSTREAM_URL=ws://jetstream:6008/subscribe

View File

@@ -1,4 +1,4 @@
JETSTREAM_WS_URL=ws://bgs.${host}/xrpc/com.atproto.sync.subscribeRepos JETSTREAM_WS_URL=wss://bgs.${host}/xrpc/com.atproto.sync.subscribeRepos
JETSTREAM_DATA_DIR=/data JETSTREAM_DATA_DIR=/data
JETSTREAM_LISTEN_ADDR=:6008 JETSTREAM_LISTEN_ADDR=:6008
JETSTREAM_METRICS_LISTEN_ADDR=:6009 JETSTREAM_METRICS_LISTEN_ADDR=:6009

32
icon.svg Normal file
View File

@@ -0,0 +1,32 @@
<svg
viewBox="0 0 2821.6379 794.29016"
preserveAspectRatio="xMidYMid"
xmlns:svg="http://www.w3.org/2000/svg">
<g
transform="matrix(0.1,0,0,-0.1,-282.80153,1445)"
fill="#000000"
stroke="none"
>
<path
d="m 24787,14443 c -4,-3 -7,-224 -7,-490 v -483 h 545 545 v 490 490 h -538 c -296,0 -542,-3 -545,-7 z"
/>
<path
d="m 5190,13285 c -8,-3 -96,-12 -195,-20 -199,-16 -296,-32 -430,-70 -49,-14 -115,-32 -145,-40 -153,-39 -504,-198 -662,-301 -21,-13 -57,-36 -80,-51 -24,-16 -72,-50 -109,-78 -241,-182 -377,-315 -528,-517 -119,-158 -120,-160 -106,-188 23,-45 140,-140 560,-457 110,-84 319,-242 465,-353 146,-110 369,-279 495,-375 791,-600 723,-549 1049,-799 269,-207 398,-307 524,-403 l 114,-86 -49,-49 c -128,-131 -378,-258 -588,-299 -66,-13 -357,-13 -420,0 -115,23 -172,39 -202,54 -18,10 -37,17 -43,17 -24,0 -171,81 -255,141 -50,35 -146,121 -215,192 -69,70 -133,127 -142,127 -19,0 -153,-63 -177,-83 -9,-7 -54,-35 -101,-62 -47,-26 -110,-64 -140,-85 -30,-20 -97,-61 -148,-91 -51,-30 -107,-62 -124,-72 -18,-10 -57,-38 -87,-61 -70,-53 -252,-168 -446,-281 -82,-49 -158,-95 -168,-104 -16,-16 -14,-21 34,-106 165,-289 544,-666 867,-860 9,-6 35,-22 58,-37 36,-23 267,-138 349,-173 108,-47 160,-67 240,-93 150,-48 201,-62 228,-62 14,0 64,-9 109,-19 197,-46 302,-56 573,-56 197,1 281,5 345,17 47,9 110,19 141,22 31,3 75,13 99,21 23,8 56,15 72,15 17,0 51,7 77,15 25,8 80,24 121,36 41,12 125,41 185,66 61,25 124,50 140,56 17,7 89,42 160,78 113,58 177,98 395,246 82,56 273,232 403,371 127,136 237,271 237,291 0,12 -208,179 -425,342 -186,140 -1121,843 -1720,1294 -264,199 -589,444 -723,546 -134,101 -274,208 -312,237 -39,29 -70,58 -70,66 0,21 107,115 203,179 95,64 237,133 295,143 20,4 49,12 63,20 96,48 519,48 619,-1 14,-7 41,-16 60,-20 65,-13 262,-118 360,-191 99,-74 250,-230 372,-384 34,-44 70,-80 80,-80 9,0 39,15 67,33 28,17 70,44 94,58 23,15 121,76 217,136 96,60 254,156 350,213 96,56 272,160 390,230 118,70 230,135 248,144 41,21 41,45 -3,116 -18,30 -46,75 -61,100 -64,106 -252,348 -352,454 -106,112 -169,171 -293,274 -197,164 -310,235 -594,376 -215,106 -519,205 -735,240 -137,22 -574,51 -610,41 z"
/>
<path
d="m 29095,12736 c -421,-44 -744,-157 -975,-342 -259,-207 -396,-446 -465,-809 -16,-84 -23,-301 -14,-425 36,-518 257,-859 694,-1070 166,-80 284,-116 716,-215 449,-103 514,-121 646,-186 218,-107 308,-253 306,-494 -2,-303 -188,-477 -588,-551 -140,-26 -640,-27 -825,-1 -275,38 -486,94 -776,203 -94,35 -174,64 -178,64 -3,0 -6,-175 -6,-389 v -388 l 88,-41 c 404,-187 905,-282 1486,-282 675,0 1154,150 1465,459 196,194 311,434 362,756 20,121 17,476 -5,600 -89,517 -358,800 -945,994 -130,43 -241,71 -616,156 -137,31 -299,73 -360,92 -331,106 -455,246 -455,511 1,249 127,412 387,501 272,92 801,76 1269,-39 116,-28 326,-96 432,-138 l 52,-22 -2,406 -3,406 -66,28 c -154,66 -413,140 -604,174 -279,49 -756,69 -1020,42 z"
/>
<path
d="m 9630,12676 c 0,-2 403,-996 896,-2208 493,-1211 898,-2211 901,-2220 6,-21 -135,-386 -193,-499 -104,-202 -256,-324 -471,-380 -118,-30 -340,-43 -516,-29 -84,6 -185,17 -225,24 -40,7 -75,10 -77,7 -3,-2 -5,-163 -5,-357 v -353 l 63,-30 c 194,-93 493,-138 801,-120 414,23 683,115 937,319 174,140 357,402 474,680 26,62 1945,5159 1945,5167 0,2 -232,2 -516,1 l -517,-3 -556,-1550 c -485,-1350 -560,-1550 -578,-1553 -18,-3 -26,11 -62,105 -23,59 -295,758 -605,1553 l -562,1445 -567,3 c -312,1 -567,0 -567,-2 z"
/>
<path
d="m 15690,10867 c 0,-1970 -1,-1936 56,-2153 128,-497 461,-787 1014,-885 147,-26 440,-36 605,-20 413,40 834,200 1181,447 35,25 73,44 88,44 14,0 26,-1 26,-3 0,-2 20,-94 45,-205 25,-111 45,-204 45,-207 0,-3 209,-5 465,-5 h 465 v 2400 2400 h -535 -535 l -2,-1866 -3,-1866 -115,-50 c -425,-185 -743,-252 -1088,-227 -302,21 -472,109 -562,293 -79,161 -74,11 -77,1969 l -3,1747 h -535 -535 z"
/>
<path
d="m 24785,12668 c -3,-7 -4,-1086 -3,-2398 l 3,-2385 538,-3 537,-2 v 2400 2400 h -535 c -419,0 -537,-3 -540,-12 z"
/>
<path
d="m 21660,8275 v -545 h 565 565 v 545 545 h -565 -565 z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -52,10 +52,6 @@ function at-repos-env() {
name=${host%%.*} name=${host%%.*}
domain=${host##*.} domain=${host##*.}
dport=5000 dport=5000
typeset -A PINNED_COMMITS
PINNED_COMMITS=()
#PINNED_COMMITS=( [indigo]="d49b454196351c988ceb5ce1f5e21b689487b5ab" [atproto]="104e6ed37b0589cc000109dc76316be35b2257e1")
} }
# Arrays for patch management # Arrays for patch management
@@ -72,10 +68,11 @@ typeset -a PATCH_FILES
PATCH_FILES=( PATCH_FILES=(
"170-pds-oauth-same-site-fix.patch" "170-pds-oauth-same-site-fix.patch"
"8980-social-app-disable-proxy.diff" "8980-social-app-disable-proxy.diff"
"disable-statsig-sdk.diff"
"140-social-app-yarn-network-timeout.patch" "140-social-app-yarn-network-timeout.patch"
"130-atproto-ozone-enable-daemon-v2.patch" "130-atproto-ozone-enable-daemon-v2.patch"
"190-bgs-disable-ratelimit.patch"
"200-feed-generator-custom.patch" "200-feed-generator-custom.patch"
"210-bgs-since-empty-fix.patch"
) )
function at-repos-clone() { function at-repos-clone() {
@@ -103,10 +100,6 @@ function at-repos-pull() {
echo $repo echo $repo
if [ -d $d/repos/${repo##*/} ];then if [ -d $d/repos/${repo##*/} ];then
cd $d/repos/${repo##*/} cd $d/repos/${repo##*/}
# Clean up before pull: reset changes, remove .orig files and untracked patch-created files
git checkout -- .
find . -name "*.orig" -type f -delete 2>/dev/null
git clean -fd 2>/dev/null
git stash -u git stash -u
if ! git pull;then if ! git pull;then
rm -rf $d/repos/${repo##*/} rm -rf $d/repos/${repo##*/}
@@ -120,31 +113,32 @@ function at-repos-pull() {
cd .. cd ..
fi fi
done done
# Copy feed-generator Dockerfile if missing (removed by git checkout)
if [ ! -f $d/repos/feed-generator/Dockerfile ] && [ -f $d/docker/feed/Dockerfile ];then
cp -rf $d/docker/feed/Dockerfile $d/repos/feed-generator/
fi
cd $d cd $d
} }
function at-repos-checkout-pinned() { function at-repos-social-app-avatar-write() {
echo "🔒 Checking out pinned commits..." did_admin=did:plc:6qyecktefllvenje24fcxnie
cd $d/repos dt=$d/repos/social-app/src
for repo_name pinned_commit in ${(kv)PINNED_COMMITS}; do cd $dt
if [ -n "$pinned_commit" ] && [ -d "$d/repos/$repo_name" ]; then grep -R syu.is .|cut -d : -f 1|sort -u|xargs sediment "s/syu.is/${host}/g"
echo " 📌 $repo_name -> $pinned_commit" grep -R web.syu.is .|cut -d : -f 1|sort -u|xargs sediment "s/web.syu.is/web.${host}/g"
cd $d/repos/$repo_name f=$dt/lib/constants.ts
git fetch origin sediment "s#export const BSKY_SERVICE = 'https://bsky.social'#export const BSKY_SERVICE = 'https://${host}'#g" $f
git checkout $pinned_commit sediment "s#export const BSKY_SERVICE_DID = 'did:web:bsky.social'#export const BSKY_SERVICE_DID = 'did:web:${host}'#g" $f
cd $d/repos sediment "s#export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'#export const PUBLIC_BSKY_SERVICE = 'https://bsky.${host}'#g" $f
fi sediment "s#export const PUBLIC_APPVIEW = 'https://api.bsky.app'#export const PUBLIC_APPVIEW = 'https://bsky.${host}'#g" $f
done sediment "s#export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'#export const PUBLIC_APPVIEW_DID = 'did:web:bsky.${host}'#g" $f
cd $d
f=$dt/view/com/util/UserAvatar.tsx
curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/view/com/util/UserAvatar.tsx -o $f
sediment "s#/img/avatar/plain/#https://cdn.web.syu.is/img/avatar/plain/#g" $f
sediment "s#/img/avatar_thumbnail/plain/#https://bsky.${host}/img/avatar/plain/#g" $f
sediment "s#source={{uri: avatar}}#source={{ uri: hackModifyThumbnailPath(avatar, 1 > 0), }}#g" $f
curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/lib/strings/url-helpers.ts -o $dt/lib/strings/url-helpers.ts
sediment "s#https://go.web.syu.is/redirect?u=\${encodeURIComponent(url)}#\${url}#g" $dt/lib/strings/url-helpers.ts
grep -R $did_admin .|cut -d : -f 1|sort -u|xargs sediment "s/${did_admin}/${did}/g"
} }
function at-repos-social-app-ios-patch() {
$d/ios/setup.zsh
}
# Common patch function with status detection # Common patch function with status detection
function apply-patch() { function apply-patch() {
@@ -267,21 +261,16 @@ function at-repos-patch-apply-all() {
local repo="" local repo=""
# Determine repo from filename # Determine repo from filename
# Note: check indigo/bgs BEFORE pds to avoid "newpds" matching "pds"
if [[ $filename == *"social-app"* || $filename == *"statsig"* ]]; then if [[ $filename == *"social-app"* || $filename == *"statsig"* ]]; then
repo="social-app" repo="social-app"
elif [[ $filename == *"indigo"* || $filename == *"bgs"* ]]; then
repo="indigo"
elif [[ $filename == *"atproto"* ]]; then elif [[ $filename == *"atproto"* ]]; then
repo="atproto" repo="atproto"
elif [[ $filename == *"pds"* ]]; then elif [[ $filename == *"pds"* ]]; then
repo="atproto" repo="atproto"
elif [[ $filename == *"indigo"* || $filename == *"bgs"* ]]; then
repo="indigo"
elif [[ $filename == *"feed"* ]]; then elif [[ $filename == *"feed"* ]]; then
repo="feed-generator" repo="feed-generator"
# feed-generatorパッチ適用前に既存のDockerfileを削除upstreamと競合回避
if [[ $filename == "200-feed-generator-custom.patch" ]]; then
rm -f "$d/repos/feed-generator/Dockerfile"
fi
fi fi
patch-apply "$title" "$repo" "$filename" patch-apply "$title" "$repo" "$filename"
@@ -334,169 +323,20 @@ export const SOCIAL_APP_DOMAIN =\
}' lib/constants.ts 2>/dev/null || true }' lib/constants.ts 2>/dev/null || true
# Fix parseInt() to handle undefined by adding || '' # Fix parseInt() to handle undefined by adding || ''
sediment "s/parseInt(env('\([^']*\)'))/parseInt(env('\1') || '0')/g" lib/constants.ts 2>/dev/null || true sediment "s/parseInt(env('\([^']*\)'))/parseInt(env('\1') || '0')/g" lib/constants.ts 2>/dev/null || true
# Add next-runtime-env to package.json if missing (used by lib/constants.ts)
if ! grep -q 'next-runtime-env' package.json 2>/dev/null; then
echo "📦 Adding next-runtime-env to package.json..."
python3 -c "
import json
with open('package.json') as f:
d = json.load(f)
d.setdefault('dependencies', {})['next-runtime-env'] = '^1.6.2'
with open('package.json', 'w') as f:
json.dump(d, f, indent=2)
f.write('\n')
"
echo "✅ Added next-runtime-env"
fi
# Update @atproto/ozone in service/package.json to latest
echo "📦 Updating @atproto/ozone in service/package.json..."
local latest_ozone_ver
latest_ozone_ver=$(npm view @atproto/ozone version 2>/dev/null)
if [ -n "$latest_ozone_ver" ] && [ -f service/package.json ]; then
python3 -c "
import json, sys
ver = sys.argv[1]
with open('service/package.json') as f:
d = json.load(f)
old = d.get('dependencies', {}).get('@atproto/ozone', '')
d.setdefault('dependencies', {})['@atproto/ozone'] = ver
with open('service/package.json', 'w') as f:
json.dump(d, f, indent=2)
f.write('\n')
print(f'✅ @atproto/ozone: {old} -> {ver}')
" "$latest_ozone_ver"
fi
popd > /dev/null popd > /dev/null
} }
function at-repos-feed-generator-newfiles() {
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Creating feed-generator new files..."
# Create app.ts
cat > $d/repos/feed-generator/src/algos/app.ts <<'EOF'
import { QueryParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
import { AppContext } from '../config'
// max 15 chars
export const shortname = 'app'
export const handler = async (ctx: AppContext, params: QueryParams) => {
let builder = ctx.db
.selectFrom('post')
.selectAll()
.orderBy('indexedAt', 'desc')
.orderBy('cid', 'desc')
.limit(params.limit)
if (params.cursor) {
const timeStr = new Date(parseInt(params.cursor, 10)).toISOString()
builder = builder.where('post.indexedAt', '<', timeStr)
}
const res = await builder.execute()
const feed = res.map((row) => ({
post: row.uri,
}))
let cursor: string | undefined
const last = res.at(-1)
if (last) {
cursor = new Date(last.indexedAt).getTime().toString(10)
}
return {
cursor,
feed,
}
}
EOF
echo "✅ Created src/algos/app.ts"
# Restore Dockerfile (removed during patch apply to avoid conflicts)
if [ ! -f $d/repos/feed-generator/Dockerfile ] && [ -f $d/docker/feed/Dockerfile ];then
cp -rf $d/docker/feed/Dockerfile $d/repos/feed-generator/
echo "✅ Restored Dockerfile"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
function at-repos-docker-verify() {
local service=$1
local image="at-${service}:latest"
local check_file=""
case $service in
pds) check_file="/app/services/pds/index.js" ;;
bsky) check_file="/app/services/bsky/api.js" ;;
ozone) check_file="/app/services/ozone/api.js" ;;
bgs) check_file="/relay" ;;
plc) check_file="/app/packages/server/dist/index.js" ;;
*) return 0 ;;
esac
local cid
cid=$(docker create --entrypoint "" "$image" true 2>&1)
if [ $? -ne 0 ]; then
echo " ❌ FAILED: cannot create container from $image"
echo " $cid"
return 1
fi
if docker cp "$cid:$check_file" /tmp/.docker-verify-tmp 2>/dev/null; then
rm -f /tmp/.docker-verify-tmp
docker rm "$cid" > /dev/null 2>&1
echo " ✅ Verified: $check_file exists"
return 0
else
rm -f /tmp/.docker-verify-tmp
docker rm "$cid" > /dev/null 2>&1
echo " ❌ FAILED: $check_file not found in $image"
return 1
fi
}
function at-repos-build-docker-atproto() { function at-repos-build-docker-atproto() {
cd $d cd $d
local failed=() docker image prune -a
if [ -z "$1" ];then if [ -z "$1" ];then
for ((i=1; i<=${#services}; i++)); do for ((i=1; i<=${#services}; i++)); do
service=${services[$i]} service=${services[$i]}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" docker compose build --no-cache $service
echo "🔨 Building: $service"
if ! docker compose build --no-cache $service; then
echo " ❌ Build failed: $service"
failed+=($service)
continue
fi
if ! at-repos-docker-verify $service; then
failed+=($service)
continue
fi
if [ "$service" = "ozone" ]; then
docker compose build --no-cache ${service}-web
fi
done done
else else
echo "🔨 Building: $1" docker compose build --no-cache $1
if ! docker compose build --no-cache $1; then
echo "❌ Build failed: $1"
return 1
fi
if ! at-repos-docker-verify $1; then
return 1
fi
fi fi
if [ ${#failed[@]} -gt 0 ]; then
echo ""
echo "❌ Failed builds: ${failed[*]}"
echo "⚠️ Do NOT push these images."
return 1
fi
echo ""
echo "✅ All builds verified successfully."
} }
function at-repos-push-reset() { function at-repos-push-reset() {
@@ -514,41 +354,26 @@ function at-repos-push-reset() {
} }
function at-repos-push-docker() { function at-repos-push-docker() {
local dtag=$(date +%Y%m%d) if [ -z "$1" ];then
if [ -z "$1" ] || [ "$1" = "push" ]; then for ((i=1; i<=${#services}; i++)); do
for service in "${services[@]}"; do service=${services[$i]}
if ! at-repos-docker-verify $service; then
echo "⚠️ Skipping push: $service (verification failed)"
continue
fi
docker tag at-${service}:latest localhost:${dport}/${service}:latest docker tag at-${service}:latest localhost:${dport}/${service}:latest
docker tag at-${service}:latest localhost:${dport}/${service}:${dtag}
docker push localhost:${dport}/${service}:latest docker push localhost:${dport}/${service}:latest
docker push localhost:${dport}/${service}:${dtag} if [ "$service" == "ozone" ];then
if [ "$service" = "ozone" ]; then
docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest
docker tag at-${service}-web:latest localhost:${dport}/${service}-web:${dtag}
docker push localhost:${dport}/${service}-web:latest docker push localhost:${dport}/${service}-web:latest
docker push localhost:${dport}/${service}-web:${dtag}
fi fi
done done
else else
if ! at-repos-docker-verify $1; then
echo "❌ Push aborted: $1 (verification failed)"
return 1
fi
docker tag at-${1}:latest localhost:${dport}/${1}:latest docker tag at-${1}:latest localhost:${dport}/${1}:latest
docker tag at-${1}:latest localhost:${dport}/${1}:${dtag}
docker push localhost:${dport}/${1}:latest docker push localhost:${dport}/${1}:latest
docker push localhost:${dport}/${1}:${dtag}
fi fi
echo "📦 Pushed with tags: latest, ${dtag}"
} }
function at-repos-pull-docker() { function at-repos-pull-docker() {
cd $d cd $d
docker image prune -a
docker compose up -d --pull always docker compose up -d --pull always
echo "💡 Run 'docker image prune' manually to clean up old images."
} }
function at-repos-reset-bgs-db() { function at-repos-reset-bgs-db() {
@@ -579,55 +404,28 @@ function at-repos-reset-bgs-db() {
echo "⚙️ Updating Slurp Config..." echo "⚙️ Updating Slurp Config..."
docker exec -i $dp psql -U postgres -d bgs -c "UPDATE slurp_configs SET new_subs_disabled = false, new_pds_per_day_limit = 1000 WHERE id = 1;" docker exec -i $dp psql -U postgres -d bgs -c "UPDATE slurp_configs SET new_subs_disabled = false, new_pds_per_day_limit = 1000 WHERE id = 1;"
# host=pds:3000 echo "🔗 Registering Trusted Domain & Resetting Repos..."
echo "🔗 Registering Trusted Domain..."
# Retry loop for addTrustedDomain as BGS might still be warming up # Retry loop for addTrustedDomain as BGS might still be warming up
for i in {1..5}; do for i in {1..5}; do
if curl -f -X POST "https://bgs.${host}/admin/pds/addTrustedDomain?domain=${host}" -H "Authorization: Bearer ${BGS_ADMIN_KEY}"; then if curl -f -X POST "https://bgs.${host}/admin/pds/addTrustedDomain?domain=${host}" -H "Authorization: Bearer ${BGS_ADMIN_KEY}"; then
echo ""
echo "✅ Trusted domain registered" echo "✅ Trusted domain registered"
break break
fi fi
echo "Failed to contact BGS (attempt $i/5)... waiting 5s" echo "Bot failed to contact BGS (attempt $i/5)... waiting 5s"
sleep 5 sleep 5
done done
echo "🔗 Requesting PDS Crawl..."
# Request BGS to crawl the PDS - this registers the PDS and starts subscription
for i in {1..5}; do
result=$(curl -s -X POST "https://bgs.${host}/admin/pds/requestCrawl" \
-H "Authorization: Bearer ${BGS_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d "{\"hostname\":\"${host}\"}" \
-w "%{http_code}" -o /dev/null)
if [ "$result" = "200" ]; then
echo "✅ PDS crawl requested successfully"
break
fi
echo "Failed to request crawl (attempt $i/5, status: $result)... waiting 5s"
sleep 5
done
echo "⏳ Waiting 5s for BGS to connect to PDS..."
sleep 5
echo "🔄 Triggering repo sync for existing users..."
for ((i=1; i<=${#handles}; i++)); do for ((i=1; i<=${#handles}; i++)); do
handle=${handles[$i]} handle=${handles[$i]}
did=$(curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" | jq -r .did) did=`curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" |jq -r .did`
if [ -n "$did" ] && [ "$did" != "null" ]; then if [ ! -z "$did" ] && [ "$did" != "null" ]; then
echo " Syncing repo: $handle ($did)" echo "Resetting repo: $handle ($did)"
# Use takedown=false to trigger a resync without actually taking down curl -X POST "https://bgs.${host}/admin/repo/reset?did=${did}" \
curl -s -X POST "https://bgs.${host}/admin/repo/takedown?did=${did}&takedown=false" \ -H "Authorization: Bearer ${BGS_ADMIN_KEY}"
-H "Authorization: Bearer ${BGS_ADMIN_KEY}" || true
else else
echo " Skipping $handle (DID not found)" echo "Skipping reset for $handle (DID not found)"
fi fi
done done
echo ""
echo "✅ BGS reset complete!"
echo " PDS should now be subscribed and syncing repos."
} }
function at-repos-feed-generator-start-push() { function at-repos-feed-generator-start-push() {
@@ -690,204 +488,20 @@ curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $
https://${host}/xrpc/com.atproto.repo.putRecord https://${host}/xrpc/com.atproto.repo.putRecord
} }
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Patch creation helpers
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Find existing patches that modify the same file
# Usage: at-patch-find-conflicts <filepath> <patch-dir>
function at-patch-find-conflicts() {
local filepath="$1"
local patch_dir="$2"
local conflicts=()
if [ ! -d "$patch_dir" ]; then
return
fi
for pf in "$patch_dir"/*.patch(N) "$patch_dir"/*.diff(N); do
[ -f "$pf" ] || continue
if grep -q "^--- a/$filepath" "$pf" 2>/dev/null || grep -q "^+++ b/$filepath" "$pf" 2>/dev/null; then
conflicts+=("$(basename "$pf")")
fi
done
if [ ${#conflicts[@]} -gt 0 ]; then
echo "⚠️ This file is also modified by:"
for c in "${conflicts[@]}"; do
echo " - $c"
done
echo ""
echo " Ensure patches are applied in order before patch-begin."
echo " Baseline must reflect the post-earlier-patches state."
fi
}
# Save current file state as baseline for patch creation
# Usage: ./install.zsh patch-begin <repo> <file-path> [--ios]
# Example: ./install.zsh patch-begin social-app "src/screens/Profile/Header/ProfileHeaderStandard.tsx" --ios
function at-patch-begin() {
local repo="$1"
local filepath="$2"
local flag="$3"
if [ -z "$repo" ] || [ -z "$filepath" ]; then
echo "Usage: ./install.zsh patch-begin <repo> <file-path> [--ios]"
echo "Example: ./install.zsh patch-begin social-app \"src/screens/Profile/Header/ProfileHeaderStandard.tsx\" --ios"
return 1
fi
local full_path="$d/repos/$repo/$filepath"
if [ ! -f "$full_path" ]; then
echo "❌ File not found: $full_path"
return 1
fi
local tmp_file="/tmp/patch-base--$(echo "$filepath" | tr '/' '-')"
cp "$full_path" "$tmp_file"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Baseline saved: $tmp_file"
echo " File: $full_path"
echo ""
# Check for conflicting patches
if [ "$flag" = "--ios" ]; then
at-patch-find-conflicts "$filepath" "$d/ios/patching"
else
at-patch-find-conflicts "$filepath" "$d/patching"
fi
echo "Next steps:"
echo " 1. Edit: $full_path"
echo " 2. Save: ./install.zsh patch-save <NNN-name.patch> $repo \"$filepath\" ${flag:---ios}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
# Generate patch from baseline diff
# Usage: ./install.zsh patch-save <patch-filename> <repo> <file-path> [--ios]
# Example: ./install.zsh patch-save 042-social-app-ios-feature.patch social-app "src/path/to/file.tsx" --ios
function at-patch-save() {
local patch_filename="$1"
local repo="$2"
local filepath="$3"
local flag="$4"
if [ -z "$patch_filename" ] || [ -z "$repo" ] || [ -z "$filepath" ]; then
echo "Usage: ./install.zsh patch-save <patch-filename> <repo> <file-path> [--ios]"
echo "Example: ./install.zsh patch-save 042-social-app-ios-feature.patch social-app \"src/file.tsx\" --ios"
return 1
fi
local full_path="$d/repos/$repo/$filepath"
local tmp_file="/tmp/patch-base--$(echo "$filepath" | tr '/' '-')"
if [ ! -f "$tmp_file" ]; then
echo "❌ No baseline found. Run 'patch-begin' first."
return 1
fi
if [ ! -f "$full_path" ]; then
echo "❌ File not found: $full_path"
return 1
fi
# Determine output directory
local patch_dir="$d/patching"
if [ "$flag" = "--ios" ]; then
patch_dir="$d/ios/patching"
fi
# Generate diff with proper a/b paths
diff -u "$tmp_file" "$full_path" \
| sed "1s|--- .*|--- a/$filepath|" \
| sed "2s|+++ .*|+++ b/$filepath|" \
> "$patch_dir/$patch_filename"
local line_count
line_count=$(wc -l < "$patch_dir/$patch_filename" | tr -d ' ')
if [ "$line_count" -eq 0 ]; then
echo "⚠️ No differences found. Patch file is empty."
rm "$patch_dir/$patch_filename"
return 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Patch: $patch_dir/$patch_filename ($line_count lines)"
# Dry-run verify: restore baseline, test patch, restore edit
pushd "$d/repos/$repo" > /dev/null
cp "$full_path" /tmp/patch-edited-tmp
cp "$tmp_file" "$full_path"
if patch --dry-run -p1 < "$patch_dir/$patch_filename" > /dev/null 2>&1; then
echo "✅ Dry-run: OK"
else
echo "❌ Dry-run: FAILED"
patch --dry-run -p1 < "$patch_dir/$patch_filename" 2>&1 | head -5
fi
cp /tmp/patch-edited-tmp "$full_path"
rm -f /tmp/patch-edited-tmp
popd > /dev/null
rm -f "$tmp_file"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
# Verify all patches can be applied (full dry-run)
# Usage: ./install.zsh patch-check [--ios]
function at-patch-check() {
local flag="$1"
if [ "$flag" = "--ios" ]; then
echo "Checking iOS patches against: $d/repos/social-app"
pushd "$d/repos/social-app" > /dev/null
for pf in "$d/ios/patching"/*.patch; do
[ -f "$pf" ] || continue
local name="$(basename "$pf")"
if patch --dry-run -p1 < "$pf" > /dev/null 2>&1; then
echo "$name"
else
echo "$name"
fi
done
popd > /dev/null
else
echo "Checking server patches..."
for pf in "$d/patching"/*.patch "$d/patching"/*.diff; do
[ -f "$pf" ] || continue
echo " $(basename "$pf")"
done
fi
}
at-repos-env at-repos-env
case "$1" in case "$1" in
pull) pull)
at-repos-clone at-repos-clone
at-repos-pull at-repos-pull
exit exit
;; ;;
patch) patch)
at-repos-social-app-ios-patch at-repos-social-app-avatar-write
at-repos-patch-apply-all at-repos-patch-apply-all
at-repos-ozone-patch at-repos-ozone-patch
at-repos-feed-generator-newfiles
show-failed-patches show-failed-patches
exit exit
;; ;;
patch-begin)
at-patch-begin "$2" "$3" "$4"
exit
;;
patch-save)
at-patch-save "$2" "$3" "$4" "$5"
exit
;;
patch-check)
at-patch-check "$2"
exit
;;
build) build)
at-repos-build-docker-atproto $2 at-repos-build-docker-atproto $2
exit exit
@@ -913,7 +527,7 @@ esac
case "`cat /etc/hostname`" in case "`cat /etc/hostname`" in
at) at)
if [ "$1" = "bgs-reset" ];then if [ "$1" = "bgs-reset" ];then
# at-repos-reset-bgs-db at-repos-reset-bgs-db
exit exit
fi fi
at-repos-pull-docker at-repos-pull-docker
@@ -922,11 +536,9 @@ case "`cat /etc/hostname`" in
*) *)
at-repos-clone at-repos-clone
at-repos-pull at-repos-pull
at-repos-checkout-pinned at-repos-social-app-avatar-write
at-repos-social-app-ios-patch
at-repos-patch-apply-all at-repos-patch-apply-all
at-repos-ozone-patch at-repos-ozone-patch
at-repos-feed-generator-newfiles
show-failed-patches show-failed-patches
at-repos-build-docker-atproto at-repos-build-docker-atproto
at-repos-push-docker at-repos-push-docker

View File

@@ -2,10 +2,8 @@ APP_NAME="Aiat"
REPO_DIR="../repos/social-app" REPO_DIR="../repos/social-app"
APP_SLUG="aiat" APP_SLUG="aiat"
APP_SCHEME="syui" APP_SCHEME="syui"
APP_GROUP="group.ai.syui.at"
APP_MAIL=user@example.com
APP_KEYCHAIN=@keychain:KEYCHAIN_NAME
BUNDLE_ID="ai.syui.at" BUNDLE_ID="ai.syui.at"
APP_GROUP="group.ai.syui.at"
SERVICE_URL="https://syu.is" SERVICE_URL="https://syu.is"
HELP_URL="https://syu.is/about/support/help" HELP_URL="https://syu.is/about/support/help"
PRIVACY_URL="https://syu.is/about/support/privacy-policy" PRIVACY_URL="https://syu.is/about/support/privacy-policy"
@@ -13,5 +11,3 @@ TERMS_URL="https://syu.is/about/support/tos"
REPO_DIR="../repos/social-app" REPO_DIR="../repos/social-app"
CONFIG_FILE="$REPO_DIR/app.config.js" CONFIG_FILE="$REPO_DIR/app.config.js"
CONSTANTS_FILE="$REPO_DIR/src/lib/constants.ts" CONSTANTS_FILE="$REPO_DIR/src/lib/constants.ts"
IOS_CERTIFICATE_NAME="Apple Distribution: $TEAM($TEAM_ID)"
PDS_HOST=syu.is

0
ios/.keep Normal file
View File

View File

@@ -1,7 +1,72 @@
This document summarizes the key points for the ./ios (social-app) development. 今回の./ios (social-app)開発の要点をまとめます。
1. Comply with the MIT license and ensure it can be published as an iOS app without issues
2. Do not use the "Bluesky" name. Change icons and links accordingly
3. Must work with self-hosted instances
1. MITのライセンスを遵守すること、iosアプリとして出品しても問題ないようにすること
https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICENSE https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICENSE
2. "Bluesky"という名称を使用しないこと。アイコンの変更。リンクの変更
3. selfhostでも動くこと。本来のsocial-appは動きませんので、これは不便なのでiosアプリに出品することにしました。なお、これはすでにpatchで実現しています。
```sh
$ ./install.zsh pull
$ ./install.zsh patch
$ ./ios/setup.zsh
$ ./ios/preview.zsh
```
## 実装済み
1. 最初の画面で、webではちゃんと私のサイトのロゴが表示されていますが、ios モバイル版では、未だにBluesky (icon)です。アカウント作成、サインイン、が表示されています。
2. 上のメニューバーにもBlueskyのロゴが表示されています。
3. サインイン後のホスティングプロバイダーで中身はsyu.isですが、表示は"Bluesky Social"になっています。これをsyu.isに変更してください。ios/webでコードは異なります。
4. チャット機能
チャット機能は今回無効化するので、下メニューバーやプロフィール、設定画面に表示しないでください。
5. 設定ボタン(左カラム)を押すと、フィードバック、ヘルプが表示されますが、非表示にしてください。
6. 設定ボタン(左カラム)を押すと、フィード、リスト、保存済みの項目がありますが、これを削除してください。
7. 設定ボタン(左カラム)を押すと、下に利用規約、プライバシーポリシーが表示されますが、リンクがbsky.socialです。
- /about/support/privacy-policy
- /about/support/tos
このページを独自に作って表示してください。
8. LOG 09:52:20 (logger) Poll latest failed {
"feed": "following",
"message": "Error: Could not find repo: did:plc:z72i7hdynmk6r22z27h6tvur"
}
9. LOG 10:24:03 (metric) router:navigate
LOG 10:24:04 (dms-agent) init failed {
"safeMessage": "could not resolve iss did"
}
9. 設定ボタン(左カラム)の一番下、利用規約やプライバシーポリシーが表示されいてるライセンスという項目を追加。ページを追加して、ライセンスの表示。
https://github.com/bluesky-social/social-app
https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICENSE
10. アカウント作成時(create account)のページに"Having trouble?"で`Contact support`のリンクがありますが、これを削除してください。
11. スタートページ、つまり、`Create account`, `Sign in`があるページの一番下にライセンスページへのリンクを追加してください。また、footerに`© syui`を表示してください。このページのタイトル下にある文字`What's up?`の項目は削除。
12. スタートページのラインセンスリンクが機能しない。おそらくページ変遷に問題があるため。また、ライセンスページは上下が隠れて見えてしまうため、大きく上下に空間を開けること。
13. 利用規約、プライバシーポリシーのページの言語が日本語で書かれています。ラインセンスと同様に、英語を基本とし、日本語訳をその下に表示してください。
14. Settings/ 項目の非表示を追加。
- Helpの非表示
- Aboutのリンクを変更
## 壊れた実装
1. ログイン後のメイン画面、"Following"の項目(フィード)に表示されるものをシンプルにします。表示するのはFollowingのみで、以下のものを削除してください。
- おすすめの削除
- Discoverの削除
- アカウントを探すの削除
2. 年齢保証、年齢確認ページがでてくるのを削除。誕生日を入力する処理を削除。アプリ配布国は限定します。

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M237.9 461.4C237.9 463.4 235.6 465 232.7 465C229.4 465.3 227.1 463.7 227.1 461.4C227.1 459.4 229.4 457.8 232.3 457.8C235.3 457.5 237.9 459.1 237.9 461.4zM206.8 456.9C206.1 458.9 208.1 461.2 211.1 461.8C213.7 462.8 216.7 461.8 217.3 459.8C217.9 457.8 216 455.5 213 454.6C210.4 453.9 207.5 454.9 206.8 456.9zM251 455.2C248.1 455.9 246.1 457.8 246.4 460.1C246.7 462.1 249.3 463.4 252.3 462.7C255.2 462 257.2 460.1 256.9 458.1C256.6 456.2 253.9 454.9 251 455.2zM316.8 72C178.1 72 72 177.3 72 316C72 426.9 141.8 521.8 241.5 555.2C254.3 557.5 258.8 549.6 258.8 543.1C258.8 536.9 258.5 502.7 258.5 481.7C258.5 481.7 188.5 496.7 173.8 451.9C173.8 451.9 162.4 422.8 146 415.3C146 415.3 123.1 399.6 147.6 399.9C147.6 399.9 172.5 401.9 186.2 425.7C208.1 464.3 244.8 453.2 259.1 446.6C261.4 430.6 267.9 419.5 275.1 412.9C219.2 406.7 162.8 398.6 162.8 302.4C162.8 274.9 170.4 261.1 186.4 243.5C183.8 237 175.3 210.2 189 175.6C209.9 169.1 258 202.6 258 202.6C278 197 299.5 194.1 320.8 194.1C342.1 194.1 363.6 197 383.6 202.6C383.6 202.6 431.7 169 452.6 175.6C466.3 210.3 457.8 237 455.2 243.5C471.2 261.2 481 275 481 302.4C481 398.9 422.1 406.6 366.2 412.9C375.4 420.8 383.2 435.8 383.2 459.3C383.2 493 382.9 534.7 382.9 542.9C382.9 549.4 387.5 557.3 400.2 555C500.2 521.8 568 426.9 568 316C568 177.3 455.5 72 316.8 72zM169.2 416.9C167.9 417.9 168.2 420.2 169.9 422.1C171.5 423.7 173.8 424.4 175.1 423.1C176.4 422.1 176.1 419.8 174.4 417.9C172.8 416.3 170.5 415.6 169.2 416.9zM158.4 408.8C157.7 410.1 158.7 411.7 160.7 412.7C162.3 413.7 164.3 413.4 165 412C165.7 410.7 164.7 409.1 162.7 408.1C160.7 407.5 159.1 407.8 158.4 408.8zM190.8 444.4C189.2 445.7 189.8 448.7 192.1 450.6C194.4 452.9 197.3 453.2 198.6 451.6C199.9 450.3 199.3 447.3 197.3 445.4C195.1 443.1 192.1 442.8 190.8 444.4zM179.4 429.7C177.8 430.7 177.8 433.3 179.4 435.6C181 437.9 183.7 438.9 185 437.9C186.6 436.6 186.6 434 185 431.7C183.6 429.4 181 428.4 179.4 429.7z"/></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M453.2 112L523.8 112L369.6 288.2L551 528L409 528L297.7 382.6L170.5 528L99.8 528L264.7 339.5L90.8 112L236.4 112L336.9 244.9L453.2 112zM428.4 485.8L467.5 485.8L215.1 152L173.1 152L428.4 485.8z"/></svg>

Before

Width:  |  Height:  |  Size: 421 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M581.7 188.1C575.5 164.4 556.9 145.8 533.4 139.5C490.9 128 320.1 128 320.1 128C320.1 128 149.3 128 106.7 139.5C83.2 145.8 64.7 164.4 58.4 188.1C47 231 47 320.4 47 320.4C47 320.4 47 409.8 58.4 452.7C64.7 476.3 83.2 494.2 106.7 500.5C149.3 512 320.1 512 320.1 512C320.1 512 490.9 512 533.5 500.5C557 494.2 575.5 476.3 581.8 452.7C593.2 409.8 593.2 320.4 593.2 320.4C593.2 320.4 593.2 231 581.8 188.1zM264.2 401.6L264.2 239.2L406.9 320.4L264.2 401.6z"/></svg>

Before

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -1,212 +0,0 @@
#!/bin/zsh
set -e
SCRIPT_DIR=${0:a:h}
cd "$SCRIPT_DIR"
source .env
function sediment() {
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "$@"
else
sed -i "$@"
fi
}
# 絶対パスに変換
REPO_DIR="$SCRIPT_DIR/../repos/social-app"
APP_NAME="Aiat"
WORKSPACE="$REPO_DIR/ios/${APP_NAME}.xcworkspace"
SCHEME="$APP_NAME"
BUILD_DIR="$REPO_DIR/build"
MOBILEPROVISION="$REPO_DIR/embedded.mobileprovision"
ASSETS_DIR="$SCRIPT_DIR/assets"
echo "Running iOS preview workflow..."
cd "$REPO_DIR"
# 0. Environment Setup (Fix Node Version)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
echo "Checking Node version..."
if command -v nvm >/dev/null; then
nvm use 22 || nvm use 20 || echo "Warning: Could not switch to Node 22/20. Current: $(node -v)"
else
echo "nvm not found, using system node: $(node -v)"
fi
# 1. Install dependencies
echo "1. Installing dependencies (yarn)..."
yarn install
# 1.5. Copy assets
echo "1.5. Copying assets..."
if [ -d "$ASSETS_DIR" ]; then
cp -rf "$ASSETS_DIR/"* "$REPO_DIR/assets/"
echo "✅ Copied all assets (including logo.png, logo-1024.png)"
else
echo "⚠️ Warning: $ASSETS_DIR not found"
fi
function cleanup_build {
# 1.8. Update package.json version (prevent App Store version conflict)
echo "1.8. Updating package.json version..."
if [ -n "$APP_VERSION" ]; then
# Use node to update version in package.json (already in REPO_DIR)
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
pkg.version = '$APP_VERSION';
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
"
echo " ✅ Set version to $APP_VERSION"
else
echo " ⚠️ APP_VERSION not set in .env"
fi
# 1.9. Update buildNumber (CFBundleVersion) with current timestamp
echo "1.9. Updating buildNumber..."
local build_number=$(date +%y%m%d%H%M%S)
sediment "s/buildNumber: '[0-9]*'/buildNumber: '${build_number}'/" "./app.config.js"
echo " ✅ Set buildNumber to $build_number"
# 2. Prebuild (Generate ios directory)
echo "2. Running Expo Prebuild..."
# Clean old ios folder to remove old entitlements/AppClip targets
rm -rf ios
npx expo prebuild --platform ios --clean
# 3. CocoaPods
echo "3. Installing CocoaPods..."
if [ -z "$1" ];then
cd ios
pod install
cd ..
fi
# 4. Signing (Automated)
echo "4. Configuring Xcode Signing..."
XCODE_PROJ="ios/${APP_NAME}.xcodeproj"
if [ ! -d "$XCODE_PROJ" ]; then
XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1)
fi
PBXPROJ="$XCODE_PROJ/project.pbxproj"
# Set DEVELOPMENT_TEAM in pbxproj
if [ -n "$DEVELOPMENT_TEAM" ]; then
echo " Setting DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM"
sediment "s/PRODUCT_BUNDLE_IDENTIFIER = /DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; PRODUCT_BUNDLE_IDENTIFIER = /g" "$PBXPROJ"
sediment "s/DEVELOPMENT_TEAM = \"\";/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ"
sediment "s/DEVELOPMENT_TEAM = ;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ"
fi
# Create/Update entitlements file with App Group
ENTITLEMENTS_FILE="ios/${APP_NAME}/${APP_NAME}.entitlements"
if [ -n "$APP_GROUP" ]; then
echo " Setting APP_GROUP=$APP_GROUP"
cat > "$ENTITLEMENTS_FILE" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>production</string>
<key>com.apple.security.application-groups</key>
<array>
<string>${APP_GROUP}</string>
</array>
</dict>
</plist>
EOF
if ! grep -q "CODE_SIGN_ENTITLEMENTS" "$PBXPROJ"; then
sediment "s/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; CODE_SIGN_ENTITLEMENTS = ${APP_NAME}\\/${APP_NAME}.entitlements;/g" "$PBXPROJ"
fi
fi
echo "✅ Signing configured automatically"
# (Old manual step - commented out)
# open "$XCODE_PROJ"
# read
}
case $1 in
i)
echo "Skipping cleanup_build..."
;;
*)
cleanup_build
;;
esac
echo "Building $APP_NAME for App Store upload..."
# ビルドディレクトリ作成
mkdir -p "$BUILD_DIR"
# アーカイブ(詳細ログ出力)
xcodebuild -workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration Release \
-archivePath "$BUILD_DIR/${APP_NAME}.xcarchive" \
-allowProvisioningUpdates \
DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
archive 2>&1 | tee "$BUILD_DIR/build.log"
# アーカイブ成功確認
if [ ! -d "$BUILD_DIR/${APP_NAME}.xcarchive" ]; then
echo "Error: Archive failed. Check $BUILD_DIR/build.log for details"
exit 1
fi
cd "$BUILD_DIR"
# IPA作成
rm -rf Payload ${APP_NAME}.ipa
mkdir -p Payload
cp -R ${APP_NAME}.xcarchive/Products/Applications/${APP_NAME}.app Payload/
# store.mobileprovisionの存在確認とコピー
# https://developer.apple.com/account/resources/profiles/list
if [ ! -f "$MOBILEPROVISION" ]; then
# 親ディレクトリからコピーを試みる
PARENT_MOBILEPROVISION="$SCRIPT_DIR/../embedded.mobileprovision"
if [ -f "$PARENT_MOBILEPROVISION" ]; then
echo "Copying mobileprovision from $PARENT_MOBILEPROVISION to $MOBILEPROVISION"
cp "$PARENT_MOBILEPROVISION" "$MOBILEPROVISION"
else
echo "Error: store.mobileprovision not found at $MOBILEPROVISION or $PARENT_MOBILEPROVISION"
exit 1
fi
fi
cp "$MOBILEPROVISION" Payload/${APP_NAME}.app/embedded.mobileprovision
# entitlements抽出
security cms -D -i Payload/${APP_NAME}.app/embedded.mobileprovision > /tmp/profile.plist
/usr/libexec/PlistBuddy -x -c "Print :Entitlements" /tmp/profile.plist > /tmp/entitlements.plist
# 署名
CERT="$IOS_CERTIFICATE_NAME"
# Frameworksディレクトリが存在する場合のみ署名
if [ -d "Payload/${APP_NAME}.app/Frameworks" ]; then
for framework in Payload/${APP_NAME}.app/Frameworks/*.framework; do
if [ -e "$framework" ]; then
echo "Signing $framework"
codesign -f -s "$CERT" "$framework"
fi
done
fi
# アプリ本体に署名
codesign -f -s "$CERT" --entitlements /tmp/entitlements.plist Payload/${APP_NAME}.app
# IPA作成
zip -r ${APP_NAME}.ipa Payload
# アップロード
xcrun altool --upload-app -f ${APP_NAME}.ipa -t ios -u "${APP_MAIL}" -p "${APP_KEYCHAIN}"
echo "Upload complete: ${APP_NAME}.ipa"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,70 +1,65 @@
diff --git a/Dockerfile b/Dockerfile
index 371e8402c..2e139503e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -66,7 +66,8 @@ RUN \. "$NVM_DIR/nvm.sh" && \
echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env && \
echo "EXPO_PUBLIC_SENTRY_DSN=$EXPO_PUBLIC_SENTRY_DSN" >> .env && \
npm install --global yarn && \
- yarn && \
+ yarn config set registry https://registry.npmjs.org/ && \
+ yarn install --frozen-lockfile --network-timeout 100000 && \
yarn intl:build 2>&1 | tee i18n.log && \
if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compile errors!\n\n"; fi && \
SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN SENTRY_RELEASE=$EXPO_PUBLIC_RELEASE_VERSION SENTRY_DIST=$EXPO_PUBLIC_BUNDLE_IDENTIFIER yarn build-web
diff --git a/app.config.js b/app.config.js
index 246d8abd3..a6582864b 100644
--- a/app.config.js --- a/app.config.js
+++ b/app.config.js +++ b/app.config.js
@@ -23,10 +23,7 @@ @@ -33,8 +33,8 @@ 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 ? [] : []),
]
@@ -38,27 +35,25 @@
return { return {
expo: { expo: {
version: VERSION, version: VERSION,
- name: 'Bluesky', - name: 'Bluesky',
- slug: 'bluesky', - slug: 'bluesky',
- scheme: 'bluesky',
+ name: 'Aiat', + name: 'Aiat',
+ slug: 'aiat', + slug: 'aiat',
+ scheme: 'syui', scheme: 'bluesky',
owner: 'blueskysocial', owner: 'blueskysocial',
runtimeVersion: { runtimeVersion: {
policy: 'appVersion', @@ -45,15 +45,20 @@ module.exports = function (_config) {
},
- icon: './assets/app-icons/ios_icon_default_next.png',
+ icon: './assets/app-icon.png',
userInterfaceStyle: 'automatic',
primaryColor: '#1083fe', primaryColor: '#1083fe',
newArchEnabled: false, newArchEnabled: false,
ios: { ios: {
+ infoPlist: {
+ NSAppTransportSecurity: {
+ NSAllowsArbitraryLoads: true,
+ },
+ },
supportsTablet: false, supportsTablet: false,
- bundleIdentifier: 'xyz.blueskyweb.app', - bundleIdentifier: 'xyz.blueskyweb.app',
+ bundleIdentifier: 'ai.syui.at', + bundleIdentifier: 'ai.syui.at',
+ buildNumber: '__BUILD_NUMBER__',
config: { config: {
usesNonExemptEncryption: false, usesNonExemptEncryption: false,
}, },
- icon: icon:
- PLATFORM === 'web' // web build doesn't like .icon files PLATFORM === 'web' // web build doesn't like .icon files
- ? './assets/app-icons/ios_icon_default_next.png' ? './assets/app-icons/ios_icon_default_next.png'
- : './assets/app-icons/ios_icon_default.icon', - : './assets/app-icons/ios_icon_default.icon',
+ icon: './assets/app-icon.png', + : './assets/app-icons/ios_icon_default_next.png',
infoPlist: { infoPlist: {
UIBackgroundModes: ['remote-notification'], UIBackgroundModes: ['remote-notification'],
NSCameraUsageDescription: NSCameraUsageDescription:
@@ -118,7 +113,7 @@ @@ -113,7 +118,7 @@ module.exports = function (_config) {
entitlements: { entitlements: {
'com.apple.developer.kernel.increased-memory-limit': true, 'com.apple.developer.kernel.increased-memory-limit': true,
'com.apple.developer.kernel.extended-virtual-addressing': true, 'com.apple.developer.kernel.extended-virtual-addressing': true,
- 'com.apple.security.application-groups': 'group.app.bsky', - 'com.apple.security.application-groups': 'group.app.bsky',
+ 'com.apple.security.application-groups': 'group.ai.syui.at', + 'com.apple.security.application-groups': 'group.ai.syui.at',
// 'com.apple.developer.device-information.user-assigned-device-name': true,
}, },
privacyManifests: { privacyManifests: {
@@ -181,14 +176,14 @@ NSPrivacyCollectedDataTypes: [
barStyle: 'light-content', @@ -182,7 +187,7 @@ module.exports = function (_config) {
},
android: {
- icon: './assets/app-icons/android_icon_default_next.png',
+ icon: './assets/app-icon.png',
adaptiveIcon: {
foregroundImage: './assets/icon-android-foreground.png',
monochromeImage: './assets/icon-android-monochrome.png',
backgroundColor: '#006AFF', backgroundColor: '#006AFF',
}, },
googleServicesFile: './google-services.json', googleServicesFile: './google-services.json',
@@ -73,16 +68,28 @@
intentFilters: [ intentFilters: [
{ {
action: 'VIEW', action: 'VIEW',
@@ -196,7 +191,7 @@ @@ -220,7 +225,7 @@ module.exports = function (_config) {
data: [ checkAutomatically: 'NEVER',
{ },
scheme: 'https', plugins: [
- host: 'bsky.app', - 'expo-video',
+ host: 'syu.is', + 'expo-video', './plugins/withCodeSignEntitlements.js',
}, 'expo-localization',
...(IS_DEV 'expo-web-browser',
? [ [
@@ -280,7 +275,6 @@ @@ -239,6 +244,11 @@ module.exports = function (_config) {
'expo-build-properties',
{
ios: {
+ infoPlist: {
+ NSAppTransportSecurity: {
+ NSAllowsArbitraryLoads: true,
+ },
+ },
deploymentTarget: '15.1',
buildReactNativeFromSource: true,
},
@@ -264,7 +274,6 @@ module.exports = function (_config) {
networkInstrumentation: true, networkInstrumentation: true,
}, },
], ],
@@ -90,44 +97,54 @@
'./plugins/withGradleJVMHeapSizeIncrease.js', './plugins/withGradleJVMHeapSizeIncrease.js',
'./plugins/withAndroidManifestLargeHeapPlugin.js', './plugins/withAndroidManifestLargeHeapPlugin.js',
'./plugins/withAndroidManifestFCMIconPlugin.js', './plugins/withAndroidManifestFCMIconPlugin.js',
@@ -288,8 +282,6 @@ @@ -296,6 +305,11 @@ module.exports = function (_config) {
'./plugins/withAndroidStylesAccentColorPlugin.js', 'expo-splash-screen',
'./plugins/withAndroidDayNightThemePlugin.js',
'./plugins/withAndroidNoJitpackPlugin.js',
- './plugins/shareExtension/withShareExtensions.js',
- './plugins/notificationsExtension/withNotificationsExtension.js',
[
'expo-font',
{ {
@@ -417,30 +409,7 @@ ios: {
+ infoPlist: {
+ NSAppTransportSecurity: {
+ NSAllowsArbitraryLoads: true,
+ },
+ },
enableFullScreenImage_legacy: true,
backgroundColor: '#ffffff',
image: './assets/splash.png',
@@ -394,29 +408,30 @@ module.exports = function (_config) {
build: { build: {
experimental: { experimental: {
ios: { ios: {
- appExtensions: [ + infoPlist: {
- { + NSAppTransportSecurity: {
- targetName: 'Share-with-Bluesky', + NSAllowsArbitraryLoads: true,
+ },
+ },
appExtensions: [
{
targetName: 'Share-with-Bluesky',
- bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky', - bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
- entitlements: { + bundleIdentifier: 'ai.syui.at.Share-with-Bluesky',
- 'com.apple.security.application-groups': [ entitlements: {
'com.apple.security.application-groups': [
- 'group.app.bsky', - 'group.app.bsky',
- ], + 'group.ai.syui.at',
- }, ],
- }, },
- { },
- targetName: 'BlueskyNSE', {
targetName: 'BlueskyNSE',
- bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE', - bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE',
- entitlements: { + bundleIdentifier: 'ai.syui.at.BlueskyNSE',
- 'com.apple.security.application-groups': [ entitlements: {
'com.apple.security.application-groups': [
- 'group.app.bsky', - 'group.app.bsky',
- ], + 'group.ai.syui.at',
- }, ],
- }, },
},
- { - {
- targetName: 'BlueskyClip', - targetName: 'BlueskyClip',
- bundleIdentifier: 'xyz.blueskyweb.app.AppClip', - bundleIdentifier: 'xyz.blueskyweb.app.AppClip',
- }, - },
- ], ],
+ appExtensions: [],
}, },
}, },
},

View File

@@ -1,27 +1,5 @@
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 diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 231447b4f..a44b3da05 100644 index 231447b4f..33c51cc0a 100644
--- a/src/lib/constants.ts --- a/src/lib/constants.ts
+++ b/src/lib/constants.ts +++ b/src/lib/constants.ts
@@ -7,12 +7,12 @@ import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env' @@ -7,12 +7,12 @@ import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
@@ -38,54 +16,11 @@ index 231447b4f..a44b3da05 100644
-const HELP_DESK_LANG = 'en-us' -const HELP_DESK_LANG = 'en-us'
-export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}` -export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
+const HELP_DESK_LANG = 'ja-jp' +const HELP_DESK_LANG = 'ja-jp'
+export const HELP_DESK_URL = '/support/license' +export const HELP_DESK_URL = 'https://syu.is/help'
export const EMBED_SERVICE = 'https://embed.bsky.app' export const EMBED_SERVICE = 'https://embed.bsky.app'
export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download' export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download'
@@ -79,19 +79,17 @@ export function IS_PROD_SERVICE(url?: string) { @@ -209,8 +209,8 @@ export const urls = {
}
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 = {
}, },
} }
@@ -96,32 +31,41 @@ index 231447b4f..a44b3da05 100644
export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev' export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
@@ -236,8 +234,8 @@ export const BLUESKY_MOD_SERVICE_HEADERS = { @@ -236,8 +236,8 @@ export const BLUESKY_MOD_SERVICE_HEADERS = {
} }
export const webLinks = { export const webLinks = {
- tos: `https://bsky.social/about/support/tos`, - tos: `https://bsky.social/about/support/tos`,
- privacy: `https://bsky.social/about/support/privacy-policy`, - privacy: `https://bsky.social/about/support/privacy-policy`,
+ tos: `/support/tos`, + tos: `https://syu.is/about/support/tos`,
+ privacy: `/support/privacy-policy`, + privacy: `https://syu.is/about/support/privacy-policy`,
community: `https://bsky.social/about/support/community-guidelines`, community: `https://bsky.social/about/support/community-guidelines`,
communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`, communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`,
} }
diff --git a/src/lib/demo.ts b/src/lib/demo.ts diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
index 5ead62c9d..7c80dfe15 100644 index 860e841eb..a595b0868 100644
--- a/src/lib/demo.ts --- a/src/lib/statsig/statsig.tsx
+++ b/src/lib/demo.ts +++ b/src/lib/statsig/statsig.tsx
@@ -1,7 +1,7 @@ @@ -265,6 +265,7 @@ export async function tryFetchGates(
import {type AppBskyFeedGetFeed} from '@atproto/api' }
import {subDays, subMinutes} from 'date-fns'
-const DID = `did:plc:z72i7hdynmk6r22z27h6tvur` export function initialize() {
+const DID = `did:plc:6qyecktefllvenje24fcxnie` + if (!SDK_KEY) return Promise.resolve()
const NOW = new Date() return Statsig.initialize(SDK_KEY, null, createStatsigOptions([]))
const POST_1_DATE = subMinutes(NOW, 2).toISOString() }
const POST_2_DATE = subMinutes(NOW, 4).toISOString()
@@ -309,6 +310,9 @@ export function Provider({children}: {children: React.ReactNode}) {
return () => clearInterval(id)
}, [handleIntervalTick])
+ if (!SDK_KEY) {
+ return <GateCache.Provider value={gateCache}>{children}</GateCache.Provider>
+ }
return (
<GateCache.Provider value={gateCache}>
<StatsigProvider
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
index 6088e2806..0f6787a4d 100644 index 6088e2806..7c903763d 100644
--- a/src/lib/strings/url-helpers.ts --- a/src/lib/strings/url-helpers.ts
+++ b/src/lib/strings/url-helpers.ts +++ b/src/lib/strings/url-helpers.ts
@@ -53,7 +53,7 @@ export function toNiceDomain(url: string): string { @@ -53,7 +53,7 @@ export function toNiceDomain(url: string): string {
@@ -133,85 +77,3 @@ index 6088e2806..0f6787a4d 100644
} }
return urlp.host ? urlp.host : url return urlp.host ? urlp.host : url
} catch (e) { } 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

@@ -1,51 +1,5 @@
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 diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index 8a9e51a33..65d643b89 100644 index 8a9e51a33..cb0eb7b71 100644
--- a/src/view/com/util/UserAvatar.tsx --- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx
@@ -444,7 +444,7 @@ let EditableUserAvatar = ({ @@ -444,7 +444,7 @@ let EditableUserAvatar = ({
@@ -57,20 +11,17 @@ index 8a9e51a33..65d643b89 100644
accessibilityRole="image" accessibilityRole="image"
/> />
) : ( ) : (
@@ -618,9 +618,8 @@ export {PreviewableUserAvatar} @@ -619,7 +619,7 @@ export {PreviewableUserAvatar}
// manually string-replace to use the smaller ones
// -prf // -prf
function hackModifyThumbnailPath(uri: string, isEnabled: boolean): string { function hackModifyThumbnailPath(uri: string, isEnabled: boolean): string {
- return isEnabled return isEnabled
- ? uri.replace('/img/avatar/plain/', '/img/avatar_thumbnail/plain/') - ? uri.replace('/img/avatar/plain/', '/img/avatar_thumbnail/plain/')
- : uri + ? uri.replace('https://cdn.web.syu.is/img/avatar/plain/', 'https://bsky.syu.is/img/avatar/plain/')
+ // syu.is: avatars are served directly from bsky.syu.is, no CDN transformation needed : uri
+ return uri
} }
const styles = StyleSheet.create({
diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx
index d7208df13..2763800ac 100644 index d7208df13..b711f71c7 100644
--- a/src/view/icons/Logo.tsx --- a/src/view/icons/Logo.tsx
+++ b/src/view/icons/Logo.tsx +++ b/src/view/icons/Logo.tsx
@@ -1,75 +1,17 @@ @@ -1,75 +1,17 @@
@@ -153,7 +104,7 @@ index d7208df13..2763800ac 100644
- /> - />
- </Svg> - </Svg>
+ <Image + <Image
+ source={require('../../../assets/logo.png')} + source={require('../../../assets/icon.png')}
+ style={[{width: size, height: size}, flatten(style)]} + style={[{width: size, height: size}, flatten(style)]}
+ contentFit="contain" + contentFit="contain"
+ accessibilityLabel="Logo" + accessibilityLabel="Logo"

View File

@@ -1,75 +1,72 @@
diff --git a/src/App.native.tsx b/src/App.native.tsx diff --git a/src/App.native.tsx b/src/App.native.tsx
index 2c4d6fa41..b69e2b18d 100644 index fb3008627..539ebc055 100644
--- a/src/App.native.tsx --- a/src/App.native.tsx
+++ b/src/App.native.tsx +++ b/src/App.native.tsx
@@ -95,7 +95,7 @@ if (isAndroid) { @@ -92,7 +92,7 @@ if (isAndroid) {
* Begin geolocation ASAP * Begin geolocation ASAP
*/ */
Geo.resolve() Geo.resolve()
-prefetchAgeAssuranceConfig() -prefetchAgeAssuranceConfig()
+// // // prefetchAgeAssuranceConfig() +// // // prefetchAgeAssuranceConfig()
prefetchLiveEvents()
function InnerApp() { function InnerApp() {
const [isReady, setIsReady] = React.useState(false)
diff --git a/src/routes.ts b/src/routes.ts diff --git a/src/routes.ts b/src/routes.ts
index f325539c7..3e2c7b3eb 100644 index 1ed913bb2..c80340edb 100644
--- a/src/routes.ts --- a/src/routes.ts
+++ b/src/routes.ts +++ b/src/routes.ts
@@ -72,9 +72,11 @@ export const router = new Router<AllNavigatableRoutes>({ @@ -71,8 +71,8 @@ export const router = new Router<AllNavigatableRoutes>({
FindContactsSettings: '/settings/find-contacts', MiscellaneousNotificationSettings: '/settings/notifications/miscellaneous',
// support // support
Support: '/support', Support: '/support',
- PrivacyPolicy: '/support/privacy', - PrivacyPolicy: '/support/privacy',
- TermsOfService: '/support/tos', - TermsOfService: '/support/tos',
+ PrivacyPolicy: ['/support/privacy-policy', '/about/support/privacy-policy'], + PrivacyPolicy: 'https://syu.is/privacy',
+ TermsOfService: ['/support/tos', '/about/support/tos'], + TermsOfService: 'https://syu.is/terms',
CommunityGuidelines: '/support/community-guidelines', CommunityGuidelines: '/support/community-guidelines',
+ License: ['/support/license', '/about/support/license'],
+ AppInfo: '/support/app',
CopyrightPolicy: '/support/copyright', CopyrightPolicy: '/support/copyright',
// hashtags // hashtags
Hashtag: '/hashtag/:tag',
diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts
index 5c8ce3b97..ee85beb08 100644 index 5c8ce3b97..ee85beb08 100644
--- a/src/state/session/agent.ts --- a/src/state/session/agent.ts
+++ b/src/state/session/agent.ts +++ b/src/state/session/agent.ts
@@ -47,7 +47,8 @@ export function createPublicAgent() { @@ -47,7 +47,8 @@ export function createPublicAgent() {
configureModerationForGuest() // Side effect but only relevant for tests configureModerationForGuest() // Side effect but only relevant for tests
const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE}) const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE})
- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments + // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent return agent
} }
@@ -88,7 +89,8 @@ export async function createAgentAndResume( @@ -88,7 +89,8 @@ export async function createAgentAndResume(
// after session is attached // after session is attached
const aa = prefetchAgeAssuranceData({agent}) const aa = prefetchAgeAssuranceData({agent})
- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments + // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent.prepare({ return agent.prepare({
resolvers: [gates, moderation, aa], resolvers: [gates, moderation, aa],
@@ -127,7 +129,8 @@ export async function createAgentAndLogin( @@ -127,7 +129,8 @@ export async function createAgentAndLogin(
const moderation = configureModerationForAccount(agent, account) const moderation = configureModerationForAccount(agent, account)
const aa = prefetchAgeAssuranceData({agent}) const aa = prefetchAgeAssuranceData({agent})
- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments + // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent.prepare({ return agent.prepare({
resolvers: [gates, moderation, aa], resolvers: [gates, moderation, aa],
@@ -299,7 +302,8 @@ export async function createAgentAndCreateAccount( @@ -299,7 +302,8 @@ export async function createAgentAndCreateAccount(
logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`}) logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`})
} }
- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - agent.configureProxy(BLUESKY_PROXY_HEADER.get())
+ // Disable proxy for self-hosted environments + // Disable proxy for self-hosted environments
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
return agent.prepare({ return agent.prepare({
resolvers: [gates, moderation, aa], resolvers: [gates, moderation, aa],

View File

@@ -1,47 +1,460 @@
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 6b8257b91..35202224b 100644
--- a/src/screens/Settings/AboutSettings.tsx --- a/src/screens/Settings/AboutSettings.tsx
+++ b/src/screens/Settings/AboutSettings.tsx +++ b/src/screens/Settings/AboutSettings.tsx
@@ -79,7 +79,7 @@ @@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
<Layout.Content> <Layout.Content>
<SettingsList.Container> <SettingsList.Container>
<SettingsList.LinkItem <SettingsList.LinkItem
- to="https://bsky.social/about/support/tos" - to="https://bsky.social/about/support/tos"
+ to="/support/tos" + to="https://syu.is/about/support/tos"
label={_(msg`Terms of Service`)}> label={_(msg`Terms of Service`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} /> <SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText> <SettingsList.ItemText>
@@ -87,7 +87,7 @@ @@ -88,7 +88,7 @@ export function AboutSettingsScreen({}: Props) {
</SettingsList.ItemText> </SettingsList.ItemText>
</SettingsList.LinkItem> </SettingsList.LinkItem>
<SettingsList.LinkItem <SettingsList.LinkItem
- to="https://bsky.social/about/support/privacy-policy" - to="https://bsky.social/about/support/privacy-policy"
+ to="/support/privacy-policy" + to="https://syu.is/about/support/privacy-policy"
label={_(msg`Privacy Policy`)}> label={_(msg`Privacy Policy`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} /> <SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText> <SettingsList.ItemText>
diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx
index dd319a4c6..0e80f956a 100644
--- a/src/screens/Takendown.tsx --- a/src/screens/Takendown.tsx
+++ b/src/screens/Takendown.tsx +++ b/src/screens/Takendown.tsx
@@ -210,10 +210,10 @@ @@ -223,11 +223,11 @@ export function Takendown() {
<Trans> <Trans>
Your account was found to be in violation of the{' '} Your account was found to be in violation of the{' '}
<SimpleInlineLinkText <InlineLinkText
- label={_(msg`Bluesky Social Terms of Service`)} - label={_(msg`Bluesky Social Terms of Service`)}
- to="https://bsky.social/about/support/tos" - to="https://bsky.social/about/support/tos"
+ label={_(msg`syu.is Terms of Service`)} + label={_(msg`syu.is Terms of Service`)}
+ to="/support/tos" + to="https://syu.is/about/support/tos"
style={[a.text_md, a.leading_snug]}> style={[a.text_md, a.leading_normal]}
overridePresentation>
- Bluesky Social Terms of Service - Bluesky Social Terms of Service
+ syu.is Terms of Service + syu.is Terms of Service
</SimpleInlineLinkText> </InlineLinkText>
. You have been sent an email outlining the specific violation . You have been sent an email outlining the specific violation
and suspension period, if applicable. You can appeal this 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..0f583c915 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,60 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
import * as Layout from '#/components/Layout'
import {useDemoMode} from '#/storage/hooks/demo-mode'
+const DEFAULT_PINNED_FEEDS = [{
+ feedDescriptor: 'following',
+ displayName: 'Following',
+ id: 'following',
+ type: 'feed',
+ savedFeed: undefined,
+ pinned: true,
+}]
+
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
+ const safePinnedFeedInfos = 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 +90,43 @@ function HomeScreenReady({
const {hasSession} = useSession()
const setMinimalShellMode = useSetMinimalShellMode()
- 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]
-
- // 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],
- )
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode]))
+
+ const onPageSelected = React.useCallback((index) => {
+ setMinimalShellMode(false)
+ const maybeFeed = allFeeds[index]
+ lastPagerReportedIndexRef.current = index
+ setSelectedFeed(maybeFeed)
+ }, [setSelectedFeed, setMinimalShellMode, allFeeds])
+
+ 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: 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 renderCustomFeedEmptyState = React.useCallback(() => {
- return <CustomFeedEmptyState />
- }, [])
-
- 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) => {
+ const renderTabBar = React.useCallback((props) => {
+ return <HomeHeader key="FEEDS_TAB_BAR" {...props} testID="homeScreenFeedTabs" onPressSelected={onPressSelected} feeds={pinnedFeedInfos} />
+ }, [onPressSelected, pinnedFeedInfos])
+
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, [])
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
+
+ const homeFeedParams = React.useMemo(() => ({
+ mergeFeedEnabled: false, mergeFeedSources: []
+ }), [preferences])
+
+ 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..228af4966 100644
--- a/src/view/screens/PrivacyPolicy.tsx --- a/src/view/screens/PrivacyPolicy.tsx
+++ b/src/view/screens/PrivacyPolicy.tsx +++ b/src/view/screens/PrivacyPolicy.tsx
@@ -1,52 +1,49 @@ @@ -1,52 +1,13 @@
import React from 'react' import React from 'react'
-import {View} from 'react-native' -import {View} from 'react-native'
-import {msg} from '@lingui/core/macro' -import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react' -import {useLingui} from '@lingui/react'
-import {Trans} from '@lingui/react/macro'
-import {useFocusEffect} from '@react-navigation/native' -import {useFocusEffect} from '@react-navigation/native'
- -
-import {usePalette} from '#/lib/hooks/usePalette' -import {usePalette} from '#/lib/hooks/usePalette'
@@ -54,28 +467,25 @@
-import {TextLink} from '#/view/com/util/Link' -import {TextLink} from '#/view/com/util/Link'
-import {Text} from '#/view/com/util/text/Text' -import {Text} from '#/view/com/util/text/Text'
-import {ScrollView} from '#/view/com/util/Views' -import {ScrollView} from '#/view/com/util/Views'
+import {ScrollView} from 'react-native' +import { WebView } from 'react-native-webview'
import * as Layout from '#/components/Layout' import * as Layout from '#/components/Layout'
-import {ViewHeader} from '../com/util/ViewHeader' -import {ViewHeader} from '../com/util/ViewHeader'
+import {useSetTitle} from '#/lib/hooks/useSetTitle' -
+import {atoms as a, useTheme} from '#/alf'
+import {Text} from '#/components/Typography'
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'> -type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'>
-export const PrivacyPolicyScreen = (_props: Props) => { -export const PrivacyPolicyScreen = (_props: Props) => {
- const pal = usePalette('default') - const pal = usePalette('default')
- const {_} = useLingui() - const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode() - const setMinimalShellMode = useSetMinimalShellMode()
+export function PrivacyPolicyScreen() { -
+ useSetTitle('Privacy Policy')
+ const t = useTheme()
- useFocusEffect( - useFocusEffect(
- React.useCallback(() => { - React.useCallback(() => {
- setMinimalShellMode(false) - setMinimalShellMode(false)
- }, [setMinimalShellMode]), - }, [setMinimalShellMode]),
- ) - )
- +import {useSetTitle} from '#/lib/hooks/useSetTitle'
+export function PrivacyPolicyScreen() {
+ useSetTitle('Privacy Policy')
return ( return (
<Layout.Screen> <Layout.Screen>
- <ViewHeader title={_(msg`Privacy Policy`)} /> - <ViewHeader title={_(msg`Privacy Policy`)} />
@@ -93,50 +503,20 @@
- </Text> - </Text>
- </View> - </View>
- <View style={s.footerSpacer} /> - <View style={s.footerSpacer} />
+ <ScrollView - </ScrollView>
+ style={[a.flex_1, {backgroundColor: t.palette.white}]} + <WebView source={{ uri: 'https://syu.is/privacy' }} style={{ flex: 1 }} />
+ contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
+ <Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>Privacy Policy</Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Data Collection</Text>
+ <Text style={[a.mb_md]}>
+ syu.is collects minimal data necessary to provide the service. This includes your account information, posts, and interactions on the AT Protocol network.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Data Storage</Text>
+ <Text style={[a.mb_md]}>
+ Your data is stored on the AT Protocol network. Posts and profile information are public by default as part of the decentralized social network.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Third Parties</Text>
+ <Text style={[a.mb_md]}>
+ We do not sell your personal information to third parties. Your data may be visible to other users and services on the AT Protocol network.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Contact</Text>
+ <Text style={[a.mb_md]}>
+ For privacy-related questions, please contact the administrator.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_xl, a.mb_md]}>日本語</Text>
+ <Text style={[a.mb_md]}>
+ syu.isはサービス提供に必要な最小限のデータのみを収集します。投稿やプロフィール情報はAT Protocolネットワーク上で公開されます。個人情報を第三者に販売することはありません。
+ </Text>
+
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
+ Last updated: 2026
+ </Text>
</ScrollView>
</Layout.Screen> </Layout.Screen>
) )
}
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
index d843c713c..c0b34c886 100644
--- a/src/view/screens/TermsOfService.tsx --- a/src/view/screens/TermsOfService.tsx
+++ b/src/view/screens/TermsOfService.tsx +++ b/src/view/screens/TermsOfService.tsx
@@ -1,50 +1,49 @@ @@ -1,50 +1,13 @@
import React from 'react' import React from 'react'
-import {View} from 'react-native' -import {View} from 'react-native'
-import {msg} from '@lingui/core/macro' -import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react' -import {useLingui} from '@lingui/react'
-import {Trans} from '@lingui/react/macro'
-import {useFocusEffect} from '@react-navigation/native' -import {useFocusEffect} from '@react-navigation/native'
- -
-import {usePalette} from '#/lib/hooks/usePalette' -import {usePalette} from '#/lib/hooks/usePalette'
@@ -149,28 +529,25 @@
-import {TextLink} from '#/view/com/util/Link' -import {TextLink} from '#/view/com/util/Link'
-import {Text} from '#/view/com/util/text/Text' -import {Text} from '#/view/com/util/text/Text'
-import {ScrollView} from '#/view/com/util/Views' -import {ScrollView} from '#/view/com/util/Views'
+import {ScrollView} from 'react-native' +import { WebView } from 'react-native-webview'
import * as Layout from '#/components/Layout' import * as Layout from '#/components/Layout'
-import {ViewHeader} from '../com/util/ViewHeader' -import {ViewHeader} from '../com/util/ViewHeader'
+import {useSetTitle} from '#/lib/hooks/useSetTitle' -
+import {atoms as a, useTheme} from '#/alf'
+import {Text} from '#/components/Typography'
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'> -type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
-export const TermsOfServiceScreen = (_props: Props) => { -export const TermsOfServiceScreen = (_props: Props) => {
- const pal = usePalette('default') - const pal = usePalette('default')
- const setMinimalShellMode = useSetMinimalShellMode() - const setMinimalShellMode = useSetMinimalShellMode()
- const {_} = useLingui() - const {_} = useLingui()
+export function TermsOfServiceScreen() { -
+ useSetTitle('Terms of Service')
+ const t = useTheme()
- useFocusEffect( - useFocusEffect(
- React.useCallback(() => { - React.useCallback(() => {
- setMinimalShellMode(false) - setMinimalShellMode(false)
- }, [setMinimalShellMode]), - }, [setMinimalShellMode]),
- ) - )
- +import {useSetTitle} from '#/lib/hooks/useSetTitle'
+export function TermsOfServiceScreen() {
+ useSetTitle('Terms of Service')
return ( return (
<Layout.Screen> <Layout.Screen>
- <ViewHeader title={_(msg`Terms of Service`)} /> - <ViewHeader title={_(msg`Terms of Service`)} />
@@ -186,39 +563,8 @@
- </Text> - </Text>
- </View> - </View>
- <View style={s.footerSpacer} /> - <View style={s.footerSpacer} />
+ <ScrollView - </ScrollView>
+ style={[a.flex_1, {backgroundColor: t.palette.white}]} + <WebView source={{ uri: 'https://syu.is/terms' }} style={{ flex: 1 }} />
+ contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
+ <Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>Terms of Service</Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Acceptance</Text>
+ <Text style={[a.mb_md]}>
+ By using syu.is, you agree to these terms. If you do not agree, please do not use the service.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Prohibited Content</Text>
+ <Text style={[a.mb_md]}>
+ Do not post illegal content, spam, or harass others. Do not impersonate others or spread misinformation.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Account Termination</Text>
+ <Text style={[a.mb_md]}>
+ The administrator reserves the right to suspend or terminate accounts that violate these terms.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Disclaimer</Text>
+ <Text style={[a.mb_md]}>
+ This service is provided "as is" without warranty of any kind.
+ </Text>
+
+ <Text style={[a.text_lg, a.font_bold, a.mt_xl, a.mb_md]}>日本語</Text>
+ <Text style={[a.mb_md]}>
+ syu.isを利用することで、これらの利用規約に同意したものとみなします。違法なコンテンツの投稿、スパム、他者への嫌がらせは禁止です。管理者は規約違反のアカウントを停止する権利を有します。本サービスは現状のまま提供され、いかなる保証もありません。
+ </Text>
+
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
+ Last updated: 2026
+ </Text>
</ScrollView>
</Layout.Screen> </Layout.Screen>
) )
}

View File

@@ -1,8 +1,311 @@
diff --git a/src/components/dialogs/BirthDateSettings.tsx b/src/components/dialogs/BirthDateSettings.tsx
index 9915d0a2d..c200a7c67 100644
--- a/src/components/dialogs/BirthDateSettings.tsx
+++ b/src/components/dialogs/BirthDateSettings.tsx
@@ -163,7 +163,7 @@ function BirthdayInner({
<Trans>
You must be at least 13 years old to use Bluesky. Read our{' '}
<SimpleInlineLinkText
- to="https://bsky.social/about/support/tos"
+ to="https://syu.is/about/support/tos"
label={_(msg`Terms of Service`)}>
Terms of Service
</SimpleInlineLinkText>{' '}
diff --git a/src/components/dialogs/ServerInput.tsx b/src/components/dialogs/ServerInput.tsx
index d7c02bb9f..fda1dfe4a 100644
--- a/src/components/dialogs/ServerInput.tsx
+++ b/src/components/dialogs/ServerInput.tsx
@@ -165,7 +165,7 @@ function DialogInner({
<Trans>
Bluesky is an open network where you can choose your own
provider. If you're new here, we recommend sticking with the
- default Bluesky Social option.
+ default syu.is option.
</Trans>
</Admonition>
</View>
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index 42a5fe417..8e7963512 100644 index ed2a6cfb7..2f387b4a8 100644
--- a/src/view/shell/Drawer.tsx --- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx
@@ -294,17 +294,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { @@ -1,60 +1,50 @@
-import React, {type ComponentProps, type JSX} from 'react'
-import {Linking, ScrollView, TouchableOpacity, View} from 'react-native'
-import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import {msg, Plural, plural, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {StackActions, useNavigation} from '@react-navigation/native'
-
-import {useActorStatus} from '#/lib/actor-status'
-import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
-import {type PressableScale} from '#/lib/custom-animations/PressableScale'
-import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
-import {getTabState, TabState} from '#/lib/routes/helpers'
-import {type NavigationProp} from '#/lib/routes/types'
-import {sanitizeHandle} from '#/lib/strings/handles'
-import {colors} from '#/lib/styles'
-import {isWeb} from '#/platform/detection'
-import {emitSoftReset} from '#/state/events'
-import {useKawaiiMode} from '#/state/preferences/kawaii'
-import {useUnreadNotifications} from '#/state/queries/notifications/unread'
-import {useProfileQuery} from '#/state/queries/profile'
-import {type SessionAccount, useSession} from '#/state/session'
-import {useSetDrawerOpen} from '#/state/shell'
-import {formatCount} from '#/view/com/util/numeric/format'
-import {UserAvatar} from '#/view/com/util/UserAvatar'
-import {NavSignupCard} from '#/view/shell/NavSignupCard'
-import {atoms as a, tokens, useTheme, web} from '#/alf'
-import {Button, ButtonIcon, ButtonText} from '#/components/Button'
-import {Divider} from '#/components/Divider'
+import React, { type ComponentProps, type JSX } from 'react'
+import { Linking, ScrollView, TouchableOpacity, View } from 'react-native'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { msg, Plural, plural, Trans } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import { StackActions, useNavigation } from '@react-navigation/native'
+
+import { useActorStatus } from '#/lib/actor-status'
+import { FEEDBACK_FORM_URL, HELP_DESK_URL } from '#/lib/constants'
+import { type PressableScale } from '#/lib/custom-animations/PressableScale'
+import { useNavigationTabState } from '#/lib/hooks/useNavigationTabState'
+import { getTabState, TabState } from '#/lib/routes/helpers'
+import { type NavigationProp } from '#/lib/routes/types'
+import { sanitizeHandle } from '#/lib/strings/handles'
+import { colors } from '#/lib/styles'
+import { isWeb } from '#/platform/detection'
+import { emitSoftReset } from '#/state/events'
+import { useKawaiiMode } from '#/state/preferences/kawaii'
+import { useUnreadNotifications } from '#/state/queries/notifications/unread'
+import { useProfileQuery } from '#/state/queries/profile'
+import { type SessionAccount, useSession } from '#/state/session'
+import { useSetDrawerOpen } from '#/state/shell'
+import { formatCount } from '#/view/com/util/numeric/format'
+import { UserAvatar } from '#/view/com/util/UserAvatar'
+import { NavSignupCard } from '#/view/shell/NavSignupCard'
+import { atoms as a, tokens, useTheme, web } from '#/alf'
+import { Button } from '#/components/Button'
+import { Divider } from '#/components/Divider'
import {
Bell_Filled_Corner0_Rounded as BellFilled,
Bell_Stroke2_Corner0_Rounded as Bell,
} from '#/components/icons/Bell'
-import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
-import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList'
-import {
- Hashtag_Filled_Corner0_Rounded as HashtagFilled,
- Hashtag_Stroke2_Corner0_Rounded as Hashtag,
-} from '#/components/icons/Hashtag'
import {
HomeOpen_Filled_Corner0_Rounded as HomeFilled,
HomeOpen_Stoke2_Corner0_Rounded as Home,
} from '#/components/icons/HomeOpen'
-import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass'
-import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2'
-import {
- Message_Stroke2_Corner0_Rounded as Message,
- Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
-} from '#/components/icons/Message'
-import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2'
+import { MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled } from '#/components/icons/MagnifyingGlass'
+import { MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass } from '#/components/icons/MagnifyingGlass2'
+import { SettingsGear2_Stroke2_Corner0_Rounded as Settings } from '#/components/icons/SettingsGear2'
import {
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
} from '#/components/icons/UserCircle'
-import {InlineLinkText} from '#/components/Link'
-import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
+import { InlineLinkText } from '#/components/Link'
+import { Text } from '#/components/Typography'
+import { useSimpleVerificationState } from '#/components/verification'
+import { VerificationCheck } from '#/components/verification/VerificationCheck'
const iconWidth = 26
@@ -65,11 +55,11 @@ let DrawerProfileCard = ({
account: SessionAccount
onPressProfile: () => void
}): React.ReactNode => {
- const {_, i18n} = useLingui()
+ const { _, i18n } = useLingui()
const t = useTheme()
- const {data: profile} = useProfileQuery({did: account.did})
- const verification = useSimpleVerificationState({profile})
- const {isActive: live} = useActorStatus(profile)
+ const { data: profile } = useProfileQuery({ did: account.did })
+ const verification = useSimpleVerificationState({ profile })
+ const { isActive: live } = useActorStatus(profile)
return (
<TouchableOpacity
@@ -81,7 +71,6 @@ let DrawerProfileCard = ({
<UserAvatar
size={52}
avatar={profile?.avatar}
- // See https://github.com/bluesky-social/social-app/pull/1801:
usePlainRNImage={true}
type={profile?.associated?.labeler ? 'labeler' : 'user'}
live={live}
@@ -140,9 +129,9 @@ let DrawerProfileCard = ({
)
}
DrawerProfileCard = React.memo(DrawerProfileCard)
-export {DrawerProfileCard}
+export { DrawerProfileCard }
-let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
+let DrawerContent = ({ }: React.PropsWithoutRef<{}>): React.ReactNode => {
const t = useTheme()
const insets = useSafeAreaInsets()
const setDrawerOpen = useSetDrawerOpen()
@@ -150,27 +139,20 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
const {
isAtHome,
isAtSearch,
- isAtFeeds,
- isAtBookmarks,
isAtNotifications,
isAtMyProfile,
- isAtMessages,
} = useNavigationTabState()
- const {hasSession, currentAccount} = useSession()
-
- // events
- // =
+ const { hasSession, currentAccount } = useSession()
const onPressTab = React.useCallback(
(tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => {
const state = navigation.getState()
setDrawerOpen(false)
if (isWeb) {
- // hack because we have flat navigator for web and MyProfile does not exist on the web navigator -ansh
if (tab === 'MyProfile') {
- navigation.navigate('Profile', {name: currentAccount!.handle})
+ navigation.navigate('Profile', { name: currentAccount!.handle })
} else {
- // @ts-expect-error struggles with string unions, apparently
+ // @ts-expect-error struggles with string unions
navigation.navigate(tab)
}
} else {
@@ -178,21 +160,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
if (tabState === TabState.InsideAtRoot) {
emitSoftReset()
} else if (tabState === TabState.Inside) {
- // find the correct navigator in which to pop-to-top
- const target = state.routes.find(route => route.name === `${tab}Tab`)
- ?.state?.key
+ const target = state.routes.find(route => route.name === `${tab}Tab`)?.state?.key
if (target) {
- // if we found it, trigger pop-to-top
- navigation.dispatch({
- ...StackActions.popToTop(),
- target,
- })
+ navigation.dispatch({ ...StackActions.popToTop(), target })
} else {
- // fallback: reset navigation
- navigation.reset({
- index: 0,
- routes: [{name: `${tab}Tab`}],
- })
+ navigation.reset({ index: 0, routes: [{ name: `${tab}Tab` }] })
}
} else {
navigation.navigate(`${tab}Tab`)
@@ -203,76 +175,21 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
)
const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
-
- const onPressSearch = React.useCallback(
- () => onPressTab('Search'),
- [onPressTab],
- )
-
- const onPressMessages = React.useCallback(
- () => onPressTab('Messages'),
- [onPressTab],
- )
-
- const onPressNotifications = React.useCallback(
- () => onPressTab('Notifications'),
- [onPressTab],
- )
-
- const onPressProfile = React.useCallback(() => {
- onPressTab('MyProfile')
- }, [onPressTab])
-
- const onPressMyFeeds = React.useCallback(() => {
- navigation.navigate('Feeds')
- setDrawerOpen(false)
- }, [navigation, setDrawerOpen])
-
- const onPressLists = React.useCallback(() => {
- navigation.navigate('Lists')
- setDrawerOpen(false)
- }, [navigation, setDrawerOpen])
-
- const onPressBookmarks = React.useCallback(() => {
- navigation.navigate('Bookmarks')
- setDrawerOpen(false)
- }, [navigation, setDrawerOpen])
-
+ const onPressSearch = React.useCallback(() => onPressTab('Search'), [onPressTab])
+ const onPressNotifications = React.useCallback(() => onPressTab('Notifications'), [onPressTab])
+ const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab])
const onPressSettings = React.useCallback(() => {
navigation.navigate('Settings')
setDrawerOpen(false)
}, [navigation, setDrawerOpen])
- const onPressFeedback = React.useCallback(() => {
- Linking.openURL(
- FEEDBACK_FORM_URL({
- email: currentAccount?.email,
- handle: currentAccount?.handle,
- }),
- )
- }, [currentAccount])
-
- const onPressHelp = React.useCallback(() => {
- Linking.openURL(HELP_DESK_URL)
- }, [])
-
- // rendering
- // =
-
return (
<View
testID="drawer"
style={[a.flex_1, a.border_r, t.atoms.bg, t.atoms.border_contrast_low]}>
<ScrollView
style={[a.flex_1]}
- contentContainerStyle={[
- {
- paddingTop: Math.max(
- insets.top + a.pt_xl.paddingTop,
- a.pt_xl.paddingTop,
- ),
- },
- ]}>
+ contentContainerStyle={[{ paddingTop: Math.max(insets.top + a.pt_xl.paddingTop, a.pt_xl.paddingTop) }]}>
<View style={[a.px_xl]}>
{hasSession && currentAccount ? (
<DrawerProfileCard
@@ -284,7 +201,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
<NavSignupCard />
</View>
)}
-
<Divider style={[a.mt_xl, a.mb_sm]} />
</View>
@@ -292,17 +208,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
<> <>
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} /> <SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} /> <HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
@@ -11,7 +314,7 @@ index 42a5fe417..8e7963512 100644
isActive={isAtNotifications} isActive={isAtNotifications}
onPress={onPressNotifications} onPress={onPressNotifications}
/> />
<FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} /> - <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
- <ListsMenuItem onPress={onPressLists} /> - <ListsMenuItem onPress={onPressLists} />
- <BookmarksMenuItem - <BookmarksMenuItem
- isActive={isAtBookmarks} - isActive={isAtBookmarks}
@@ -20,10 +323,53 @@ index 42a5fe417..8e7963512 100644
<ProfileMenuItem <ProfileMenuItem
isActive={isAtMyProfile} isActive={isAtMyProfile}
onPress={onPressProfile} onPress={onPressProfile}
@@ -359,17 +353,7 @@ let DrawerFooter = ({ @@ -312,7 +221,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
), ) : (
}, <>
]}> <HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
</>
)}
@@ -322,69 +230,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
<ExtraLinks />
</View>
</ScrollView>
-
- <DrawerFooter
- onPressFeedback={onPressFeedback}
- onPressHelp={onPressHelp}
- />
</View>
)
}
DrawerContent = React.memo(DrawerContent)
-export {DrawerContent}
-
-let DrawerFooter = ({
- onPressFeedback,
- onPressHelp,
-}: {
- onPressFeedback: () => void
- onPressHelp: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const insets = useSafeAreaInsets()
- return (
- <View
- style={[
- a.flex_row,
- a.gap_sm,
- a.flex_wrap,
- a.pl_xl,
- a.pt_md,
- {
- paddingBottom: Math.max(
- insets.bottom + tokens.space.xs,
- tokens.space.xl,
- ),
- },
- ]}>
- <Button - <Button
- label={_(msg`Send feedback`)} - label={_(msg`Send feedback`)}
- size="small" - size="small"
@@ -35,31 +381,333 @@ index 42a5fe417..8e7963512 100644
- <Trans>Feedback</Trans> - <Trans>Feedback</Trans>
- </ButtonText> - </ButtonText>
- </Button> - </Button>
+{/* Feedback button removed for syu.is */} - <Button
<Button - label={_(msg`Get help`)}
label={_(msg`Get help`)} - size="small"
size="small" - variant="outline"
@@ -697,15 +681,21 @@ function ExtraLinks() { - color="secondary"
<InlineLinkText - onPress={onPressHelp}
style={[a.text_md]} - style={{
label={_(msg`Terms of Service`)} - backgroundColor: 'transparent',
- }}>
- <ButtonText>
- <Trans>Help</Trans>
- </ButtonText>
- </Button>
- </View>
- )
-}
-DrawerFooter = React.memo(DrawerFooter)
+export { DrawerContent }
interface MenuItemProps extends ComponentProps<typeof PressableScale> {
icon: JSX.Element
@@ -400,7 +250,7 @@ let SearchMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -426,7 +276,7 @@ let HomeMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -445,32 +295,6 @@ let HomeMenuItem = ({
}
HomeMenuItem = React.memo(HomeMenuItem)
-let ChatMenuItem = ({
- isActive,
- onPress,
-}: {
- isActive: boolean
- onPress: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
- return (
- <MenuItem
- icon={
- isActive ? (
- <MessageFilled style={[t.atoms.text]} width={iconWidth} />
- ) : (
- <Message style={[t.atoms.text]} width={iconWidth} />
- )
- }
- label={_(msg`Chat`)}
- bold={isActive}
- onPress={onPress}
- />
- )
-}
-ChatMenuItem = React.memo(ChatMenuItem)
-
let NotificationsMenuItem = ({
isActive,
onPress,
@@ -478,7 +302,7 @@ let NotificationsMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
const numUnreadNotifications = useUnreadNotifications()
return (
@@ -495,11 +319,11 @@ let NotificationsMenuItem = ({
numUnreadNotifications === ''
? ''
: _(
- msg`${plural(numUnreadNotifications ?? 0, {
- one: '# unread item',
- other: '# unread items',
- })}` || '',
- )
+ msg`${plural(numUnreadNotifications ?? 0, {
+ one: '# unread item',
+ other: '# unread items',
+ })}` || '',
+ )
}
count={numUnreadNotifications}
bold={isActive}
@@ -509,72 +333,6 @@ let NotificationsMenuItem = ({
}
NotificationsMenuItem = React.memo(NotificationsMenuItem)
-let FeedsMenuItem = ({
- isActive,
- onPress,
-}: {
- isActive: boolean
- onPress: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
- return (
- <MenuItem
- icon={
- isActive ? (
- <HashtagFilled width={iconWidth} style={[t.atoms.text]} />
- ) : (
- <Hashtag width={iconWidth} style={[t.atoms.text]} />
- )
- }
- label={_(msg`Feeds`)}
- bold={isActive}
- onPress={onPress}
- />
- )
-}
-FeedsMenuItem = React.memo(FeedsMenuItem)
-
-let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
-
- return (
- <MenuItem
- icon={<List style={[t.atoms.text]} width={iconWidth} />}
- label={_(msg`Lists`)}
- onPress={onPress}
- />
- )
-}
-ListsMenuItem = React.memo(ListsMenuItem)
-
-let BookmarksMenuItem = ({
- isActive,
- onPress,
-}: {
- isActive: boolean
- onPress: () => void
-}): React.ReactNode => {
- const {_} = useLingui()
- const t = useTheme()
-
- return (
- <MenuItem
- icon={
- isActive ? (
- <BookmarkFilled style={[t.atoms.text]} width={iconWidth} />
- ) : (
- <Bookmark style={[t.atoms.text]} width={iconWidth} />
- )
- }
- label={_(msg({message: 'Saved', context: 'link to bookmarks screen'}))}
- onPress={onPress}
- />
- )
-}
-BookmarksMenuItem = React.memo(BookmarksMenuItem)
-
let ProfileMenuItem = ({
isActive,
onPress,
@@ -582,7 +340,7 @@ let ProfileMenuItem = ({
isActive: boolean
onPress: () => void
}): React.ReactNode => {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -600,8 +358,8 @@ let ProfileMenuItem = ({
}
ProfileMenuItem = React.memo(ProfileMenuItem)
-let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
- const {_} = useLingui()
+let SettingsMenuItem = ({ onPress }: { onPress: () => void }): React.ReactNode => {
+ const { _ } = useLingui()
const t = useTheme()
return (
<MenuItem
@@ -613,7 +371,7 @@ let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
}
SettingsMenuItem = React.memo(SettingsMenuItem)
-function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
+function MenuItem({ icon, label, count, bold, onPress }: MenuItemProps) {
const t = useTheme()
return (
<Button
@@ -621,7 +379,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
onPress={onPress}
accessibilityRole="tab"
label={label}>
- {({hovered, pressed}) => (
+ {({ hovered, pressed }) => (
<View
style={[
a.flex_1,
@@ -640,7 +398,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
a.absolute,
a.inset_0,
a.align_end,
- {top: -4, right: a.gap_sm.gap * -1},
+ { top: -4, right: a.gap_sm.gap * -1 },
]}>
<View
style={[
@@ -686,37 +444,23 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
}
function ExtraLinks() {
- const {_} = useLingui()
+ const { _ } = useLingui()
const t = useTheme()
const kawaii = useKawaiiMode()
+ const navigation = useNavigation<NavigationProp>()
return (
<View style={[a.flex_col, a.gap_md, a.flex_wrap]}>
- <InlineLinkText
- style={[a.text_md]}
- label={_(msg`Terms of Service`)}
- to="https://bsky.social/about/support/tos"> - to="https://bsky.social/about/support/tos">
+ to="/support/tos"> - <Trans>Terms of Service</Trans>
<Trans>Terms of Service</Trans> - </InlineLinkText>
</InlineLinkText> - <InlineLinkText
<InlineLinkText - style={[a.text_md]}
style={[a.text_md]}
- to="https://bsky.social/about/support/privacy-policy" - to="https://bsky.social/about/support/privacy-policy"
+ to="/support/privacy-policy" - label={_(msg`Privacy Policy`)}>
label={_(msg`Privacy Policy`)}> - <Trans>Privacy Policy</Trans>
<Trans>Privacy Policy</Trans> - </InlineLinkText>
</InlineLinkText> - {kawaii && (
+ <InlineLinkText - <Text style={t.atoms.text_contrast_medium}>
+ style={[a.text_md]} - <Trans>
+ to="/support/license" - Logo by{' '}
+ label="License"> - <InlineLinkText
+ License - style={[a.text_md]}
+ </InlineLinkText> - to="/profile/sawaratsuki.bsky.social"
{kawaii && ( - label="@sawaratsuki.bsky.social">
<Text style={t.atoms.text_contrast_medium}> - @sawaratsuki.bsky.social
<Trans> - </InlineLinkText>
- </Trans>
+ <TouchableOpacity onPress={() => navigation.navigate('TermsOfService')}>
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
+ <Trans>Terms of Service</Trans>
</Text>
- )}
+ </TouchableOpacity>
+ <TouchableOpacity onPress={() => navigation.navigate('PrivacyPolicy')}>
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
+ <Trans>Privacy Policy</Trans>
+ </Text>
+ </TouchableOpacity>
</View>
)
}
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx
index 779ebda68..bfd9b70fa 100644
--- a/src/view/shell/bottom-bar/BottomBar.tsx
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
@@ -198,38 +198,6 @@ export function BottomBar({navigation}: BottomTabBarProps) {
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
/>
- <Btn
- testID="bottomBarMessagesBtn"
- icon={
- isAtMessages ? (
- <MessageFilled
- width={iconWidth - 1}
- style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
- />
- ) : (
- <Message
- width={iconWidth - 1}
- style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
- />
- )
- }
- onPress={onPressMessages}
- notificationCount={numUnreadMessages.numUnread}
- hasNew={numUnreadMessages.hasNew}
- accessible={true}
- accessibilityRole="tab"
- accessibilityLabel={_(msg`Chat`)}
- accessibilityHint={
- numUnreadMessages.count > 0
- ? _(
- msg`${plural(numUnreadMessages.numUnread ?? 0, {
- one: '# unread item',
- other: '# unread items',
- })}` || '',
- )
- : ''
- }
- />
<Btn
testID="bottomBarNotificationsBtn"
icon={
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 1d097fc9a..e11a3a202 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -109,13 +109,13 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
</>
)}
<InlineLinkText
- to="https://bsky.social/about/support/privacy-policy"
+ to="https://syu.is/about/support/privacy-policy"
label={_(msg`Privacy`)}>
{_(msg`Privacy`)}
</InlineLinkText>
{' • '}
<InlineLinkText
- to="https://bsky.social/about/support/tos"
+ to="https://syu.is/about/support/tos"
label={_(msg`Terms`)}>
{_(msg`Terms`)}
</InlineLinkText>

View File

@@ -4,13 +4,119 @@ index 6a00cfd23..f91decc08 100644
+++ b/plugins/notificationsExtension/withNotificationsExtension.js +++ b/plugins/notificationsExtension/withNotificationsExtension.js
@@ -10,7 +10,7 @@ const EXTENSION_NAME = 'BlueskyNSE' @@ -10,7 +10,7 @@ const EXTENSION_NAME = 'BlueskyNSE'
const EXTENSION_CONTROLLER_NAME = 'NotificationService' const EXTENSION_CONTROLLER_NAME = 'NotificationService'
const withNotificationsExtension = config => { const withNotificationsExtension = config => {
- const soundFiles = ['dm.aiff'] - const soundFiles = ['dm.aiff']
+ const soundFiles = [] + const soundFiles = []
return withPlugins(config, [ return withPlugins(config, [
// IOS // IOS
diff --git a/src/ageAssurance/util.ts b/src/ageAssurance/util.ts
index 104328330..c992a21de 100644
--- a/src/ageAssurance/util.ts
+++ b/src/ageAssurance/util.ts
@@ -2,87 +2,32 @@ import {useMemo} from 'react'
import {
ageAssuranceRuleIDs as ids,
type AppBskyAgeassuranceDefs,
- getAgeAssuranceRegionConfig,
} from '@atproto/api'
-
-import {getAge} from '#/lib/strings/time'
-import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
import {AgeAssuranceAccess} from '#/ageAssurance/types'
import {type Geolocation, useGeolocation} from '#/geolocation'
+import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
-const DEFAULT_MIN_AGE = 13
-
-/**
- * Get age assurance region config based on geolocation, with fallback to
- * app defaults if no region config is found.
- *
- * See {@link getAgeAssuranceRegionConfig} for the generic option, which can
- * return undefined if the geolocation does not match any AA region.
- */
export function getAgeAssuranceRegionConfigWithFallback(
config: AppBskyAgeassuranceDefs.Config,
geolocation: Geolocation,
): AppBskyAgeassuranceDefs.ConfigRegion {
- const region = getAgeAssuranceRegionConfig(config, {
- countryCode: geolocation.countryCode ?? '',
- regionCode: geolocation.regionCode,
- })
-
- return (
- region || {
- countryCode: '*',
- regionCode: undefined,
- rules: [
- {
- $type: ids.IfDeclaredOverAge,
- age: DEFAULT_MIN_AGE,
- access: AgeAssuranceAccess.Full,
- },
- {
- $type: ids.Default,
- access: AgeAssuranceAccess.None,
- },
- ],
- }
- )
+ return {
+ countryCode: '*',
+ regionCode: undefined,
+ rules: [{ $type: ids.Default, access: AgeAssuranceAccess.Full }],
+ }
}
-/**
- * Hook to get the age assurance region config based on current geolocation.
- * Does not fall-back to our app defaults. If no config is found, returns
- * undefined, which indicates no regional age assurance rules apply.
- */
export function useAgeAssuranceRegionConfig() {
const geolocation = useGeolocation()
const {config} = useAgeAssuranceDataContext()
- return useMemo(() => {
- if (!config) return
- // use generic helper, we want to potentially return undefined
- return getAgeAssuranceRegionConfig(config, {
- countryCode: geolocation.countryCode ?? '',
- regionCode: geolocation.regionCode,
- })
- }, [config, geolocation])
+ return useMemo(() => ({
+ countryCode: '*',
+ regionCode: undefined,
+ rules: [{ $type: ids.Default, access: AgeAssuranceAccess.Full }],
+ }), [config, geolocation])
}
-/**
- * Some users may have erroneously set their birth date to the current date
- * if one wasn't set on their account. We previously didn't do validation on
- * the bday dialog, and it defaulted to the current date. This bug _has_ been
- * seen in production, so we need to check for it where possible.
- */
-export function isLegacyBirthdateBug(birthDate: string) {
- return ['2025', '2024', '2023'].includes((birthDate || '').slice(0, 4))
-}
-
-/**
- * Returns whether the user is under the minimum age required to use the app.
- * This applies to all regions.
- */
-export function isUserUnderMinimumAge(birthDate: string) {
- return getAge(new Date(birthDate)) < DEFAULT_MIN_AGE
-}
-
-export function isUserUnderAdultAge(birthDate: string) {
- return getAge(new Date(birthDate)) < 18
-}
+export function isLegacyBirthdateBug(birthDate: string) { return false }
+export function isUserUnderMinimumAge(birthDate: string) { return false }
+export function isUserUnderAdultAge(birthDate: string) { return false }
diff --git a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx diff --git a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
index 8365057e8..59c8506a2 100644 index 8365057e8..59c8506a2 100644
--- a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx --- a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
@@ -20,13 +126,13 @@ index 8365057e8..59c8506a2 100644
terms: { terms: {
overridePresentation: false, overridePresentation: false,
- to: `https://bsky.social/about/support/tos`, - to: `https://bsky.social/about/support/tos`,
+ to: `/support/tos`, + to: `https://syu.is/about/support/tos`,
label: _(msg`Terms of Service`), label: _(msg`Terms of Service`),
}, },
privacy: { privacy: {
overridePresentation: false, overridePresentation: false,
- to: `https://bsky.social/about/support/privacy-policy`, - to: `https://bsky.social/about/support/privacy-policy`,
+ to: `/support/privacy-policy`, + to: `https://syu.is/about/support/privacy-policy`,
label: _(msg`Privacy Policy`), label: _(msg`Privacy Policy`),
}, },
copyright: { copyright: {

View File

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

View File

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

View File

@@ -1,11 +1,70 @@
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
index 3442d1bdf..2b059af52 100644
--- a/src/view/com/auth/SplashScreen.tsx
+++ b/src/view/com/auth/SplashScreen.tsx
@@ -40,16 +40,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
@@ -94,7 +84,7 @@ export const SplashScreen = ({
style={[
a.px_lg,
a.pt_md,
- a.pb_2xl,
+ a.pb_md,
a.justify_center,
a.align_center,
]}>
@@ -102,6 +92,17 @@ export const SplashScreen = ({
<AppLanguageDropdown />
</View>
</View>
+ <View
+ style={[
+ a.px_lg,
+ 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 diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
index d9185d778..504f521a4 100644 index 22dd23d7f..08cd8aa33 100644
--- a/src/view/com/auth/SplashScreen.web.tsx --- a/src/view/com/auth/SplashScreen.web.tsx
+++ b/src/view/com/auth/SplashScreen.web.tsx +++ b/src/view/com/auth/SplashScreen.web.tsx
@@ -94,14 +94,6 @@ export const SplashScreen = ({ @@ -3,7 +3,9 @@ import {Pressable, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import {useNavigation} from '@react-navigation/native'
+import {NavigationProp} from '#/lib/routes/types'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useKawaiiMode} from '#/state/preferences/kawaii'
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
@@ -93,15 +95,6 @@ export const SplashScreen = ({
<Logotype width={161} fill={t.atoms.text.color} />
</View> </View>
)} )}
-
- <Text - <Text
- style={[ - style={[
- a.text_md, - a.text_md,
@@ -15,5 +74,44 @@ index d9185d778..504f521a4 100644
- <Trans>What's up?</Trans> - <Trans>What's up?</Trans>
- </Text> - </Text>
</View> </View>
<View <View
@@ -150,7 +143,6 @@ export const SplashScreen = ({
function Footer() {
const t = useTheme()
- const {_} = useLingui()
return (
<View
@@ -168,26 +160,12 @@ function Footer() {
a.flex_1,
t.atoms.border_contrast_medium,
]}>
- <InlineLinkText
- label={_(msg`Learn more about Bluesky`)}
- to="https://bsky.social">
- <Trans>Business</Trans>
- </InlineLinkText>
- <InlineLinkText
- label={_(msg`Read the Bluesky blog`)}
- to="https://bsky.social/about/blog">
- <Trans>Blog</Trans>
- </InlineLinkText>
- <InlineLinkText
- label={_(msg`See jobs at Bluesky`)}
- to="https://bsky.social/about/join">
- <Trans comment="Link to a page with job openings at Bluesky">
- Jobs
- </Trans>
- </InlineLinkText>
-
<View style={a.flex_1} />
+ <Text style={[a.text_xs, t.atoms.text_contrast_low]}>
+ © syui
+ </Text>
+
<AppLanguageDropdown />
</View>
)

View File

@@ -0,0 +1,31 @@
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 6b8257b91..bed851753 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="/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="/about/support/privacy-policy"
label={_(msg`Privacy Policy`)}>
<SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText>
@@ -96,7 +96,7 @@ export function AboutSettingsScreen({}: Props) {
</SettingsList.ItemText>
</SettingsList.LinkItem>
<SettingsList.LinkItem
- to={STATUS_PAGE_URL}
+ to="https://syu.is/"
label={_(msg`Status Page`)}>
<SettingsList.ItemIcon icon={GlobeIcon} />
<SettingsList.ItemText>

View File

@@ -1,37 +1,8 @@
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 2fa5aa7de..3faf6a7b3 100644 index 1703036b5..42baa462c 100644
--- a/src/screens/Settings/Settings.tsx --- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx +++ b/src/screens/Settings/Settings.tsx
@@ -203,26 +203,8 @@ export function SettingsScreen({}: Props) { @@ -231,16 +231,6 @@ 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>
- {IS_NATIVE &&
- findContactsEnabled &&
- !ax.features.enabled(ax.features.ImportContactsSettingsDisable) && (
- <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`)}>
@@ -247,16 +229,6 @@ export function SettingsScreen({}: Props) {
<Trans>Languages</Trans> <Trans>Languages</Trans>
</SettingsList.ItemText> </SettingsList.ItemText>
</SettingsList.LinkItem> </SettingsList.LinkItem>

View File

@@ -1,9 +1,9 @@
diff --git a/plugins/withCodeSignEntitlements.js b/plugins/withCodeSignEntitlements.js diff --git a/plugins/withCodeSignEntitlements.js b/plugins/withCodeSignEntitlements.js
new file mode 100644 new file mode 100644
index 000000000..b03b6bd68 index 000000000..e69de29bb
--- /dev/null --- /dev/null
+++ b/plugins/withCodeSignEntitlements.js +++ b/plugins/withCodeSignEntitlements.js
@@ -0,0 +1,25 @@ @@ -0,0 +1,22 @@
+/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable @typescript-eslint/no-var-requires */
+const { withXcodeProject } = require('@expo/config-plugins') +const { withXcodeProject } = require('@expo/config-plugins')
+ +
@@ -16,7 +16,6 @@ index 000000000..b03b6bd68
+ const configuration = configurations[key] + const configuration = configurations[key]
+ if ( + if (
+ configuration.buildSettings && + configuration.buildSettings &&
+ configuration.comment &&
+ !configuration.comment.includes('TEST') + !configuration.comment.includes('TEST')
+ ) { + ) {
+ configuration.buildSettings.CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = + configuration.buildSettings.CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION =

View File

@@ -1,39 +0,0 @@
diff --git a/src/ageAssurance/index.tsx b/src/ageAssurance/index.tsx
--- a/src/ageAssurance/index.tsx
+++ b/src/ageAssurance/index.tsx
@@ -90,25 +90,16 @@ function InnerProvider({children}: {children: React.ReactNode}) {
return (
<AgeAssuranceStateContext.Provider
value={useMemo(() => {
- const chatDisabled = state.access !== AgeAssuranceAccess.Full
- const isUnderAdultAge = data?.birthdate
- ? isUnderAge(data.birthdate, 18)
- : true
- const isOverRegionMinAccessAge = data?.birthdate
- ? !isUnderAge(data.birthdate, config.minAccessAge)
- : false
- const isOverAppMinAccessAge = data?.birthdate
- ? !isUnderAge(data.birthdate, MIN_ACCESS_AGE)
- : false
- const adultContentDisabled =
- state.access !== AgeAssuranceAccess.Full || isUnderAdultAge
return {
Access: AgeAssuranceAccess,
Status: AgeAssuranceStatus,
- state,
+ state: {
+ ...state,
+ access: AgeAssuranceAccess.Full,
+ },
flags: {
- adultContentDisabled,
- chatDisabled,
- isOverRegionMinAccessAge,
- isOverAppMinAccessAge,
+ adultContentDisabled: false,
+ chatDisabled: false,
+ isOverRegionMinAccessAge: true,
+ isOverAppMinAccessAge: true,
},
}
}, [state, data, config])}>

View File

@@ -1,241 +0,0 @@
--- a/src/view/com/posts/DiscoverFallbackHeader.tsx
+++ b/src/view/com/posts/DiscoverFallbackHeader.tsx
@@ -7,37 +7,5 @@
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
}
--- a/src/view/com/posts/FollowingEmptyState.tsx
+++ b/src/view/com/posts/FollowingEmptyState.tsx
@@ -1,38 +1,15 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
-import {
- FontAwesomeIcon,
- type FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
import {Trans} from '@lingui/react/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 {IS_WEB} from '#/env'
-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 (IS_WEB) {
- navigation.navigate('Search', {})
- } else {
- navigation.navigate('SearchTab')
- navigation.popToTop()
- }
- }, [navigation])
-
- const onPressDiscoverFeeds = React.useCallback(() => {
- navigation.navigate('Feeds')
- }, [navigation])
-
return (
<View style={styles.container}>
<View style={styles.inner}>
@@ -45,36 +22,6 @@
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 @@
marginLeft: 'auto',
marginRight: 'auto',
},
- emptyBtn: {
- marginVertical: 20,
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingVertical: 18,
- paddingHorizontal: 24,
- borderRadius: 30,
- },
})
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
@@ -1,37 +1,14 @@
import React from 'react'
import {Dimensions, StyleSheet, View} from 'react-native'
-import {
- FontAwesomeIcon,
- type FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
import {Trans} from '@lingui/react/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 {IS_WEB} from '#/env'
-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 (IS_WEB) {
- navigation.navigate('Search', {})
- } else {
- navigation.navigate('SearchTab')
- navigation.popToTop()
- }
- }, [navigation])
-
- const onPressDiscoverFeeds = React.useCallback(() => {
- navigation.navigate('Feeds')
- }, [navigation])
-
return (
<View
style={[
@@ -41,41 +18,8 @@
]}>
<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>
+ <Trans>You've reached the end of your feed!</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>
)
@@ -93,13 +37,4 @@
width: '100%',
maxWidth: 460,
},
- emptyBtn: {
- marginVertical: 20,
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingVertical: 18,
- paddingHorizontal: 24,
- borderRadius: 30,
- },
})
--- a/src/view/com/posts/PostFeed.tsx
+++ b/src/view/com/posts/PostFeed.tsx
@@ -765,7 +765,7 @@
} 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

@@ -1,17 +0,0 @@
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -317,6 +317,14 @@ 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)
+ e.GET("/support/privacy-policy", server.WebGeneric)
+ e.GET("/support/license", server.WebGeneric)
+ e.GET("/support/app", server.WebGeneric)
+ e.GET("/support/help", server.WebGeneric)
+ // /about/support/ paths (for web compatibility)
+ e.GET("/about/support/tos", server.WebGeneric)
+ e.GET("/about/support/privacy-policy", server.WebGeneric)
+ e.GET("/about/support/license", server.WebGeneric)
e.GET("/intent/compose", server.WebGeneric)
e.GET("/intent/verify-email", server.WebGeneric)
e.GET("/intent/age-assurance", server.WebGeneric)

View File

@@ -1,75 +0,0 @@
--- a/src/view/shell/bottom-bar/BottomBar.tsx
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
@@ -196,38 +196,40 @@
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
/>
- <Btn
- testID="bottomBarMessagesBtn"
- icon={
- isAtMessages ? (
- <MessageFilled
- width={iconWidth - 1}
- style={[styles.ctrlIcon, t.atoms.text, styles.feedsIcon]}
- />
- ) : (
- <Message
- width={iconWidth - 1}
- style={[styles.ctrlIcon, t.atoms.text, styles.feedsIcon]}
- />
- )
- }
- onPress={onPressMessages}
- notificationCount={numUnreadMessages.numUnread}
- hasNew={numUnreadMessages.hasNew}
- accessible={true}
- accessibilityRole="tab"
- accessibilityLabel={_(msg`Chat`)}
- accessibilityHint={
- numUnreadMessages.count > 0
- ? _(
- plural(numUnreadMessages.numUnread ?? 0, {
- one: '# unread item',
- other: '# unread items',
- }),
- )
- : ''
- }
- />
+ {currentAccount?.isSelfHosted && (
+ <Btn
+ testID="bottomBarMessagesBtn"
+ icon={
+ isAtMessages ? (
+ <MessageFilled
+ width={iconWidth - 1}
+ style={[styles.ctrlIcon, t.atoms.text, styles.feedsIcon]}
+ />
+ ) : (
+ <Message
+ width={iconWidth - 1}
+ style={[styles.ctrlIcon, t.atoms.text, styles.feedsIcon]}
+ />
+ )
+ }
+ onPress={onPressMessages}
+ notificationCount={numUnreadMessages.numUnread}
+ hasNew={numUnreadMessages.hasNew}
+ accessible={true}
+ accessibilityRole="tab"
+ accessibilityLabel={_(msg`Chat`)}
+ accessibilityHint={
+ numUnreadMessages.count > 0
+ ? _(
+ plural(numUnreadMessages.numUnread ?? 0, {
+ one: '# unread item',
+ other: '# unread items',
+ }),
+ )
+ : ''
+ }
+ />
+ )}
<Btn
testID="bottomBarNotificationsBtn"
icon={

Some files were not shown because too many files have changed in this diff Show More