Compare commits
17 Commits
0506a32fd8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
7518cd7d06
|
|||
|
f130c8d731
|
|||
|
511527abfd
|
|||
|
31ee74bb66
|
|||
|
acd41f8ece
|
|||
|
34a51a2198
|
|||
|
966bfc7c2f
|
|||
|
25e7927c70
|
|||
|
6d89ea4481
|
|||
|
1690aa4c12
|
|||
|
c9dd10d6f3
|
|||
|
b6f7d8bed1
|
|||
|
f4180e0c54
|
|||
|
9157948d93
|
|||
|
8db064d8c0
|
|||
|
c09738f342
|
|||
|
0ef8f22dcb
|
7
.github/workflows/cf-pages.yml
vendored
@@ -33,11 +33,8 @@ jobs:
|
|||||||
working-directory: web
|
working-directory: web
|
||||||
|
|
||||||
- name: Deploy to Cloudflare Pages
|
- name: Deploy to Cloudflare Pages
|
||||||
uses: cloudflare/pages-action@v1
|
uses: cloudflare/wrangler-action@v3
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
command: pages deploy web/dist/aiat --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||||
directory: web/dist/aiat
|
|
||||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
wranglerVersion: '3'
|
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -9,4 +9,4 @@ k8s/deploy.yml
|
|||||||
web/dist
|
web/dist
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
/tmp
|
||||||
|
|||||||
21
README.md
@@ -1,18 +1,15 @@
|
|||||||
# at
|
# at
|
||||||
|
|
||||||
- https://github.com/bluesky-social/atproto
|
- https://github.com/bluesky-social/atproto
|
||||||
- https://github.com/bluesky-social/atproto/discussions/2026
|
- https://github.com/bluesky-social/social-app
|
||||||
|
|
||||||
|word|name|example|
|
|name|type|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/guides/glossary
|
|
||||||
|
|
||||||
## account
|
## account
|
||||||
|
|
||||||
- [ai@syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie)
|
- [ai@syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie)
|
||||||
@@ -22,19 +19,6 @@
|
|||||||
|
|
||||||
```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
|
||||||
@@ -85,4 +69,3 @@ $ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=${handle}&collection=a
|
|||||||
./ios/build.zsh
|
./ios/build.zsh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
32
icon.svg
@@ -1,32 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 4.0 KiB |
130
install.zsh
@@ -120,6 +120,10 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +334,41 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,23 +417,86 @@ export const handler = async (ctx: AppContext, params: QueryParams) => {
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✅ Created src/algos/app.ts"
|
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 "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
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
|
||||||
docker image prune -a
|
local failed=()
|
||||||
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]}
|
||||||
docker compose build --no-cache $service
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
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
|
if [ "$service" = "ozone" ]; then
|
||||||
docker compose build --no-cache ${service}-web
|
docker compose build --no-cache ${service}-web
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
docker compose build --no-cache $1
|
echo "🔨 Building: $1"
|
||||||
|
if ! docker compose build --no-cache $1; then
|
||||||
|
echo "❌ Build failed: $1"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if ! at-repos-docker-verify $1; then
|
||||||
|
return 1
|
||||||
|
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() {
|
||||||
@@ -412,25 +514,41 @@ function at-repos-push-reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function at-repos-push-docker() {
|
function at-repos-push-docker() {
|
||||||
|
local dtag=$(date +%Y%m%d)
|
||||||
if [ -z "$1" ] || [ "$1" = "push" ]; then
|
if [ -z "$1" ] || [ "$1" = "push" ]; then
|
||||||
for service in "${services[@]}"; do
|
for service in "${services[@]}"; do
|
||||||
|
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
|
||||||
docker tag at-${1}:latest localhost:${dport}/${1}:latest
|
if ! at-repos-docker-verify $1; then
|
||||||
docker push localhost:${dport}/${1}:latest
|
echo "❌ Push aborted: $1 (verification failed)"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
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}:${dtag}
|
||||||
|
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() {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 821 B |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 821 B |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
ios/assets/favicons/bsky.chat.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 536 B |
|
Before Width: | Height: | Size: 1005 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1005 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
BIN
ios/assets/logo_old.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
ios/assets/logo_white.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ios/assets/splash/illustration-mobile.mp4
Normal file
BIN
ios/icon.png
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
@@ -27,7 +27,7 @@
|
|||||||
policy: 'appVersion',
|
policy: 'appVersion',
|
||||||
},
|
},
|
||||||
- icon: './assets/app-icons/ios_icon_default_next.png',
|
- icon: './assets/app-icons/ios_icon_default_next.png',
|
||||||
+ icon: './assets/logo.png',
|
+ icon: './assets/app-icon.png',
|
||||||
userInterfaceStyle: 'automatic',
|
userInterfaceStyle: 'automatic',
|
||||||
primaryColor: '#1083fe',
|
primaryColor: '#1083fe',
|
||||||
newArchEnabled: false,
|
newArchEnabled: false,
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
- 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/logo.png',
|
+ icon: './assets/app-icon.png',
|
||||||
infoPlist: {
|
infoPlist: {
|
||||||
UIBackgroundModes: ['remote-notification'],
|
UIBackgroundModes: ['remote-notification'],
|
||||||
NSCameraUsageDescription:
|
NSCameraUsageDescription:
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
},
|
},
|
||||||
android: {
|
android: {
|
||||||
- icon: './assets/app-icons/android_icon_default_next.png',
|
- icon: './assets/app-icons/android_icon_default_next.png',
|
||||||
+ icon: './assets/logo.png',
|
+ icon: './assets/app-icon.png',
|
||||||
adaptiveIcon: {
|
adaptiveIcon: {
|
||||||
foregroundImage: './assets/icon-android-foreground.png',
|
foregroundImage: './assets/icon-android-foreground.png',
|
||||||
monochromeImage: './assets/icon-android-monochrome.png',
|
monochromeImage: './assets/icon-android-monochrome.png',
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ 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/help'
|
+export const HELP_DESK_URL = '/support/license'
|
||||||
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'
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
|
|
||||||
index 9ba067a2f..e34b9f9b0 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
|
||||||
@@ -78,7 +78,7 @@ export function AboutSettingsScreen({}: Props) {
|
@@ -79,7 +79,7 @@
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
<SettingsList.Container>
|
<SettingsList.Container>
|
||||||
<SettingsList.LinkItem
|
<SettingsList.LinkItem
|
||||||
@@ -11,7 +9,7 @@ index 9ba067a2f..e34b9f9b0 100644
|
|||||||
label={_(msg`Terms of Service`)}>
|
label={_(msg`Terms of Service`)}>
|
||||||
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
||||||
<SettingsList.ItemText>
|
<SettingsList.ItemText>
|
||||||
@@ -86,7 +86,7 @@ export function AboutSettingsScreen({}: Props) {
|
@@ -87,7 +87,7 @@
|
||||||
</SettingsList.ItemText>
|
</SettingsList.ItemText>
|
||||||
</SettingsList.LinkItem>
|
</SettingsList.LinkItem>
|
||||||
<SettingsList.LinkItem
|
<SettingsList.LinkItem
|
||||||
@@ -20,11 +18,9 @@ index 9ba067a2f..e34b9f9b0 100644
|
|||||||
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 660aecf1a..f19a62c0f 100644
|
|
||||||
--- a/src/screens/Takendown.tsx
|
--- a/src/screens/Takendown.tsx
|
||||||
+++ b/src/screens/Takendown.tsx
|
+++ b/src/screens/Takendown.tsx
|
||||||
@@ -212,10 +212,10 @@ export function Takendown() {
|
@@ -210,10 +210,10 @@
|
||||||
<Trans>
|
<Trans>
|
||||||
Your account was found to be in violation of the{' '}
|
Your account was found to be in violation of the{' '}
|
||||||
<SimpleInlineLinkText
|
<SimpleInlineLinkText
|
||||||
@@ -38,15 +34,14 @@ index 660aecf1a..f19a62c0f 100644
|
|||||||
</SimpleInlineLinkText>
|
</SimpleInlineLinkText>
|
||||||
. 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/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
|
|
||||||
index a89eaadc4..71ce7c81f 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,51 +1,49 @@
|
@@ -1,52 +1,49 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
-import {View} from 'react-native'
|
-import {View} from 'react-native'
|
||||||
-import {msg, Trans} from '@lingui/macro'
|
-import {msg} from '@lingui/core/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'
|
||||||
@@ -62,26 +57,25 @@ index a89eaadc4..71ce7c81f 100644
|
|||||||
+import {ScrollView} from 'react-native'
|
+import {ScrollView} from 'react-native'
|
||||||
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'
|
-
|
||||||
+import {atoms as a, useTheme} from '#/alf'
|
|
||||||
+import {Text} from '#/components/Typography'
|
|
||||||
+
|
|
||||||
+export function PrivacyPolicyScreen() {
|
|
||||||
+ useSetTitle('Privacy Policy')
|
|
||||||
+ const t = useTheme()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen>
|
<Layout.Screen>
|
||||||
- <ViewHeader title={_(msg`Privacy Policy`)} />
|
- <ViewHeader title={_(msg`Privacy Policy`)} />
|
||||||
@@ -130,20 +124,19 @@ index a89eaadc4..71ce7c81f 100644
|
|||||||
+ </Text>
|
+ </Text>
|
||||||
+
|
+
|
||||||
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
||||||
+ Last updated: 2025
|
+ Last updated: 2026
|
||||||
+ </Text>
|
+ </Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
)
|
)
|
||||||
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
|
|
||||||
index d843c713c..c6a36268b 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,49 +1,49 @@
|
@@ -1,50 +1,49 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
-import {View} from 'react-native'
|
-import {View} from 'react-native'
|
||||||
-import {msg, Trans} from '@lingui/macro'
|
-import {msg} from '@lingui/core/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'
|
||||||
@@ -159,26 +152,25 @@ index d843c713c..c6a36268b 100644
|
|||||||
+import {ScrollView} from 'react-native'
|
+import {ScrollView} from 'react-native'
|
||||||
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'
|
-
|
||||||
+import {atoms as a, useTheme} from '#/alf'
|
|
||||||
+import {Text} from '#/components/Typography'
|
|
||||||
+
|
|
||||||
+export function TermsOfServiceScreen() {
|
|
||||||
+ useSetTitle('Terms of Service')
|
|
||||||
+ const t = useTheme()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen>
|
<Layout.Screen>
|
||||||
- <ViewHeader title={_(msg`Terms of Service`)} />
|
- <ViewHeader title={_(msg`Terms of Service`)} />
|
||||||
@@ -225,7 +217,7 @@ index d843c713c..c6a36268b 100644
|
|||||||
+ </Text>
|
+ </Text>
|
||||||
+
|
+
|
||||||
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
||||||
+ Last updated: 2025
|
+ Last updated: 2026
|
||||||
+ </Text>
|
+ </Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
diff --git a/src/view/com/posts/DiscoverFallbackHeader.tsx b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
|
||||||
index e35a33aaf..a36f84ae0 100644
|
|
||||||
--- a/src/view/com/posts/DiscoverFallbackHeader.tsx
|
--- a/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||||
+++ b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
+++ b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||||
@@ -7,37 +7,5 @@ import {TextLink} from '../util/Link'
|
@@ -7,37 +7,5 @@
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
export function DiscoverFallbackHeader() {
|
export function DiscoverFallbackHeader() {
|
||||||
@@ -41,18 +39,16 @@ index e35a33aaf..a36f84ae0 100644
|
|||||||
- )
|
- )
|
||||||
+ return null
|
+ return null
|
||||||
}
|
}
|
||||||
diff --git a/src/view/com/posts/FollowingEmptyState.tsx b/src/view/com/posts/FollowingEmptyState.tsx
|
|
||||||
index 352cc1dc0..f477521af 100644
|
|
||||||
--- a/src/view/com/posts/FollowingEmptyState.tsx
|
--- a/src/view/com/posts/FollowingEmptyState.tsx
|
||||||
+++ b/src/view/com/posts/FollowingEmptyState.tsx
|
+++ b/src/view/com/posts/FollowingEmptyState.tsx
|
||||||
@@ -1,37 +1,14 @@
|
@@ -1,38 +1,15 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
-import {
|
-import {
|
||||||
- FontAwesomeIcon,
|
- FontAwesomeIcon,
|
||||||
- type FontAwesomeIconStyle,
|
- type FontAwesomeIconStyle,
|
||||||
-} from '@fortawesome/react-native-fontawesome'
|
-} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
-import {useNavigation} from '@react-navigation/native'
|
-import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
@@ -67,7 +63,7 @@ index 352cc1dc0..f477521af 100644
|
|||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
- const palInverted = usePalette('inverted')
|
- const palInverted = usePalette('inverted')
|
||||||
- const navigation = useNavigation<NavigationProp>()
|
- const navigation = useNavigation<NavigationProp>()
|
||||||
-
|
|
||||||
- const onPressFindAccounts = React.useCallback(() => {
|
- const onPressFindAccounts = React.useCallback(() => {
|
||||||
- if (IS_WEB) {
|
- if (IS_WEB) {
|
||||||
- navigation.navigate('Search', {})
|
- navigation.navigate('Search', {})
|
||||||
@@ -80,10 +76,11 @@ index 352cc1dc0..f477521af 100644
|
|||||||
- const onPressDiscoverFeeds = React.useCallback(() => {
|
- const onPressDiscoverFeeds = React.useCallback(() => {
|
||||||
- navigation.navigate('Feeds')
|
- navigation.navigate('Feeds')
|
||||||
- }, [navigation])
|
- }, [navigation])
|
||||||
|
-
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
@@ -45,36 +22,6 @@ export function FollowingEmptyState() {
|
<View style={styles.inner}>
|
||||||
|
@@ -45,36 +22,6 @@
|
||||||
happening.
|
happening.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
@@ -120,7 +117,7 @@ index 352cc1dc0..f477521af 100644
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -98,13 +45,4 @@ const styles = StyleSheet.create({
|
@@ -98,13 +45,4 @@
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
},
|
},
|
||||||
@@ -134,18 +131,16 @@ index 352cc1dc0..f477521af 100644
|
|||||||
- borderRadius: 30,
|
- borderRadius: 30,
|
||||||
- },
|
- },
|
||||||
})
|
})
|
||||||
diff --git a/src/view/com/posts/FollowingEndOfFeed.tsx b/src/view/com/posts/FollowingEndOfFeed.tsx
|
|
||||||
index e3c84d782..efb55d406 100644
|
|
||||||
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
|
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||||
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
|
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||||
@@ -1,36 +1,13 @@
|
@@ -1,37 +1,14 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Dimensions, StyleSheet, View} from 'react-native'
|
import {Dimensions, StyleSheet, View} from 'react-native'
|
||||||
-import {
|
-import {
|
||||||
- FontAwesomeIcon,
|
- FontAwesomeIcon,
|
||||||
- type FontAwesomeIconStyle,
|
- type FontAwesomeIconStyle,
|
||||||
-} from '@fortawesome/react-native-fontawesome'
|
-} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
-import {useNavigation} from '@react-navigation/native'
|
-import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
@@ -159,7 +154,7 @@ index e3c84d782..efb55d406 100644
|
|||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
- const palInverted = usePalette('inverted')
|
- const palInverted = usePalette('inverted')
|
||||||
- const navigation = useNavigation<NavigationProp>()
|
- const navigation = useNavigation<NavigationProp>()
|
||||||
-
|
|
||||||
- const onPressFindAccounts = React.useCallback(() => {
|
- const onPressFindAccounts = React.useCallback(() => {
|
||||||
- if (IS_WEB) {
|
- if (IS_WEB) {
|
||||||
- navigation.navigate('Search', {})
|
- navigation.navigate('Search', {})
|
||||||
@@ -172,10 +167,11 @@ index e3c84d782..efb55d406 100644
|
|||||||
- const onPressDiscoverFeeds = React.useCallback(() => {
|
- const onPressDiscoverFeeds = React.useCallback(() => {
|
||||||
- navigation.navigate('Feeds')
|
- navigation.navigate('Feeds')
|
||||||
- }, [navigation])
|
- }, [navigation])
|
||||||
|
-
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -41,41 +18,8 @@ export function FollowingEndOfFeed() {
|
style={[
|
||||||
|
@@ -41,41 +18,8 @@
|
||||||
]}>
|
]}>
|
||||||
<View style={styles.inner}>
|
<View style={styles.inner}>
|
||||||
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||||
@@ -183,7 +179,8 @@ index e3c84d782..efb55d406 100644
|
|||||||
- You've reached the end of your feed! Find some more accounts to
|
- You've reached the end of your feed! Find some more accounts to
|
||||||
- follow.
|
- follow.
|
||||||
- </Trans>
|
- </Trans>
|
||||||
- </Text>
|
+ <Trans>You've reached the end of your feed!</Trans>
|
||||||
|
</Text>
|
||||||
- <Button
|
- <Button
|
||||||
- type="inverted"
|
- type="inverted"
|
||||||
- style={styles.emptyBtn}
|
- style={styles.emptyBtn}
|
||||||
@@ -200,8 +197,7 @@ index e3c84d782..efb55d406 100644
|
|||||||
-
|
-
|
||||||
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||||
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
|
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||||
+ <Trans>You've reached the end of your feed!</Trans>
|
- </Text>
|
||||||
</Text>
|
|
||||||
- <Button
|
- <Button
|
||||||
- type="inverted"
|
- type="inverted"
|
||||||
- style={[styles.emptyBtn, s.mt10]}
|
- style={[styles.emptyBtn, s.mt10]}
|
||||||
@@ -218,7 +214,7 @@ index e3c84d782..efb55d406 100644
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -93,13 +37,4 @@ const styles = StyleSheet.create({
|
@@ -93,13 +37,4 @@
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: 460,
|
maxWidth: 460,
|
||||||
},
|
},
|
||||||
@@ -232,11 +228,9 @@ index e3c84d782..efb55d406 100644
|
|||||||
- borderRadius: 30,
|
- borderRadius: 30,
|
||||||
- },
|
- },
|
||||||
})
|
})
|
||||||
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
|
|
||||||
index 4f25468c9..a72a10b80 100644
|
|
||||||
--- a/src/view/com/posts/PostFeed.tsx
|
--- a/src/view/com/posts/PostFeed.tsx
|
||||||
+++ b/src/view/com/posts/PostFeed.tsx
|
+++ b/src/view/com/posts/PostFeed.tsx
|
||||||
@@ -766,7 +766,7 @@ let PostFeed = ({
|
@@ -765,7 +765,7 @@
|
||||||
} else if (row.type === 'feedShutdownMsg') {
|
} else if (row.type === 'feedShutdownMsg') {
|
||||||
return <FeedShutdownMsg feedUri={feedUriOrActorDid} />
|
return <FeedShutdownMsg feedUri={feedUriOrActorDid} />
|
||||||
} else if (row.type === 'interstitialFollows') {
|
} else if (row.type === 'interstitialFollows') {
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
diff --git a/src/env/common.ts b/src/env/common.ts
|
|
||||||
index 04e98c49c..a4ee47932 100644
|
|
||||||
--- a/src/env/common.ts
|
--- a/src/env/common.ts
|
||||||
+++ b/src/env/common.ts
|
+++ b/src/env/common.ts
|
||||||
@@ -107,19 +107,17 @@ export const GCP_PROJECT_ID: number =
|
@@ -88,7 +88,7 @@
|
||||||
|
* Metrics API host
|
||||||
|
*/
|
||||||
|
export const METRICS_API_HOST: string =
|
||||||
|
- process.env.EXPO_PUBLIC_METRICS_API_HOST || 'https://events.bsky.app'
|
||||||
|
+ process.env.EXPO_PUBLIC_METRICS_API_HOST || ''
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URLs for the app config web worker. Can be a
|
* Growthbook API host
|
||||||
* locally running server, see `env.example` for more.
|
@@ -128,9 +128,7 @@
|
||||||
+ * Disabled for self-hosted environment to avoid CORS errors
|
|
||||||
*/
|
*/
|
||||||
export const GEOLOCATION_DEV_URL = process.env.GEOLOCATION_DEV_URL
|
export const GEOLOCATION_DEV_URL = process.env.GEOLOCATION_DEV_URL
|
||||||
export const GEOLOCATION_PROD_URL = `https://ip.bsky.app`
|
export const GEOLOCATION_PROD_URL = `https://ip.bsky.app`
|
||||||
@@ -17,8 +20,7 @@ index 04e98c49c..a4ee47932 100644
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* URLs for the live-event config web worker. Can be a
|
* URLs for the live-event config web worker. Can be a
|
||||||
* locally running server, see `env.example` for more.
|
@@ -138,9 +136,7 @@
|
||||||
+ * Disabled for self-hosted environment
|
|
||||||
*/
|
*/
|
||||||
export const LIVE_EVENTS_DEV_URL = process.env.LIVE_EVENTS_DEV_URL
|
export const LIVE_EVENTS_DEV_URL = process.env.LIVE_EVENTS_DEV_URL
|
||||||
export const LIVE_EVENTS_PROD_URL = `https://live-events.workers.bsky.app`
|
export const LIVE_EVENTS_PROD_URL = `https://live-events.workers.bsky.app`
|
||||||
@@ -26,3 +28,14 @@ index 04e98c49c..a4ee47932 100644
|
|||||||
- ? (LIVE_EVENTS_DEV_URL ?? LIVE_EVENTS_PROD_URL)
|
- ? (LIVE_EVENTS_DEV_URL ?? LIVE_EVENTS_PROD_URL)
|
||||||
- : LIVE_EVENTS_PROD_URL
|
- : LIVE_EVENTS_PROD_URL
|
||||||
+export const LIVE_EVENTS_URL = null
|
+export const LIVE_EVENTS_URL = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URLs for the app-config web worker. Can be a
|
||||||
|
@@ -148,6 +144,4 @@
|
||||||
|
*/
|
||||||
|
export const APP_CONFIG_DEV_URL = process.env.APP_CONFIG_DEV_URL
|
||||||
|
export const APP_CONFIG_PROD_URL = `https://app-config.workers.bsky.app`
|
||||||
|
-export const APP_CONFIG_URL = IS_DEV
|
||||||
|
- ? (APP_CONFIG_DEV_URL ?? APP_CONFIG_PROD_URL)
|
||||||
|
- : APP_CONFIG_PROD_URL
|
||||||
|
+export const APP_CONFIG_URL = null
|
||||||
|
|||||||
@@ -125,15 +125,15 @@
|
|||||||
- <Admonition.Content>
|
- <Admonition.Content>
|
||||||
- <Admonition.Text>
|
- <Admonition.Text>
|
||||||
- {!isOverAppMinAccessAge ? (
|
- {!isOverAppMinAccessAge ? (
|
||||||
- <Trans>
|
- <Plural
|
||||||
- You must be {MIN_ACCESS_AGE} years of age or older
|
- value={MIN_ACCESS_AGE}
|
||||||
- to create an account.
|
- other="You must be # years of age or older to create an account."
|
||||||
- </Trans>
|
- />
|
||||||
- ) : (
|
- ) : (
|
||||||
- <Trans>
|
- <Plural
|
||||||
- You must be {aaRegionConfig.minAccessAge} years of
|
- value={aaRegionConfig.minAccessAge}
|
||||||
- age or older to create an account in your region.
|
- other="You must be # years of age or older to create an account in your region."
|
||||||
- </Trans>
|
- />
|
||||||
- )}
|
- )}
|
||||||
- </Admonition.Text>
|
- </Admonition.Text>
|
||||||
- {IS_NATIVE &&
|
- {IS_NATIVE &&
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx 2026-02-16 03:00:07
|
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
|
||||||
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx 2026-02-16 03:02:25
|
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
|
||||||
@@ -1,5 +1,5 @@
|
@@ -1,5 +1,5 @@
|
||||||
import {memo, useMemo, useState} from 'react'
|
import {memo, useMemo, useState} from 'react'
|
||||||
-import {View} from 'react-native'
|
-import {View} from 'react-native'
|
||||||
@@ -7,54 +7,63 @@
|
|||||||
import {
|
import {
|
||||||
type AppBskyActorDefs,
|
type AppBskyActorDefs,
|
||||||
moderateProfile,
|
moderateProfile,
|
||||||
@@ -9,9 +9,11 @@
|
@@ -11,7 +11,10 @@
|
||||||
} from '@atproto/api'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
+import {useQuery} from '@tanstack/react-query'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useActorStatus} from '#/lib/actor-status'
|
+import {useQuery} from '@tanstack/react-query'
|
||||||
|
+
|
||||||
import {useHaptics} from '#/lib/haptics'
|
import {useHaptics} from '#/lib/haptics'
|
||||||
+import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
+import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
||||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -20,7 +22,7 @@
|
@@ -47,6 +50,103 @@
|
||||||
useProfileBlockMutationQueue,
|
|
||||||
useProfileFollowMutationQueue,
|
|
||||||
} from '#/state/queries/profile'
|
|
||||||
-import {useRequireAuth, useSession} from '#/state/session'
|
|
||||||
+import {useAgent, useRequireAuth, useSession} from '#/state/session'
|
|
||||||
import {ProfileMenu} from '#/view/com/profile/ProfileMenu'
|
|
||||||
import {atoms as a, platform, useBreakpoints, useTheme} from '#/alf'
|
|
||||||
import {SubscribeProfileButton} from '#/components/activity-notifications/SubscribeProfileButton'
|
|
||||||
@@ -45,6 +47,83 @@
|
|
||||||
import {ProfileHeaderMetrics} from './Metrics'
|
|
||||||
import {ProfileHeaderShell} from './Shell'
|
import {ProfileHeaderShell} from './Shell'
|
||||||
import {ProfileHeaderSuggestedFollows} from './SuggestedFollows'
|
import {ProfileHeaderSuggestedFollows} from './SuggestedFollows'
|
||||||
+
|
|
||||||
+const SERVICE_FAVICONS: Record<string, any> = {
|
+const SERVICE_FAVICONS: Record<string, any> = {
|
||||||
+ 'syui.ai': require('../../../../assets/favicons/syui.ai.png'),
|
+ 'syui.ai': require('../../../../assets/favicons/syui.ai.png'),
|
||||||
+ 'bsky.app': require('../../../../assets/favicons/bsky.app.png'),
|
+ 'bsky.app': require('../../../../assets/favicons/bsky.app.png'),
|
||||||
+ 'atproto.com': require('../../../../assets/favicons/atproto.com.png'),
|
+ 'atproto.com': require('../../../../assets/favicons/atproto.com.png'),
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
|
+async function resolvePds(did: string): Promise<string> {
|
||||||
|
+ if (did.startsWith('did:web:')) {
|
||||||
|
+ const host = did.split(':').slice(2).join(':')
|
||||||
|
+ const res = await fetch(`https://${host}/.well-known/did.json`)
|
||||||
|
+ if (!res.ok) throw new Error('failed to resolve did:web')
|
||||||
|
+ const doc = await res.json()
|
||||||
|
+ const pds = doc.service?.find((s: any) => s.id === '#atproto_pds')?.serviceEndpoint
|
||||||
|
+ if (pds) return pds
|
||||||
|
+ return `https://${host}`
|
||||||
|
+ }
|
||||||
|
+ const res = await fetch(`https://plc.directory/${did}`)
|
||||||
|
+ if (!res.ok) throw new Error('failed to resolve DID')
|
||||||
|
+ const doc = await res.json()
|
||||||
|
+ const pds = doc.service?.find((s: any) => s.id === '#atproto_pds')?.serviceEndpoint
|
||||||
|
+ if (!pds) throw new Error('no PDS found')
|
||||||
|
+ return pds
|
||||||
|
+}
|
||||||
|
+
|
||||||
+function ProfileServiceLinks({
|
+function ProfileServiceLinks({
|
||||||
+ profile,
|
+ profile,
|
||||||
+}: {
|
+}: {
|
||||||
+ profile: AppBskyActorDefs.ProfileViewDetailed
|
+ profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
+}) {
|
+}) {
|
||||||
+ const t = useTheme()
|
+ const t = useTheme()
|
||||||
+ const agent = useAgent()
|
|
||||||
+ const openLink = useOpenLink()
|
+ const openLink = useOpenLink()
|
||||||
+
|
+
|
||||||
+ const {data: services} = useQuery({
|
+ const {data: services} = useQuery({
|
||||||
+ queryKey: ['profile-services', profile.did],
|
+ queryKey: ['profile-services', profile.did],
|
||||||
+ queryFn: async () => {
|
+ queryFn: async () => {
|
||||||
+ const res = await agent.com.atproto.repo.describeRepo({
|
+ const pds = await resolvePds(profile.did)
|
||||||
+ repo: profile.did,
|
+ const res = await fetch(
|
||||||
+ })
|
+ `${pds}/xrpc/com.atproto.repo.describeRepo?repo=${encodeURIComponent(profile.did)}`,
|
||||||
+ const collections: string[] = res.data.collections || []
|
+ )
|
||||||
|
+ if (!res.ok) throw new Error('failed')
|
||||||
|
+ const data = await res.json()
|
||||||
|
+ const collections: string[] = data.collections || []
|
||||||
+ const serviceSet = new Set<string>()
|
+ const serviceSet = new Set<string>()
|
||||||
+ for (const nsid of collections) {
|
+ for (const nsid of collections) {
|
||||||
+ const parts = nsid.split('.')
|
+ const parts = nsid.split('.')
|
||||||
@@ -109,10 +118,11 @@
|
|||||||
+ </View>
|
+ </View>
|
||||||
+ )
|
+ )
|
||||||
+}
|
+}
|
||||||
|
+
|
||||||
interface Props {
|
interface Props {
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
@@ -151,6 +230,7 @@
|
descriptionRT: RichTextAPI | null
|
||||||
|
@@ -152,6 +252,7 @@
|
||||||
{!isPlaceholderProfile && !isBlockedUser && (
|
{!isPlaceholderProfile && !isBlockedUser && (
|
||||||
<View style={a.gap_md}>
|
<View style={a.gap_md}>
|
||||||
<ProfileHeaderMetrics profile={profile} />
|
<ProfileHeaderMetrics profile={profile} />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
|
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
|
||||||
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
|
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
|
||||||
@@ -46,6 +46,7 @@
|
@@ -48,6 +48,7 @@
|
||||||
import {ProfileHeaderHandle} from './Handle'
|
import {ProfileHeaderHandle} from './Handle'
|
||||||
import {ProfileHeaderMetrics} from './Metrics'
|
import {ProfileHeaderMetrics} from './Metrics'
|
||||||
import {ProfileHeaderShell} from './Shell'
|
import {ProfileHeaderShell} from './Shell'
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
import {ProfileHeaderSuggestedFollows} from './SuggestedFollows'
|
import {ProfileHeaderSuggestedFollows} from './SuggestedFollows'
|
||||||
|
|
||||||
const SERVICE_FAVICONS: Record<string, any> = {
|
const SERVICE_FAVICONS: Record<string, any> = {
|
||||||
@@ -231,6 +232,7 @@
|
@@ -253,6 +254,7 @@
|
||||||
<View style={a.gap_md}>
|
<View style={a.gap_md}>
|
||||||
<ProfileHeaderMetrics profile={profile} />
|
<ProfileHeaderMetrics profile={profile} />
|
||||||
<ProfileServiceLinks profile={profile} />
|
<ProfileServiceLinks profile={profile} />
|
||||||
|
|||||||
19
ios/patching/043-social-app-ios-rightnav-links.patch
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
--- a/src/view/shell/desktop/RightNav.tsx
|
||||||
|
+++ b/src/view/shell/desktop/RightNav.tsx
|
||||||
|
@@ -111,14 +111,14 @@
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<InlineLinkText
|
||||||
|
- to="https://bsky.social/about/support/privacy-policy"
|
||||||
|
+ to="/support/privacy-policy"
|
||||||
|
style={[t.atoms.text_contrast_medium]}
|
||||||
|
label={_(msg`Privacy`)}>
|
||||||
|
{_(msg`Privacy`)}
|
||||||
|
</InlineLinkText>
|
||||||
|
<Text style={[t.atoms.text_contrast_low]}>{' ∙ '}</Text>
|
||||||
|
<InlineLinkText
|
||||||
|
- to="https://bsky.social/about/support/tos"
|
||||||
|
+ to="/support/tos"
|
||||||
|
style={[t.atoms.text_contrast_medium]}
|
||||||
|
label={_(msg`Terms`)}>
|
||||||
|
{_(msg`Terms`)}
|
||||||
60
ios/patching/044-social-app-ios-splash-video.patch
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
--- a/src/view/com/auth/SplashScreen.tsx
|
||||||
|
+++ b/src/view/com/auth/SplashScreen.tsx
|
||||||
|
@@ -2,6 +2,7 @@
|
||||||
|
import {Image as RNImage, View} from 'react-native'
|
||||||
|
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||||
|
import {Image} from 'expo-image'
|
||||||
|
+import {useVideoPlayer, VideoView} from 'expo-video'
|
||||||
|
import {msg} from '@lingui/core/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
@@ -15,10 +16,13 @@
|
||||||
|
import splashImagePointer from '../../../../assets/splash/illustration-mobile.png'
|
||||||
|
// @ts-ignore
|
||||||
|
import darkSplashImagePointer from '../../../../assets/splash/illustration-mobile-dark.png'
|
||||||
|
+// @ts-ignore
|
||||||
|
+import splashVideoPointer from '../../../../assets/splash/illustration-mobile.mp4'
|
||||||
|
const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri
|
||||||
|
const darkSplashImageUri = RNImage.resolveAssetSource(
|
||||||
|
darkSplashImagePointer,
|
||||||
|
).uri
|
||||||
|
+const splashVideoUri = RNImage.resolveAssetSource(splashVideoPointer).uri
|
||||||
|
|
||||||
|
export const SplashScreen = ({
|
||||||
|
onPressSignin,
|
||||||
|
@@ -53,13 +57,30 @@
|
||||||
|
}
|
||||||
|
}, [t, isDarkMode])
|
||||||
|
|
||||||
|
+ const player = useVideoPlayer(splashVideoUri, p => {
|
||||||
|
+ p.loop = true
|
||||||
|
+ p.muted = true
|
||||||
|
+ p.play()
|
||||||
|
+ })
|
||||||
|
+
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
- <Image
|
||||||
|
- accessibilityIgnoresInvertColors
|
||||||
|
- source={{uri: isDarkMode ? darkSplashImageUri : splashImageUri}}
|
||||||
|
- style={[a.absolute, a.inset_0]}
|
||||||
|
- />
|
||||||
|
+ {isDarkMode ? (
|
||||||
|
+ <Image
|
||||||
|
+ accessibilityIgnoresInvertColors
|
||||||
|
+ source={{uri: darkSplashImageUri}}
|
||||||
|
+ style={[a.absolute, a.inset_0]}
|
||||||
|
+ />
|
||||||
|
+ ) : (
|
||||||
|
+ <VideoView
|
||||||
|
+ player={player}
|
||||||
|
+ style={[a.absolute, a.inset_0]}
|
||||||
|
+ contentFit="cover"
|
||||||
|
+ nativeControls={false}
|
||||||
|
+ allowsFullscreen={false}
|
||||||
|
+ allowsPictureInPicture={false}
|
||||||
|
+ />
|
||||||
|
+ )}
|
||||||
|
|
||||||
|
<Animated.View
|
||||||
|
entering={FadeIn.duration(90)}
|
||||||
40
ios/patching/045-social-app-ios-composer-cancel.patch
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
--- a/src/view/com/composer/Composer.tsx
|
||||||
|
+++ b/src/view/com/composer/Composer.tsx
|
||||||
|
@@ -717,16 +717,7 @@
|
||||||
|
post.shortenedGraphemeLength > 0 || post.embed.media || post.embed.link,
|
||||||
|
)
|
||||||
|
|
||||||
|
- // Show discard prompt if there's content AND either:
|
||||||
|
- // - No draft is loaded (new composition)
|
||||||
|
- // - Draft is loaded but has been modified
|
||||||
|
- if (hasContent && (!composerState.draftId || composerState.isDirty)) {
|
||||||
|
- closeAllDialogs()
|
||||||
|
- Keyboard.dismiss()
|
||||||
|
- discardPromptControl.open()
|
||||||
|
- } else {
|
||||||
|
- onClose()
|
||||||
|
- }
|
||||||
|
+ onClose()
|
||||||
|
}, [
|
||||||
|
thread,
|
||||||
|
composerState.draftId,
|
||||||
|
@@ -1551,18 +1542,7 @@
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
- {!isReply && (
|
||||||
|
- <DraftsButton
|
||||||
|
- onSelectDraft={onSelectDraft}
|
||||||
|
- onSaveDraft={onSaveDraft}
|
||||||
|
- onDiscard={onDiscard}
|
||||||
|
- isEmpty={isEmpty}
|
||||||
|
- isDirty={isDirty}
|
||||||
|
- isEditingDraft={isEditingDraft}
|
||||||
|
- canSaveDraft={canSaveDraft}
|
||||||
|
- textLength={textLength}
|
||||||
|
- />
|
||||||
|
- )}
|
||||||
|
+ {/* DraftsButton removed */}
|
||||||
|
<Button
|
||||||
|
testID="composerPublishBtn"
|
||||||
|
label={
|
||||||
55
ios/patching/046-social-app-ios-null-url-guards.patch
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
diff --git a/src/features/liveEvents/context.tsx b/src/features/liveEvents/context.tsx
|
||||||
|
index 3de2534a8..e11955f57 100644
|
||||||
|
--- a/src/features/liveEvents/context.tsx
|
||||||
|
+++ b/src/features/liveEvents/context.tsx
|
||||||
|
@@ -24,6 +24,7 @@ export const DEFAULT_LIVE_EVENTS = {
|
||||||
|
|
||||||
|
async function fetchLiveEvents(): Promise<LiveEventsWorkerResponse | null> {
|
||||||
|
try {
|
||||||
|
+ if (!LIVE_EVENTS_URL) return null
|
||||||
|
const res = await fetch(`${LIVE_EVENTS_URL}/config`)
|
||||||
|
if (!res.ok) return null
|
||||||
|
const data = await res.json()
|
||||||
|
diff --git a/src/geolocation/const.ts b/src/geolocation/const.ts
|
||||||
|
index 653e829ba..4992cc870 100644
|
||||||
|
--- a/src/geolocation/const.ts
|
||||||
|
+++ b/src/geolocation/const.ts
|
||||||
|
@@ -1,7 +1,9 @@
|
||||||
|
import {GEOLOCATION_URL} from '#/env'
|
||||||
|
import {type Geolocation} from '#/geolocation/types'
|
||||||
|
|
||||||
|
-export const GEOLOCATION_SERVICE_URL = `${GEOLOCATION_URL}/geolocation`
|
||||||
|
+export const GEOLOCATION_SERVICE_URL = GEOLOCATION_URL
|
||||||
|
+ ? `${GEOLOCATION_URL}/geolocation`
|
||||||
|
+ : null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default geolocation config.
|
||||||
|
diff --git a/src/geolocation/service.ts b/src/geolocation/service.ts
|
||||||
|
index 2d9285b67..c7c3d02c0 100644
|
||||||
|
--- a/src/geolocation/service.ts
|
||||||
|
+++ b/src/geolocation/service.ts
|
||||||
|
@@ -26,9 +26,10 @@ const onGeolocationServiceResponseUpdate = (
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchGeolocationServiceData(
|
||||||
|
- url: string,
|
||||||
|
+ url: string | null,
|
||||||
|
): Promise<Geolocation | undefined> {
|
||||||
|
if (debug.enabled) return debug.resolve(debug.geolocation)
|
||||||
|
+ if (!url) throw new Error('geolocation service disabled')
|
||||||
|
const res = await fetch(url)
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`fetchGeolocationServiceData failed ${res.status}`)
|
||||||
|
diff --git a/src/state/appConfig.tsx b/src/state/appConfig.tsx
|
||||||
|
index 67b0e553e..9eacf7ead 100644
|
||||||
|
--- a/src/state/appConfig.tsx
|
||||||
|
+++ b/src/state/appConfig.tsx
|
||||||
|
@@ -30,6 +30,7 @@ let fetchAppConfigPromise: Promise<AppConfigResponse> | undefined
|
||||||
|
|
||||||
|
async function fetchAppConfig(): Promise<AppConfigResponse | null> {
|
||||||
|
try {
|
||||||
|
+ if (!APP_CONFIG_URL) return null
|
||||||
|
if (!fetchAppConfigPromise) {
|
||||||
|
fetchAppConfigPromise = (async () => {
|
||||||
|
const r = await fetch(`${APP_CONFIG_URL}/config`)
|
||||||
@@ -27,7 +27,7 @@ export function LicenseScreen() {
|
|||||||
<Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>MIT License</Text>
|
<Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>MIT License</Text>
|
||||||
|
|
||||||
<Text style={[a.mb_md, {fontFamily: 'monospace'}]}>
|
<Text style={[a.mb_md, {fontFamily: 'monospace'}]}>
|
||||||
Copyright (c) 2022-2025 Bluesky PBC
|
Copyright (c) 2022-2026 Bluesky PBC
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text style={[a.mb_md]}>
|
<Text style={[a.mb_md]}>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {type AppBskyActorDefs} from '@atproto/api'
|
|||||||
import {useQuery} from '@tanstack/react-query'
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
||||||
import {useAgent} from '#/state/session'
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {createSinglePathSVG} from '#/components/icons/TEMPLATE'
|
import {createSinglePathSVG} from '#/components/icons/TEMPLATE'
|
||||||
@@ -66,6 +65,26 @@ const SERVICE_CONFIG: Record<
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- PDS Resolution ---
|
||||||
|
|
||||||
|
async function resolvePds(did: string): Promise<string> {
|
||||||
|
if (did.startsWith('did:web:')) {
|
||||||
|
const host = did.split(':').slice(2).join(':')
|
||||||
|
const res = await fetch(`https://${host}/.well-known/did.json`)
|
||||||
|
if (!res.ok) throw new Error('failed to resolve did:web')
|
||||||
|
const doc = await res.json()
|
||||||
|
const pds = doc.service?.find((s: any) => s.id === '#atproto_pds')?.serviceEndpoint
|
||||||
|
if (pds) return pds
|
||||||
|
return `https://${host}`
|
||||||
|
}
|
||||||
|
const res = await fetch(`https://plc.directory/${did}`)
|
||||||
|
if (!res.ok) throw new Error('failed to resolve DID')
|
||||||
|
const doc = await res.json()
|
||||||
|
const pds = doc.service?.find((s: any) => s.id === '#atproto_pds')?.serviceEndpoint
|
||||||
|
if (!pds) throw new Error('no PDS found')
|
||||||
|
return pds
|
||||||
|
}
|
||||||
|
|
||||||
// --- Component ---
|
// --- Component ---
|
||||||
|
|
||||||
export function ProfileAtLinks({
|
export function ProfileAtLinks({
|
||||||
@@ -74,18 +93,18 @@ export function ProfileAtLinks({
|
|||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const agent = useAgent()
|
|
||||||
const openLink = useOpenLink()
|
const openLink = useOpenLink()
|
||||||
|
|
||||||
const {data: linkData} = useQuery({
|
const {data: linkData} = useQuery({
|
||||||
queryKey: ['at-links', profile.did],
|
queryKey: ['at-links', profile.did],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res = await agent.com.atproto.repo.getRecord({
|
const pds = await resolvePds(profile.did)
|
||||||
repo: profile.did,
|
const res = await fetch(
|
||||||
collection: 'ai.syui.at.link',
|
`${pds}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(profile.did)}&collection=ai.syui.at.link&rkey=self`,
|
||||||
rkey: 'self',
|
)
|
||||||
})
|
if (!res.ok) throw new Error('not found')
|
||||||
return res.data.value as LinkCollection
|
const json = await res.json()
|
||||||
|
return json.value as LinkCollection
|
||||||
},
|
},
|
||||||
retry: false,
|
retry: false,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
|
|||||||
12162
ios/patching/ja-messages.po
Normal file
@@ -50,6 +50,10 @@ PATCH_FILES_IOS=(
|
|||||||
"040-social-app-ios-hide-composer-prompt.patch"
|
"040-social-app-ios-hide-composer-prompt.patch"
|
||||||
"041-social-app-ios-splash-signin-button.patch"
|
"041-social-app-ios-splash-signin-button.patch"
|
||||||
"042-social-app-ios-at-links.patch"
|
"042-social-app-ios-at-links.patch"
|
||||||
|
"043-social-app-ios-rightnav-links.patch"
|
||||||
|
"044-social-app-ios-splash-video.patch"
|
||||||
|
"045-social-app-ios-composer-cancel.patch"
|
||||||
|
"046-social-app-ios-null-url-guards.patch"
|
||||||
)
|
)
|
||||||
|
|
||||||
function ios-env() {
|
function ios-env() {
|
||||||
@@ -171,6 +175,17 @@ function ios-copy-new-files() {
|
|||||||
echo "✅ Copied all assets (including logo.png, app-icons)"
|
echo "✅ Copied all assets (including logo.png, app-icons)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Generate app-icon.png (logo on #333 background) for app icon
|
||||||
|
if [ -f "$d/ios/assets/logo.png" ]; then
|
||||||
|
if command -v magick >/dev/null 2>&1; then
|
||||||
|
magick -size 1024x1024 "xc:#333333" "$d/ios/assets/logo.png" -gravity center -composite "$target_dir/assets/app-icon.png"
|
||||||
|
echo "✅ Generated app-icon.png (logo + #333 bg)"
|
||||||
|
else
|
||||||
|
echo "⚠️ ImageMagick not found, copying logo.png as app-icon.png fallback"
|
||||||
|
cp "$d/ios/assets/logo.png" "$target_dir/assets/app-icon.png"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Copy License.tsx
|
# Copy License.tsx
|
||||||
if [ -f "$patching_dir/License.tsx" ]; then
|
if [ -f "$patching_dir/License.tsx" ]; then
|
||||||
mkdir -p "$target_dir/src/view/screens"
|
mkdir -p "$target_dir/src/view/screens"
|
||||||
@@ -185,6 +200,18 @@ function ios-copy-new-files() {
|
|||||||
echo "✅ Copied AppInfo.tsx"
|
echo "✅ Copied AppInfo.tsx"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Copy Japanese locale (translation overrides)
|
||||||
|
if [ -f "$patching_dir/ja-messages.po" ]; then
|
||||||
|
cp "$patching_dir/ja-messages.po" "$target_dir/src/locale/locales/ja/messages.po"
|
||||||
|
echo "✅ Copied ja-messages.po"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy splash video
|
||||||
|
if [ -f "$d/ios/assets/splash/illustration-mobile.mp4" ]; then
|
||||||
|
cp "$d/ios/assets/splash/illustration-mobile.mp4" "$target_dir/assets/splash/illustration-mobile.mp4"
|
||||||
|
echo "✅ Copied splash video (illustration-mobile.mp4)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Copy ProfileAtLinks.tsx
|
# Copy ProfileAtLinks.tsx
|
||||||
if [ -f "$patching_dir/ProfileAtLinks.tsx" ]; then
|
if [ -f "$patching_dir/ProfileAtLinks.tsx" ]; then
|
||||||
cp "$patching_dir/ProfileAtLinks.tsx" "$target_dir/src/screens/Profile/Header/ProfileAtLinks.tsx"
|
cp "$patching_dir/ProfileAtLinks.tsx" "$target_dir/src/screens/Profile/Header/ProfileAtLinks.tsx"
|
||||||
@@ -203,6 +230,14 @@ function ios-copy-new-files() {
|
|||||||
echo "✅ Copied favicons to bskyweb/static"
|
echo "✅ Copied favicons to bskyweb/static"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Compile locale if messages.po was copied
|
||||||
|
if [ -f "$patching_dir/ja-messages.po" ]; then
|
||||||
|
echo "🌐 Compiling locale..."
|
||||||
|
pushd "$target_dir" > /dev/null
|
||||||
|
yarn intl:compile 2>/dev/null && echo "✅ Compiled locale" || echo "⚠️ Locale compile skipped (yarn not ready)"
|
||||||
|
popd > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ path: help/index.html
|
|||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
1. Download the app from the App Store
|
1. Download the app from the App Store
|
||||||
2. Sign in with your AT Protocol handle
|
2. Sign in with your handle
|
||||||
3. Start connecting with your community
|
3. Start connecting with your community
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
This app uses AT Protocol OAuth for secure authentication. Your credentials are never stored on our servers.
|
This app uses OAuth for secure authentication. Your credentials are never stored on our servers.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -36,4 +36,4 @@ This app uses AT Protocol OAuth for secure authentication. Your credentials are
|
|||||||
|
|
||||||
## Contact Support
|
## Contact Support
|
||||||
|
|
||||||
For additional help, please visit our Git repository or contact us through AT Protocol.
|
For additional help, please visit our Git repository or contact us through our social account.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ This application is open source software.
|
|||||||
|
|
||||||
This app uses the following open source libraries:
|
This app uses the following open source libraries:
|
||||||
|
|
||||||
- **AT Protocol** - MIT License
|
- **Decentralized Social Protocol** - MIT License
|
||||||
- **React Native** - MIT License
|
- **React Native** - MIT License
|
||||||
- **Expo** - MIT License
|
- **Expo** - MIT License
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ App icons and graphics are proprietary unless otherwise noted.
|
|||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
Built with AT Protocol by syui.
|
Built by syui.
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ path: privacy/index.html
|
|||||||
|
|
||||||
This application collects minimal information necessary for its operation:
|
This application collects minimal information necessary for its operation:
|
||||||
|
|
||||||
- **Account Information**: Your AT Protocol handle and DID for authentication
|
- **Account Information**: Your handle and DID for authentication
|
||||||
- **Usage Data**: Basic app usage statistics to improve the service
|
- **Usage Data**: Basic app usage statistics to improve the service
|
||||||
|
|
||||||
## How We Use Your Information
|
## How We Use Your Information
|
||||||
@@ -19,7 +19,7 @@ This application collects minimal information necessary for its operation:
|
|||||||
|
|
||||||
## Data Storage
|
## Data Storage
|
||||||
|
|
||||||
Your data is stored securely on AT Protocol compatible servers. We do not sell or share your personal information with third parties.
|
Your data is stored securely on decentralized social servers. We do not sell or share your personal information with third parties.
|
||||||
|
|
||||||
## Your Rights
|
## Your Rights
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ By using this application, you agree to these Terms of Service.
|
|||||||
|
|
||||||
You agree to use this service in accordance with:
|
You agree to use this service in accordance with:
|
||||||
- Applicable laws and regulations
|
- Applicable laws and regulations
|
||||||
- AT Protocol community guidelines
|
- Community guidelines
|
||||||
- Respectful behavior towards other users
|
- Respectful behavior towards other users
|
||||||
|
|
||||||
## User Content
|
## User Content
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"scheme": "aiat",
|
"scheme": "aiat",
|
||||||
"version": "1.111.0",
|
"version": "1.111.0",
|
||||||
"category": "Social",
|
"category": "Social",
|
||||||
"description": "Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.",
|
"description": "Aiat is a social networking application. Connect with your community on syu.is.",
|
||||||
"backLink": "syu.is",
|
"backLink": "syu.is",
|
||||||
"icon": "/static/app.png",
|
"icon": "/static/app.png",
|
||||||
"os": "iOS 26.0+",
|
"os": "iOS 26.0+",
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export function appLayout(site: SiteConfig, content: string): string {
|
|||||||
<span class="link-arrow">→</span>
|
<span class="link-arrow">→</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="link-row">
|
<div class="link-row">
|
||||||
<span class="link-icon">ATProto</span>
|
<span class="link-icon">Social</span>
|
||||||
<a href="https://syui.ai" class="link-value" target="_blank">syui.ai</a>
|
<a href="https://syui.ai" class="link-value" target="_blank">syui.ai</a>
|
||||||
<span class="link-arrow">→</span>
|
<span class="link-arrow">→</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||