Compare commits
22 Commits
main
...
b43360e32a
| Author | SHA1 | Date | |
|---|---|---|---|
|
b43360e32a
|
|||
|
ded3110fce
|
|||
|
6f4712e461
|
|||
|
bd3299154c
|
|||
|
8b97cbfd9b
|
|||
|
246f95a00c
|
|||
|
cc27f367ea
|
|||
|
72b8f39825
|
|||
|
c3d75140d8
|
|||
|
a45ba54323
|
|||
|
a77cdc1e58
|
|||
|
9db0aab05a
|
|||
|
320d98b2da
|
|||
|
e05868c9b1
|
|||
|
9d78ee8627
|
|||
|
16c785bf0f
|
|||
|
b5e44947da
|
|||
|
f26ef781f3
|
|||
|
b5b3850f7b
|
|||
|
c61fd5c748
|
|||
|
005ddc36cf
|
|||
|
5acaa7aeec
|
30
.github/workflows/cf-pages.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Deploy to Cloudflare Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'html/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy to Cloudflare Pages
|
||||
uses: cloudflare/pages-action@v1
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||
directory: html
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
wranglerVersion: '3'
|
||||
38
.github/workflows/generate-record.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Generate Record JSON
|
||||
|
||||
on:
|
||||
workflow_dispatch: # 手動トリガー
|
||||
push:
|
||||
paths:
|
||||
- scpt/generate_record.py # スクリプトに変更があったとき
|
||||
schedule:
|
||||
- cron: '0 3 * * *' # 毎日03:00 UTCに自動生成(任意)
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt || true # 必要なら
|
||||
|
||||
- name: Run Record Generator Script
|
||||
run: |
|
||||
python scpt/generate_record.py
|
||||
|
||||
- name: Commit and Push Generated JSON
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions Bot"
|
||||
git config --global user.email "actions@github.com"
|
||||
git add record.json
|
||||
git commit -m "🧬 Auto-generate record.json" || echo "No changes to commit"
|
||||
git push origin main
|
||||
6
.gitignore
vendored
@@ -1,7 +1 @@
|
||||
repos
|
||||
.claude
|
||||
deploy.yml
|
||||
claude.md
|
||||
embedded.mobileprovision
|
||||
.env
|
||||
html.zip
|
||||
|
||||
138
README.md
@@ -1,12 +1,11 @@
|
||||
# at
|
||||
|
||||
- https://github.com/bluesky-social/atproto
|
||||
- https://github.com/bluesky-social/atproto/discussions/2026
|
||||
https://github.com/bluesky-social/atproto
|
||||
|
||||
|word|name|example|
|
||||
|---|---|---|
|
||||
|at|uri|at://example.com|
|
||||
|@|user|@example.com|
|
||||
|at|uri|at://ai.syu.is|
|
||||
|@|user|@ai.syu.is|
|
||||
|[at]proto|repo|`git@github.com:bluesky-social/atproto`|
|
||||
|[at]mosphere|system|pds, bsky(appview), ozone, bgs, plc|
|
||||
|[a]uthenticated [t]ransfer|protocol|[did](https://www.w3.org/TR/did-core/)|
|
||||
@@ -15,13 +14,19 @@
|
||||
|
||||
## account
|
||||
|
||||
- [ai@syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie)
|
||||
- [yui@bsky.social](https://bsky.app/profile/did:plc:6qyecktefllvenje24fcxnie)
|
||||
[@ai.syu.is](https://web.syu.is/profile/ai.syu.is)
|
||||
|
||||
- https://at.syu.is/at/yui.syui.ai
|
||||
- https://plc.syu.is/did:plc:6qyecktefllvenje24fcxnie
|
||||
- https://plc.directory/did:plc:6qyecktefllvenje24fcxnie
|
||||
- https://plc.directory/did:plc:ytvoptig4ddshmwdsjmhtcym
|
||||
|
||||
[@yui.syui.ai](https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef)
|
||||
|
||||
- https://pds.syu.is/at://did:plc:4hqjfn7m6n5hno3doamuhgef
|
||||
|
||||
```sh
|
||||
$ curl -sL syu.is/xrpc/_health
|
||||
{"version":"0.4.65"}
|
||||
|
||||
# latest
|
||||
# https://github.com/bluesky-social/atproto/blob/main/packages/pds/package.json
|
||||
@@ -29,60 +34,95 @@ $ curl -sL https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/m
|
||||
```
|
||||
|
||||
```sh
|
||||
$ handle=ai.syui.ai
|
||||
$ curl -sL "syu.is/xrpc/com.atproto.repo.describeRepo?repo=${handle}" |jq -r .did
|
||||
$ curl -sL "syu.is/xrpc/com.atproto.repo.describeRepo?repo=ai.syu.is" |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"
|
||||
$ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=ai.syu.is&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
|
||||
|
||||
> at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app
|
||||
> at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/cmd
|
||||
|
||||
- https://syu.is/profile/ai.syui.ai/feed/app
|
||||
- https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/app
|
||||
- https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd
|
||||
- https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd
|
||||
- https://desc.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/cmd
|
||||
|
||||
```json
|
||||
{
|
||||
"uri": "at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app",
|
||||
"cid": "bafyreifme6g5mhuiwfmjaubwnkoyvwak6c6zvcy4uv3giikxvqpvhqdtau",
|
||||
"value": {
|
||||
"did": "did:web:feed.syu.is",
|
||||
"$type": "app.bsky.feed.generator",
|
||||
"avatar": {
|
||||
"$type": "blob",
|
||||
"ref": {
|
||||
"$link": "bafkreigo3ucp32carhbn3chfc3hlf6i7f4rplojc76iylihzpifyexi24y"
|
||||
},
|
||||
"mimeType": "image/jpeg",
|
||||
"size": 375259
|
||||
},
|
||||
"createdAt": "2025-12-06T09:07:32Z",
|
||||
"description": "Automated App Feed",
|
||||
"displayName": "App Feed"
|
||||
}
|
||||
}
|
||||
```
|
||||
## link
|
||||
|
||||
## build
|
||||
- https://github.com/bluesky-social/atproto
|
||||
- https://github.com/itaru2622/bluesky-selfhost-env
|
||||
- https://github.com/bluesky-social/atproto/discussions/2026
|
||||
|
||||
## self-host
|
||||
|
||||
currently, bsky and bsync require patches to function properly. additionally, social-app is not displaying avatars. for components that are not working, it's recommended to use [itaru2622/bluesky-selfhost-env](https://github.com/itaru2622/bluesky-selfhost-env). this repository provides an environment for self-hosting bluesky.
|
||||
|
||||
- bsky = appview
|
||||
- ozone = mod
|
||||
|
||||
|name|service|patch|
|
||||
|---|---|---|
|
||||
|pds|https://github.com/bluesky-social/atproto/blob/main/services/pds/Dockerfile||
|
||||
|bsky|https://github.com/bluesky-social/atproto/blob/main/services/bsky/Dockerfile|[itaru2622/bluesky-atproto-bsky](https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff)|
|
||||
|bsync|https://github.com/bluesky-social/atproto/blob/main/services/bsync/Dockerfile||
|
||||
|ozone|https://github.com/bluesky-social/atproto/blob/main/services/ozone/Dockerfile||
|
||||
|plc|https://github.com/did-method-plc/did-method-plc/tree/main/packages/server||
|
||||
|bgs|https://github.com/bluesky-social/indigo/tree/main/cmd/bigsky||
|
||||
|feed|https://github.com/bluesky-social/feed-generator||
|
||||
|web|https://github.com/bluesky-social/social-app|[bluesky-selfhost-env](https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/160-social-app-disable-hackModifyThumbnailPath.diff)|
|
||||
|
||||
```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
|
||||
# BSKY_IMG_URI_ENDPOINT, BSKY_BLOB_CACHE_LOC
|
||||
# avatar link example
|
||||
1. https://appview.${host}/img/avatar/plain/${did}/${cid}@jpeg
|
||||
2. https://${host}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}
|
||||
```
|
||||
|
||||
docker compose will not be published unless you write ports. it is only valid internally. add ports only for what you want to publish.
|
||||
|
||||
## api
|
||||
|
||||
```sh
|
||||
# create account
|
||||
url=https://${pds}/xrpc/com.atproto.server.createAccount
|
||||
json="{\"email\": \"$email\", \"handle\": \"$handle\", \"password\": \"$password\"}"
|
||||
curl -X POST -H "Content-Type: application/json" -d $json -sL $url
|
||||
```
|
||||
|
||||
change `src/pds/handle` to use a name of 3 characters or less. also, you cannot create an account with a name of 3 characters or less from social-app (web client). please create it from api.
|
||||
|
||||
- [/atproto/packages/pds/src/handle/index.ts](https://github.com/bluesky-social/atproto/blame/d4d5a6edba972c0e9976289bde8bc0b42ff547ca/packages/pds/src/handle/index.ts#L86-L88)
|
||||
|
||||
```sh
|
||||
# invite code
|
||||
admin_password=xxx
|
||||
url=https://$host/xrpc/com.atproto.server.createInviteCode
|
||||
json="{\"useCount\":1}"
|
||||
curl -X POST -u admin:${admin_password} -H "Content-Type: application/json" -d "$json" -sL $url
|
||||
```
|
||||
|
||||
## oauth
|
||||
|
||||
```sh
|
||||
# https://github.com/bluesky-social/cookbook/tree/main/python-oauth-web-app
|
||||
$ cd ./repos/cookbook/python-oauth-web-app
|
||||
$ rye sync
|
||||
$ rye run python3 -c 'import secrets; print(secrets.token_hex())'|xargs echo FLASK_SECRET_KEY|tr -d ' ' >> .env
|
||||
$ rye run python3 generate_jwk.py |xargs echo FLASK_CLIENT_SECRET_JWK|tr -d ' ' >> .env
|
||||
$ cat .env
|
||||
$ rye run flask run
|
||||
```
|
||||
|
||||
please access `127.0.0.1:5000`. it may not work if you use localhost.
|
||||
|
||||
also, oauth doesn't work on localhost. use [ngrok](https://ngrok.com/), [tailscale](https://tailscale.com/), [cloudflare](https://github.com/cloudflare/cloudflared).
|
||||
|
||||
```sh
|
||||
$ ngrok http http://localhost:5000
|
||||
```
|
||||
|
||||
```sh
|
||||
$ cloudflared tunnel --url http://localhost:5000
|
||||
```
|
||||
|
||||
47
at.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"atmosphere": {
|
||||
"name": "at",
|
||||
"repo": "https://github.com/bluesky-social/atproto",
|
||||
"uri": "https://atproto.com/ja/guides/glossary",
|
||||
"exosphere": {
|
||||
"km":[{ "min": 700, "max": 10000 }],
|
||||
"tag": [ "universe" ]
|
||||
},
|
||||
"thermosphere": {
|
||||
"km":[{ "min": 80, "max": 700 }],
|
||||
"tag": [ "aurora" ]
|
||||
},
|
||||
"mesosphere": {
|
||||
"km":[{ "min": 50, "max": 80 }],
|
||||
"tag": [ "meteor" ]
|
||||
},
|
||||
"stratosphere": {
|
||||
"km":[{ "min": 12, "max": 50 }],
|
||||
"tag": [ "ozone", "bigsky" ],
|
||||
"service":[
|
||||
{ "name":"ozone", "repo":"https://github.com/bluesky-social/atproto/tree/main/services/ozone" },
|
||||
{ "name":"bgs", "repo":"https://github.com/bluesky-social/indigo/tree/main/cmd/bigsky" }
|
||||
]
|
||||
},
|
||||
"troposphere": {
|
||||
"km":[{ "min": 0, "max": 12 }],
|
||||
"tag": [ "bluesky"],
|
||||
"service":[
|
||||
{ "name":"bsky", "repo":"https://github.com/bluesky-social/atproto/tree/main/services/bsky", "tag":[ "api", "appview" ] },
|
||||
{ "name":"bsync","repo":"https://github.com/bluesky-social/atproto/tree/main/services/bsync" },
|
||||
{ "name":"pds", "repo":"https://github.com/bluesky-social/atproto/tree/main/services/pds" }
|
||||
]
|
||||
},
|
||||
"other": {
|
||||
"tag": [ "plc", "feed", "oauth", "social-app", "stream" ],
|
||||
"service":[
|
||||
{ "name":"plc", "repo":"https://github.com/did-method-plc/did-method-plc/tree/main/packages/server", "tag" : [ "did" ] },
|
||||
{ "name":"social-app", "repo":"https://github.com/bluesky-social/social-app", "tag": [ "web" ] },
|
||||
{ "name":"oauth", "repo":"https://github.com/bluesky-social/cookbook/tree/main/python-oauth-web-app" },
|
||||
{ "name":"feed", "repo":"https://github.com/bluesky-social/feed-generator" },
|
||||
{ "name":"stream", "repo":"https://github.com/bluesky-social/jetstream" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"ref": "https://en.wikipedia.org/wiki/Atmosphere_of_Earth"
|
||||
}
|
||||
129
compose.yml
@@ -48,11 +48,36 @@ services:
|
||||
- ./envs/pds
|
||||
volumes:
|
||||
- ./data/pds/:/data/
|
||||
command: node --enable-source-maps index.js
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
bgs:
|
||||
ports:
|
||||
- 2470:2470
|
||||
build:
|
||||
context: ./repos/indigo/
|
||||
dockerfile: cmd/bigsky/Dockerfile
|
||||
restart: always
|
||||
env_file:
|
||||
- ./envs/bgs
|
||||
volumes:
|
||||
- ./data/bgs/:/data/
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
social-app:
|
||||
ports:
|
||||
- 8100:8100
|
||||
build:
|
||||
context: ./repos/social-app/
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
env_file:
|
||||
- ./envs/social-app
|
||||
command: "/usr/bin/bskyweb serve"
|
||||
|
||||
bsky:
|
||||
ports:
|
||||
- 2584:2584
|
||||
@@ -72,67 +97,6 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
bgs:
|
||||
ports:
|
||||
- 2470:2470
|
||||
build:
|
||||
context: ./repos/indigo/
|
||||
dockerfile: cmd/bigsky/Dockerfile
|
||||
restart: always
|
||||
env_file:
|
||||
- ./envs/bgs
|
||||
volumes:
|
||||
- ./data/bgs/:/data/
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
#command: ["/bigsky", "--crawl-insecure-ws"]
|
||||
|
||||
social-app:
|
||||
ports:
|
||||
- 8100:8100
|
||||
build:
|
||||
context: ./repos/social-app/
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
env_file:
|
||||
- ./envs/social-app
|
||||
command: "/usr/bin/bskyweb serve"
|
||||
|
||||
ozone:
|
||||
ports:
|
||||
- 2585:3000
|
||||
build:
|
||||
context: ./repos/atproto/
|
||||
dockerfile: services/ozone/Dockerfile
|
||||
restart: always
|
||||
command: node --enable-source-maps api.js
|
||||
volumes:
|
||||
- ./data/ozone/:/data/
|
||||
env_file:
|
||||
- ./envs/ozone
|
||||
healthcheck:
|
||||
test: "wget -q --spider http://localhost:3000/xrpc/_health"
|
||||
interval: 5s
|
||||
retries: 20
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
ozone-web:
|
||||
ports:
|
||||
- 2586:3000
|
||||
build:
|
||||
context: ./repos/ozone/
|
||||
restart: always
|
||||
volumes:
|
||||
- ./data/ozone/:/data/
|
||||
env_file:
|
||||
- ./envs/ozone
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
jetstream:
|
||||
build:
|
||||
context: ./repos/jetstream/
|
||||
@@ -145,13 +109,40 @@ services:
|
||||
env_file:
|
||||
- ./envs/jetstream
|
||||
|
||||
feed:
|
||||
ports:
|
||||
- 2587:3000
|
||||
ozone-web:
|
||||
build:
|
||||
context: ./repos/feed-generator/
|
||||
context: ./repos/ozone/
|
||||
ports:
|
||||
- 2586:3000
|
||||
restart: always
|
||||
env_file:
|
||||
- ./envs/feed
|
||||
volumes:
|
||||
- ./data/feed:/data/
|
||||
- ./data/ozone/:/data/
|
||||
env_file:
|
||||
- ./envs/ozone
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
ozone:
|
||||
build:
|
||||
context: ./repos/atproto/
|
||||
dockerfile: services/ozone/Dockerfile
|
||||
ports:
|
||||
- 2585:3000
|
||||
restart: always
|
||||
command: node --enable-source-maps api.js
|
||||
volumes:
|
||||
- ./data/ozone/:/data/
|
||||
env_file:
|
||||
- ./envs/ozone
|
||||
|
||||
ozone-daemon:
|
||||
build:
|
||||
context: ./repos/atproto/
|
||||
dockerfile: services/ozone/Dockerfile
|
||||
restart: always
|
||||
command: node --enable-source-maps daemon.js
|
||||
volumes:
|
||||
- ./data/ozone/:/data/
|
||||
env_file:
|
||||
- ./envs/ozone
|
||||
|
||||
4
envs/bgs
@@ -2,5 +2,5 @@ DATABASE_URL=postgres://postgres:postgres@database/bgs
|
||||
CARSTORE_DATABASE_URL=postgres://postgres:postgres@database/carstore
|
||||
DATA_DIR=/data
|
||||
ATP_PLC_HOST=https://plc.${host}
|
||||
BGS_NEW_PDS_PER_DAY_LIMIT=1000
|
||||
BGS_ADMIN_KEY=
|
||||
|
||||
BGS_ADMIN_KEY
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
FEEDGEN_PORT=3000
|
||||
FEEDGEN_LISTENHOST=0.0.0.0
|
||||
FEEDGEN_SQLITE_LOCATION=/data/db.sqlite
|
||||
FEEDGEN_HOSTNAME=feed.syu.is
|
||||
FEEDGEN_PUBLISHER_DID=did:plc:6qyecktefllvenje24fcxnie
|
||||
FEEDGEN_SERVICE_DID=did:web:feed.syu.is
|
||||
FEEDGEN_JETSTREAM_URL=ws://jetstream:6008/subscribe
|
||||
@@ -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_LISTEN_ADDR=:6008
|
||||
JETSTREAM_METRICS_LISTEN_ADDR=:6009
|
||||
|
||||
@@ -7,8 +7,8 @@ OZONE_DB_POSTGRES_URL=postgres://postgres:postgres@database/ozone
|
||||
OZONE_DID_PLC_URL=https://plc.${host}
|
||||
NEXT_PUBLIC_PLC_DIRECTORY_URL=https://plc.${host}
|
||||
NEXT_PUBLIC_OZONE_SERVICE_DID=did:web:ozone.${host}
|
||||
NEXT_PUBLIC_SOCIAL_APP_DOMAIN=${host}
|
||||
NEXT_PUBLIC_SOCIAL_APP_URL=https://${host}
|
||||
NEXT_PUBLIC_SOCIAL_APP_DOMAIN=mod.${host}
|
||||
NEXT_PUBLIC_SOCIAL_APP_URL=https://mod.${host}
|
||||
OZONE_APPVIEW_DID=did:web:bsky.${host}
|
||||
OZONE_APPVIEW_URL=https://bsky.${host}
|
||||
OZONE_APPVIEW_PUSH_EVENTS=false
|
||||
@@ -25,5 +25,3 @@ OZONE_VERIFIER_DID
|
||||
OZONE_VERIFIER_PASSWORD
|
||||
OZONE_VERIFIER_ISSUERS_TO_INDEX
|
||||
OZONE_VERIFIER_JETSTREAM_URL
|
||||
|
||||
OZONE_APPVIEW_PUSH_EVENTS=true
|
||||
|
||||
2
envs/pds
@@ -6,8 +6,6 @@ PDS_BLOBSTORE_DISK_LOCATION=/data/img/static
|
||||
PDS_BSKY_APP_VIEW_DID=did:web:bsky.${host}
|
||||
PDS_BSKY_APP_VIEW_URL=https://bsky.${host}
|
||||
PDS_CRAWLERS=https://bgs.${host}
|
||||
PDS_SEQUENCER_ENABLED=true
|
||||
PDS_SEQUENCER_DB_LOCATION=/data/sequencer.sqlite
|
||||
PDS_DEV_MODE=true
|
||||
PDS_DID_PLC_URL=https://plc.${host}
|
||||
PDS_ENABLE_DID_DOC_WITH_SESSION=true
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
ATP_APPVIEW_HOST=https://public.api.bsky.app
|
||||
EXPO_PUBLIC_BLUESKY_PROXY_DID=did:web:api.bsky.app
|
||||
EXPO_PUBLIC_ENV=production
|
||||
ATP_APPVIEW_HOST=https://bsky.${host}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>App Info - Aiat</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #000; color: #e0e0e0; }
|
||||
a { color: #6bb3ff; }
|
||||
h1, h2, h3 { color: #fff; }
|
||||
.section { background: #1a1a1a; }
|
||||
.info-item { background: #2a2a2a; }
|
||||
}
|
||||
.header { margin-bottom: 32px; }
|
||||
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; color: #0066cc; text-decoration: none; }
|
||||
.back-link:hover { text-decoration: underline; }
|
||||
.app-header { text-align: center; margin-bottom: 32px; }
|
||||
.app-icon { width: 80px; height: 80px; border-radius: 18px; margin-bottom: 12px; }
|
||||
.app-name { font-size: 24px; font-weight: bold; margin-bottom: 4px; }
|
||||
.app-version { font-size: 14px; color: #666; }
|
||||
.section { background: #f5f5f5; border-radius: 16px; padding: 20px; margin-bottom: 16px; }
|
||||
.section-title { font-size: 13px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
|
||||
.description { font-size: 15px; line-height: 22px; }
|
||||
.info-grid { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.info-item { flex: 1; min-width: 45%; text-align: center; background: #e8e8e8; border-radius: 12px; padding: 12px; }
|
||||
.info-label { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
|
||||
.info-value { font-size: 16px; font-weight: 600; }
|
||||
.developer-name { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
|
||||
.link-row { display: flex; align-items: center; padding: 12px 0; border-top: 1px solid rgba(0,0,0,0.1); }
|
||||
.link-icon { font-size: 14px; font-weight: 600; color: #666; width: 70px; }
|
||||
.link-value { flex: 1; font-size: 14px; color: #0084ff; text-decoration: none; }
|
||||
.link-value:hover { text-decoration: underline; }
|
||||
.link-arrow { font-size: 16px; color: #ccc; }
|
||||
.bitcoin-row { display: flex; align-items: center; background: rgba(247, 147, 26, 0.08); border-radius: 12px; padding: 14px; gap: 10px; }
|
||||
.bitcoin-label { font-size: 18px; font-weight: 600; color: #f7931a; }
|
||||
.bitcoin-address { flex: 1; font-size: 11px; font-family: monospace; color: #666; word-break: break-all; }
|
||||
.copy-btn { font-size: 12px; color: #999; cursor: pointer; min-width: 50px; text-align: right; }
|
||||
.copy-btn:hover { color: #0084ff; }
|
||||
.footer { text-align: center; margin-top: 32px; padding-top: 20px; border-top: 1px solid #ddd; }
|
||||
.copyright { font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="/" class="back-link">← Back to syu.is</a>
|
||||
</div>
|
||||
|
||||
<div class="app-header">
|
||||
<img src="/static/app.png" alt="Aiat" class="app-icon">
|
||||
<div class="app-name">Aiat</div>
|
||||
<div class="app-version">v1.111.0</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<p class="description">Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">App Information</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Version</div>
|
||||
<div class="info-value">1.111.0</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Category</div>
|
||||
<div class="info-value">Social</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Supported OS</div>
|
||||
<div class="info-value">iOS 26.0+</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Price</div>
|
||||
<div class="info-value">Free</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Developer</div>
|
||||
<div class="developer-name">syui</div>
|
||||
<div class="link-row">
|
||||
<span class="link-icon">Git</span>
|
||||
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
|
||||
<span class="link-arrow">→</span>
|
||||
</div>
|
||||
<div class="link-row">
|
||||
<span class="link-icon">ATProto</span>
|
||||
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
|
||||
<span class="link-arrow">→</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Bitcoin</div>
|
||||
<div class="bitcoin-row">
|
||||
<span class="bitcoin-label">₿</span>
|
||||
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
|
||||
<span class="copy-btn" onclick="copyBTC()">copy</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p class="copyright">© syui</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyBTC() {
|
||||
const addr = document.getElementById('btc-address').textContent;
|
||||
navigator.clipboard.writeText(addr).then(() => {
|
||||
const btn = document.querySelector('.copy-btn');
|
||||
btn.textContent = 'copied!';
|
||||
btn.style.color = '#4CAF50';
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'copy';
|
||||
btn.style.color = '';
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,100 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>Help - syu.is</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #000; color: #e0e0e0; }
|
||||
a { color: #6bb3ff; }
|
||||
h1, h2, h3 { color: #fff; }
|
||||
}
|
||||
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||
h3 { font-size: 16px; margin: 16px 0 8px; }
|
||||
p { margin-bottom: 16px; }
|
||||
ul { margin: 0 0 16px 24px; }
|
||||
li { margin-bottom: 8px; }
|
||||
a { color: #0066cc; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.header { margin-bottom: 32px; }
|
||||
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||
.faq-item { margin-bottom: 24px; }
|
||||
.contact-box { background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.contact-box { background: #1a1a1a; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="/" class="back-link">← Back to syu.is</a>
|
||||
<h1>Help Center</h1>
|
||||
</div>
|
||||
|
||||
<h2>About syu.is</h2>
|
||||
<p>syu.is is a social networking service built on the AT Protocol (Authenticated Transfer Protocol). It allows users to share content, connect with others, and participate in a decentralized social network.</p>
|
||||
|
||||
<h2>Frequently Asked Questions</h2>
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>What is the AT Protocol?</h3>
|
||||
<p>The AT Protocol is a decentralized social networking protocol that allows users to own their data and identity. It enables federation between different services while maintaining user control.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>How do I create an account?</h3>
|
||||
<p>You can create an account by downloading the app or visiting the website. You'll need to provide an email address and choose a username.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>How do I reset my password?</h3>
|
||||
<p>You can reset your password through the login screen by selecting "Forgot Password" and following the instructions sent to your email.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>How do I delete my account?</h3>
|
||||
<p>You can delete your account through Settings > Account. Please note that account deletion is permanent and cannot be undone.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>How do I report abuse or inappropriate content?</h3>
|
||||
<p>You can report content by using the report function available on each post. Our moderation team will review reports and take appropriate action.</p>
|
||||
</div>
|
||||
|
||||
<h2>Contact</h2>
|
||||
<div class="contact-box">
|
||||
<p>For additional support or questions:</p>
|
||||
<ul>
|
||||
<li>GitHub: <a href="https://github.com/syui" target="_blank">github.com/syui</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>Related Links</h2>
|
||||
<ul>
|
||||
<li><a href="/about/support/tos">Terms of Service</a></li>
|
||||
<li><a href="/about/support/privacy-policy">Privacy Policy</a></li>
|
||||
<li><a href="/about/support/license">License</a></li>
|
||||
<li><a href="/about/support/app">App Info</a></li>
|
||||
<li><a href="https://atproto.com" target="_blank">AT Protocol Documentation</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="footer">
|
||||
<p>Last updated: 2025</p>
|
||||
<p>© syui</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,66 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>License - syu.is</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #000; color: #e0e0e0; }
|
||||
a { color: #6bb3ff; }
|
||||
h1, h2, h3 { color: #fff; }
|
||||
}
|
||||
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||
p { margin-bottom: 16px; }
|
||||
ul { margin: 0 0 16px 24px; }
|
||||
li { margin-bottom: 8px; }
|
||||
a { color: #0066cc; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.header { margin-bottom: 32px; }
|
||||
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||
pre { background: #f5f5f5; padding: 16px; border-radius: 8px; overflow-x: auto; font-size: 13px; }
|
||||
@media (prefers-color-scheme: dark) { pre { background: #1a1a1a; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="/" class="back-link">← Back to syu.is</a>
|
||||
<h1>License</h1>
|
||||
</div>
|
||||
|
||||
<h2>Aiat (iOS/Android App)</h2>
|
||||
<p>This application is based on the Bluesky Social App, which is open source software.</p>
|
||||
|
||||
<h2>Open Source Licenses</h2>
|
||||
<p>This app uses the following open source software:</p>
|
||||
|
||||
<h3>Bluesky Social App</h3>
|
||||
<p>Licensed under the MIT License</p>
|
||||
<p><a href="https://github.com/bluesky-social/social-app" target="_blank">https://github.com/bluesky-social/social-app</a></p>
|
||||
|
||||
<h3>AT Protocol</h3>
|
||||
<p>Licensed under the MIT License / Apache 2.0</p>
|
||||
<p><a href="https://github.com/bluesky-social/atproto" target="_blank">https://github.com/bluesky-social/atproto</a></p>
|
||||
|
||||
<h2>Third Party Libraries</h2>
|
||||
<p>This application includes various third-party libraries, each with their own licenses. For a complete list, please see the application's source code repository.</p>
|
||||
|
||||
<div class="footer">
|
||||
<p>Last updated: 2025</p>
|
||||
<p>© syui</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,92 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>Privacy Policy - syu.is</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #000; color: #e0e0e0; }
|
||||
a { color: #6bb3ff; }
|
||||
h1, h2, h3 { color: #fff; }
|
||||
}
|
||||
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||
p { margin-bottom: 16px; }
|
||||
ul { margin: 0 0 16px 24px; }
|
||||
li { margin-bottom: 8px; }
|
||||
a { color: #0066cc; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.header { margin-bottom: 32px; }
|
||||
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="/" class="back-link">← Back to syu.is</a>
|
||||
<h1>Privacy Policy</h1>
|
||||
</div>
|
||||
|
||||
<h2>1. Introduction</h2>
|
||||
<p>This Privacy Policy explains how syu.is collects, uses, and protects your personal information when you use our service.</p>
|
||||
|
||||
<h2>2. Information We Collect</h2>
|
||||
<p>We collect the following types of information:</p>
|
||||
<ul>
|
||||
<li><strong>Account Information:</strong> Email address, username, and profile information you provide</li>
|
||||
<li><strong>Content:</strong> Posts, messages, and other content you create on the platform</li>
|
||||
<li><strong>Usage Data:</strong> Information about how you interact with our service</li>
|
||||
<li><strong>Device Information:</strong> Browser type, operating system, and device identifiers</li>
|
||||
</ul>
|
||||
|
||||
<h2>3. How We Use Your Information</h2>
|
||||
<p>We use your information to:</p>
|
||||
<ul>
|
||||
<li>Provide and maintain our service</li>
|
||||
<li>Improve and personalize your experience</li>
|
||||
<li>Communicate with you about the service</li>
|
||||
<li>Ensure security and prevent abuse</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. Data Sharing</h2>
|
||||
<p>As part of the AT Protocol federation, your public content may be shared with other servers in the network. We do not sell your personal information to third parties.</p>
|
||||
|
||||
<h2>5. Data Security</h2>
|
||||
<p>We implement appropriate security measures to protect your personal information. However, no method of transmission over the Internet is 100% secure.</p>
|
||||
|
||||
<h2>6. Your Rights</h2>
|
||||
<p>You have the right to:</p>
|
||||
<ul>
|
||||
<li>Access your personal data</li>
|
||||
<li>Request correction of your data</li>
|
||||
<li>Request deletion of your account</li>
|
||||
<li>Export your data</li>
|
||||
</ul>
|
||||
|
||||
<h2>7. Cookies</h2>
|
||||
<p>We use cookies and similar technologies to maintain your session and improve your experience.</p>
|
||||
|
||||
<h2>8. Changes to This Policy</h2>
|
||||
<p>We may update this Privacy Policy from time to time. We will notify you of any significant changes.</p>
|
||||
|
||||
<h2>9. Contact</h2>
|
||||
<p>For privacy-related questions, please visit our <a href="/about/support/help">Help page</a>.</p>
|
||||
|
||||
<div class="footer">
|
||||
<p>Last updated: 2025</p>
|
||||
<p>© syui</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,84 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>Terms of Service - syu.is</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #000; color: #e0e0e0; }
|
||||
a { color: #6bb3ff; }
|
||||
h1, h2, h3 { color: #fff; }
|
||||
}
|
||||
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||
p { margin-bottom: 16px; }
|
||||
ul { margin: 0 0 16px 24px; }
|
||||
li { margin-bottom: 8px; }
|
||||
a { color: #0066cc; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.header { margin-bottom: 32px; }
|
||||
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="/" class="back-link">← Back to syu.is</a>
|
||||
<h1>Terms of Service</h1>
|
||||
</div>
|
||||
|
||||
<h2>1. Introduction</h2>
|
||||
<p>Welcome to syu.is. By using our service, you agree to these terms. Please read them carefully.</p>
|
||||
|
||||
<h2>2. Service Description</h2>
|
||||
<p>syu.is is a social networking service built on the AT Protocol. We provide a platform for users to share content and connect with others.</p>
|
||||
|
||||
<h2>3. User Responsibilities</h2>
|
||||
<p>As a user of syu.is, you agree to:</p>
|
||||
<ul>
|
||||
<li>Provide accurate information when creating an account</li>
|
||||
<li>Keep your account credentials secure</li>
|
||||
<li>Not use the service for illegal activities</li>
|
||||
<li>Respect other users and their content</li>
|
||||
<li>Comply with applicable laws and regulations</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. Content Guidelines</h2>
|
||||
<p>Users are responsible for the content they post. Prohibited content includes:</p>
|
||||
<ul>
|
||||
<li>Illegal content</li>
|
||||
<li>Harassment or abuse</li>
|
||||
<li>Spam or misleading information</li>
|
||||
<li>Content that violates others' rights</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. Privacy</h2>
|
||||
<p>Your privacy is important to us. Please review our <a href="/about/support/privacy-policy">Privacy Policy</a> to understand how we handle your data.</p>
|
||||
|
||||
<h2>6. Disclaimer</h2>
|
||||
<p>The service is provided "as is" without warranties of any kind. We are not liable for any damages arising from your use of the service.</p>
|
||||
|
||||
<h2>7. Changes to Terms</h2>
|
||||
<p>We may update these terms from time to time. Continued use of the service after changes constitutes acceptance of the new terms.</p>
|
||||
|
||||
<h2>8. Contact</h2>
|
||||
<p>For questions about these terms, please visit our <a href="/about/support/help">Help page</a>.</p>
|
||||
|
||||
<div class="footer">
|
||||
<p>Last updated: 2025</p>
|
||||
<p>© syu.is</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
135
html/index.html
@@ -1,135 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>App Info - Aiat</title>
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #000; color: #e0e0e0; }
|
||||
a { color: #6bb3ff; }
|
||||
h1, h2, h3 { color: #fff; }
|
||||
.section { background: #1a1a1a; }
|
||||
.info-item { background: #2a2a2a; }
|
||||
}
|
||||
.header { margin-bottom: 32px; }
|
||||
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; color: #0066cc; text-decoration: none; }
|
||||
.back-link:hover { text-decoration: underline; }
|
||||
.app-header { text-align: center; margin-bottom: 32px; }
|
||||
.app-icon { width: 80px; height: 80px; border-radius: 18px; margin-bottom: 12px; }
|
||||
.app-name { font-size: 24px; font-weight: bold; margin-bottom: 4px; }
|
||||
.app-version { font-size: 14px; color: #666; }
|
||||
.section { background: #f5f5f5; border-radius: 16px; padding: 20px; margin-bottom: 16px; }
|
||||
.section-title { font-size: 13px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
|
||||
.description { font-size: 15px; line-height: 22px; }
|
||||
.info-grid { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.info-item { flex: 1; min-width: 45%; text-align: center; background: #e8e8e8; border-radius: 12px; padding: 12px; }
|
||||
.info-label { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
|
||||
.info-value { font-size: 16px; font-weight: 600; }
|
||||
.developer-name { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
|
||||
.link-row { display: flex; align-items: center; padding: 12px 0; border-top: 1px solid rgba(0,0,0,0.1); }
|
||||
.link-icon { font-size: 14px; font-weight: 600; color: #666; width: 70px; }
|
||||
.link-value { flex: 1; font-size: 14px; color: #0084ff; text-decoration: none; }
|
||||
.link-value:hover { text-decoration: underline; }
|
||||
.link-arrow { font-size: 16px; color: #ccc; }
|
||||
.bitcoin-row { display: flex; align-items: center; background: rgba(247, 147, 26, 0.08); border-radius: 12px; padding: 14px; gap: 10px; }
|
||||
.bitcoin-label { font-size: 18px; font-weight: 600; color: #f7931a; }
|
||||
.bitcoin-address { flex: 1; font-size: 11px; font-family: monospace; color: #666; word-break: break-all; }
|
||||
.copy-btn { font-size: 12px; color: #999; cursor: pointer; min-width: 50px; text-align: right; }
|
||||
.copy-btn:hover { color: #0084ff; }
|
||||
.footer { text-align: center; margin-top: 32px; padding-top: 20px; border-top: 1px solid #ddd; }
|
||||
.copyright { font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="/" class="back-link">← Back to syu.is</a>
|
||||
</div>
|
||||
|
||||
<div class="app-header">
|
||||
<img src="/static/app.png" alt="Aiat" class="app-icon">
|
||||
<div class="app-name">Aiat</div>
|
||||
<div class="app-version">v1.111.0</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<p class="description">Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">App Information</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Version</div>
|
||||
<div class="info-value">1.111.2</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Category</div>
|
||||
<div class="info-value">Social</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Supported OS</div>
|
||||
<div class="info-value">iOS 26.0+</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Price</div>
|
||||
<div class="info-value">Free</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Developer</div>
|
||||
<div class="developer-name">syui</div>
|
||||
<div class="link-row">
|
||||
<span class="link-icon">Git</span>
|
||||
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
|
||||
<span class="link-arrow">→</span>
|
||||
</div>
|
||||
<div class="link-row">
|
||||
<span class="link-icon">ATProto</span>
|
||||
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
|
||||
<span class="link-arrow">→</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Bitcoin</div>
|
||||
<div class="bitcoin-row">
|
||||
<span class="bitcoin-label">₿</span>
|
||||
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
|
||||
<span class="copy-btn" onclick="copyBTC()">copy</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p class="copyright">© syui</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyBTC() {
|
||||
const addr = document.getElementById('btc-address').textContent;
|
||||
navigator.clipboard.writeText(addr).then(() => {
|
||||
const btn = document.querySelector('.copy-btn');
|
||||
btn.textContent = 'copied!';
|
||||
btn.style.color = '#4CAF50';
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'copy';
|
||||
btn.style.color = '';
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 1005 B |
52
icons/Logotype.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from 'react'
|
||||
import Svg, {Path, SvgProps, PathProps} from 'react-native-svg'
|
||||
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
|
||||
const ratio = 17 / 64
|
||||
|
||||
export function Logotype({
|
||||
fill,
|
||||
...rest
|
||||
}: {fill?: PathProps['fill']} & SvgProps) {
|
||||
const pal = usePalette('default')
|
||||
// @ts-ignore it's fiiiiine
|
||||
const size = parseInt(rest.width || 32)
|
||||
|
||||
return (
|
||||
<Svg
|
||||
fill="none"
|
||||
viewBox="0 0 2821.6379 794.29016"
|
||||
{...rest}
|
||||
width={size}
|
||||
height={Number(size) * ratio}>
|
||||
<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 After Width: | Height: | Size: 4.0 KiB |
645
install.zsh
@@ -1,85 +1,52 @@
|
||||
#!/bin/zsh
|
||||
|
||||
# Sed compatibility wrapper
|
||||
function sediment() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Patch compatibility wrapper
|
||||
function patchment() {
|
||||
# -f : Force. Do not ask questions. (Standard in GNU and BSD patch)
|
||||
# -N : Ignore patches that seem to be reversed or already applied (Forward)
|
||||
# But we control these flags in the caller.
|
||||
patch "$@"
|
||||
}
|
||||
|
||||
# ./install.zsh $HOST
|
||||
repos_v='{}'
|
||||
function at-repos-env() {
|
||||
APP_PASSWORD=xxx
|
||||
host=syu.is
|
||||
handle=ai.syui.ai
|
||||
host=$1
|
||||
if [ -z "$1" ];then
|
||||
host=syu.is
|
||||
fi
|
||||
did=did:plc:6qyecktefllvenje24fcxnie
|
||||
icon=https://git.syui.ai/ai/at/raw/branch/main/icons/Logotype.tsx
|
||||
repos=(
|
||||
"https://github.com/did-method-plc/did-method-plc"
|
||||
"https://github.com/bluesky-social/indigo"
|
||||
"https://github.com/bluesky-social/atproto"
|
||||
"https://github.com/bluesky-social/social-app"
|
||||
"https://github.com/bluesky-social/feed-generator"
|
||||
"https://github.com/bluesky-social/ozone"
|
||||
"https://github.com/bluesky-social/jetstream"
|
||||
)
|
||||
services=(
|
||||
"bsky"
|
||||
"plc"
|
||||
"pds"
|
||||
"jetstream"
|
||||
"bgs"
|
||||
"ozone"
|
||||
"social-app"
|
||||
"feed"
|
||||
)
|
||||
handles=(
|
||||
"syui.syui.ai"
|
||||
"ai.syui.ai"
|
||||
"apple.syu.is"
|
||||
https://github.com/did-method-plc/did-method-plc
|
||||
https://github.com/bluesky-social/indigo
|
||||
https://github.com/bluesky-social/atproto
|
||||
https://github.com/bluesky-social/social-app
|
||||
https://github.com/bluesky-social/feed-generator
|
||||
https://github.com/bluesky-social/ozone
|
||||
https://github.com/bluesky-social/jetstream
|
||||
)
|
||||
d=${0:a:h}
|
||||
dh=${0:a:h:h}
|
||||
name=${host%%.*}
|
||||
domain=${host##*.}
|
||||
dport=5000
|
||||
|
||||
typeset -A PINNED_COMMITS
|
||||
PINNED_COMMITS=(
|
||||
[indigo]="d49b454196351c988ceb5ce1f5e21b689487b5ab"
|
||||
[atproto]="104e6ed37b0589cc000109dc76316be35b2257e1"
|
||||
)
|
||||
}
|
||||
|
||||
# Arrays for patch management
|
||||
typeset -a FAILED_PATCHES
|
||||
function at-repos-json() {
|
||||
f=~/.config/atproto/token.json
|
||||
j="{ \"did\": \"did:plc:6qyecktefllvenje24fcxnie\", \"didDoc\": { \"service\": [ { \"serviceEndpoint\": \"https://syu.is\" } ] }, \"handle\": \"ai.syu.is\", \"accessJwt\": \"xxx\" }"
|
||||
if [ ! -f "$f" ];then
|
||||
mkdir -p ~/.config/atproto
|
||||
echo $j >> $f
|
||||
fi
|
||||
echo $f
|
||||
}
|
||||
|
||||
# Patch file lists
|
||||
typeset -a PATCH_FILES_CURL
|
||||
PATCH_FILES_CURL=(
|
||||
"4367-atproto-services-bsky-api.diff:https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/bsky/api.js:services/bsky/api.js"
|
||||
"4367-atproto-services-pds-index.diff:https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/pds/index.js:services/pds/index.js"
|
||||
)
|
||||
|
||||
typeset -a PATCH_FILES
|
||||
PATCH_FILES=(
|
||||
"170-pds-oauth-same-site-fix.patch"
|
||||
"8980-social-app-disable-proxy.diff"
|
||||
"disable-statsig-sdk.diff"
|
||||
"140-social-app-yarn-network-timeout.patch"
|
||||
"130-atproto-ozone-enable-daemon-v2.patch"
|
||||
"190-bgs-disable-ratelimit.patch"
|
||||
"200-feed-generator-custom.patch"
|
||||
)
|
||||
function at-repos-token() {
|
||||
at-repos-json
|
||||
if [ -z "$host" ] && [ -f $f ];then
|
||||
host=`cat $f|jq -r ".didDoc.service.[].serviceEndpoint"`
|
||||
handle=`cat $f|jq -r ".handle"`
|
||||
did=`cat $f|jq -r ".did"`
|
||||
token=`cat $f|jq -r ".token"`
|
||||
host=${host##*/}
|
||||
fi
|
||||
name=${host%%.*}
|
||||
domain=${host##*.}
|
||||
}
|
||||
|
||||
function at-repos-clone() {
|
||||
if [ ! -d $d/repos ];then
|
||||
@@ -91,7 +58,7 @@ function at-repos-clone() {
|
||||
echo $repo
|
||||
if [ ! -d $d/repos/${repo##*/} ];then
|
||||
git clone $repo
|
||||
|
||||
|
||||
fi
|
||||
done
|
||||
if [ ! -f $d/repos/feed-generator/Dockerfile ] && [ -f $d/docker/feed/Dockerfile ];then
|
||||
@@ -106,11 +73,6 @@ function at-repos-pull() {
|
||||
echo $repo
|
||||
if [ -d $d/repos/${repo##*/} ];then
|
||||
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
|
||||
if ! git pull;then
|
||||
rm -rf $d/repos/${repo##*/}
|
||||
at-repos-clone
|
||||
@@ -126,457 +88,136 @@ function at-repos-pull() {
|
||||
cd $d
|
||||
}
|
||||
|
||||
function at-repos-checkout-pinned() {
|
||||
echo "🔒 Checking out pinned commits..."
|
||||
cd $d/repos
|
||||
for repo_name pinned_commit in ${(kv)PINNED_COMMITS}; do
|
||||
if [ -n "$pinned_commit" ] && [ -d "$d/repos/$repo_name" ]; then
|
||||
echo " 📌 $repo_name -> $pinned_commit"
|
||||
cd $d/repos/$repo_name
|
||||
git fetch origin
|
||||
git checkout $pinned_commit
|
||||
cd $d/repos
|
||||
fi
|
||||
done
|
||||
cd $d
|
||||
}
|
||||
|
||||
function at-repos-social-app-ios-patch() {
|
||||
$d/ios/setup.zsh
|
||||
}
|
||||
|
||||
# Common patch function with status detection
|
||||
function apply-patch() {
|
||||
local patch_name=$1
|
||||
local target_dir=$2
|
||||
local patch_file=$3
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Patch: ${patch_name}"
|
||||
echo " Target: ${target_dir}"
|
||||
echo " File: ${patch_file}"
|
||||
|
||||
pushd ${target_dir} > /dev/null
|
||||
|
||||
# Check if patch is already applied (reverse dry-run succeeds)
|
||||
# Use -f to force dry-run to fail instead of asking questions if unapplied
|
||||
if patch -f --dry-run -p1 -R < ${patch_file} > /dev/null 2>&1; then
|
||||
echo "✅ Already applied - skipping"
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 0
|
||||
function at-repos-social-app-icon() {
|
||||
curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/main/src/view/icons/Logotype.tsx -o $d/repos/social-app/src/view/icons/Logotype.tsx
|
||||
if [ -d $d/icons ];then
|
||||
mkdir -p $d/icons
|
||||
fi
|
||||
cp -rf $d/repos/social-app/src/view/icons/Logotype.tsx $d/icons/
|
||||
}
|
||||
|
||||
# Check if patch can be applied (forward dry-run succeeds)
|
||||
if patch -f --dry-run -p1 < ${patch_file} > /dev/null 2>&1; then
|
||||
echo "🔧 Applying patch..."
|
||||
if patch -f -p1 < ${patch_file}; then
|
||||
echo "✅ Applied successfully"
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 0
|
||||
else
|
||||
echo "❌ Failed to apply"
|
||||
FAILED_PATCHES+=("${patch_name} (${patch_file})")
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
function at-repos-social-app-icon-origin() {
|
||||
curl -sL $icon -o $d/icons/Logotype.tsx
|
||||
}
|
||||
|
||||
function at-repos-social-app-write() {
|
||||
did_admin=did:plc:6qyecktefllvenje24fcxnie
|
||||
|
||||
dt=$d/repos/social-app/src
|
||||
cd $dt
|
||||
grep -R syu.is .|cut -d : -f 1|sort -u|xargs sed -i "s/syu.is/${host}/g"
|
||||
grep -R web.syu.is .|cut -d : -f 1|sort -u|xargs sed -i "s/web.syu.is/web.${host}/g"
|
||||
f=$dt/lib/constants.ts
|
||||
sed -i "s/public.api.web/bsky/g" $f
|
||||
f=$dt/view/icons/Logotype.tsx
|
||||
o=$d/icons/Logotype.tsx
|
||||
cp -rf $o $f
|
||||
|
||||
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
|
||||
sed -i "s#/img/avatar/plain/#https://cdn.web.syu.is/img/avatar/plain/#g" $f
|
||||
sed -i "s#/img/avatar_thumbnail/plain/#https://bsky.${host}/img/avatar/plain/#g" $f
|
||||
sed -i "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
|
||||
sed -i "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 sed -i "s/${did_admin}/${did}/g"
|
||||
}
|
||||
|
||||
function at-repos-bsky-patch() {
|
||||
f=$d/repos/atproto/services/bsky/api.js
|
||||
curl -sL https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/bsky/api.js -o $f
|
||||
d_=$d/repos/atproto
|
||||
p_=$d/patching/105-atproto-services-for-docker.diff
|
||||
cd ${d_}
|
||||
if [ ! -f ${p_} ];then
|
||||
# https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff
|
||||
echo download patch: https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff
|
||||
curl -sL https://raw.githubusercontent.com/itaru2622/bluesky-selfhost-env/refs/heads/master/patching/105-atproto-services-for-docker.diff -o ${p_}
|
||||
else
|
||||
echo "⚠️ Cannot apply - file may have been modified"
|
||||
echo " Please check manually"
|
||||
FAILED_PATCHES+=("${patch_name} (${patch_file}) - file modified")
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 1
|
||||
echo local patch
|
||||
fi
|
||||
echo "applying patch: under ${f} for ${p_}"
|
||||
pushd ${d_}
|
||||
patch -p1 < ${p_}
|
||||
popd
|
||||
}
|
||||
|
||||
# Function to display failed patches summary
|
||||
function show-failed-patches() {
|
||||
if [ ${#FAILED_PATCHES[@]} -eq 0 ]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ All patches applied successfully!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "⚠️ FAILED PATCHES SUMMARY"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "The following patches could not be applied:"
|
||||
echo ""
|
||||
for failed_patch in "${FAILED_PATCHES[@]}"; do
|
||||
echo " ❌ ${failed_patch}"
|
||||
done
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Helper function for applying patches
|
||||
function patch-apply() {
|
||||
local name=$1
|
||||
local target=$2
|
||||
local patch_file=$3
|
||||
apply-patch "${name}" "$d/repos/${target}" "$d/patching/${patch_file}"
|
||||
}
|
||||
|
||||
# Helper function for patches with curl download
|
||||
function patch-apply-with-curl() {
|
||||
local name=$1
|
||||
local target=$2
|
||||
local patch_file=$3
|
||||
local download_url=$4
|
||||
local download_target=$5
|
||||
|
||||
curl -sL "${download_url}" -o "$d/repos/${target}/${download_target}"
|
||||
apply-patch "${name}" "$d/repos/${target}" "$d/patching/${patch_file}"
|
||||
}
|
||||
|
||||
# Auto-apply patches from list
|
||||
function at-repos-patch-apply-all() {
|
||||
# Apply curl patches
|
||||
for patch_info in "${PATCH_FILES_CURL[@]}"; do
|
||||
local filename="${patch_info%%:*}"
|
||||
local rest="${patch_info#*:}"
|
||||
local download_url="${rest%%:*}"
|
||||
local download_target="${rest#*:}"
|
||||
|
||||
local title="${filename%.*}"
|
||||
local repo=""
|
||||
|
||||
# Determine repo from filename
|
||||
if [[ $filename == *"atproto"* ]]; then
|
||||
repo="atproto"
|
||||
elif [[ $filename == *"pds"* ]]; then
|
||||
repo="atproto"
|
||||
fi
|
||||
|
||||
patch-apply-with-curl "$title" "$repo" "$filename" "$download_url" "$download_target"
|
||||
done
|
||||
|
||||
# Apply regular patches
|
||||
for filename in "${PATCH_FILES[@]}"; do
|
||||
local title="${filename%.*}"
|
||||
local repo=""
|
||||
|
||||
# Determine repo from filename
|
||||
if [[ $filename == *"social-app"* || $filename == *"statsig"* ]]; then
|
||||
repo="social-app"
|
||||
elif [[ $filename == *"atproto"* ]]; then
|
||||
repo="atproto"
|
||||
elif [[ $filename == *"pds"* ]]; then
|
||||
repo="atproto"
|
||||
elif [[ $filename == *"indigo"* || $filename == *"bgs"* ]]; then
|
||||
repo="indigo"
|
||||
elif [[ $filename == *"feed"* ]]; then
|
||||
repo="feed-generator"
|
||||
fi
|
||||
|
||||
patch-apply "$title" "$repo" "$filename"
|
||||
done
|
||||
function at-repos-social-app-patch() {
|
||||
f=$d/repos/social-app/Dockerfile
|
||||
p_=$d/patching/social-app-dockerfile.diff
|
||||
d_=$d/repos/social-app
|
||||
cd ${d_}
|
||||
curl -sLO https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/Dockerfile
|
||||
echo "applying patch: under ${f} for ${p_}"
|
||||
pushd ${d_}
|
||||
patch -p1 < ${p_}
|
||||
popd
|
||||
}
|
||||
|
||||
function at-repos-ozone-patch() {
|
||||
#DOMAIN=syu.is
|
||||
cd $d/repos
|
||||
d_=$d/repos/ozone
|
||||
rm -rf ${d_}
|
||||
p_=$d/patching/120-ozone-runtimeEnvVars.diff
|
||||
git clone https://github.com/bluesky-social/ozone
|
||||
cd ${d_}
|
||||
pushd ${d_}
|
||||
echo "applying patch: under ${d_} for ${p_}"
|
||||
patch -p1 < ${p_}
|
||||
popd
|
||||
|
||||
apply-patch "Ozone enable daemon" "${d_}" "$d/patching/122-ozone-enable-daemon.diff"
|
||||
p_=$d/patching/122-ozone-enable-daemon.diff
|
||||
echo "applying patch: under ${d_} for ${p_}"
|
||||
pushd ${d_}
|
||||
patch -p1 < ${p_}
|
||||
popd
|
||||
|
||||
if [ -f "$d/patching/150-ozone-plc-fix.patch" ]; then
|
||||
apply-patch "Ozone plc fix" "${d_}" "$d/patching/150-ozone-plc-fix.patch"
|
||||
fi
|
||||
|
||||
if [ -f "$d/patching/160-ozone-oauth-redirect-fix.patch" ]; then
|
||||
apply-patch "Ozone oauth redirect fix" "${d_}" "$d/patching/160-ozone-oauth-redirect-fix.patch"
|
||||
fi
|
||||
|
||||
# Apply constants fix and do additional sed replacements
|
||||
pushd ${d_} > /dev/null
|
||||
if [ -f "$d/patching/121-ozone-constants-fix.patch" ]; then
|
||||
patch -p1 < "$d/patching/121-ozone-constants-fix.patch" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Replace process.env with env()
|
||||
sediment 's/process\.env\.\(NEXT_PUBLIC_[A-Z_]*\)/env('\''\1'\'')/g' lib/constants.ts 2>/dev/null || true
|
||||
sediment 's/process\.env\.NODE_ENV/env('\''NODE_ENV'\'')/g' lib/constants.ts 2>/dev/null || true
|
||||
# Add missing SOCIAL_APP_DOMAIN constant after SOCIAL_APP_URL
|
||||
sediment '/^export const SOCIAL_APP_URL =/,/^$/{ /^$/a\
|
||||
export const SOCIAL_APP_DOMAIN =\
|
||||
env('\''NEXT_PUBLIC_SOCIAL_APP_DOMAIN'\'') || '\''bsky.app'\''\
|
||||
|
||||
}' lib/constants.ts 2>/dev/null || true
|
||||
# Fix multiline process.env patterns
|
||||
sediment '/^export const NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/,/^ : 7$/ {
|
||||
s/^export const NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/export const NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = env('\''NEXT_PUBLIC_NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS'\'')/
|
||||
/^ \.NEXT_PUBLIC_NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS$/d
|
||||
}' lib/constants.ts 2>/dev/null || true
|
||||
sediment '/^export const YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/,/^ : 30$/ {
|
||||
s/^export const YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/export const YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = env('\''NEXT_PUBLIC_YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS'\'')/
|
||||
/^ \.NEXT_PUBLIC_YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS$/d
|
||||
}' lib/constants.ts 2>/dev/null || true
|
||||
sediment '/^export const HIGH_PROFILE_FOLLOWER_THRESHOLD = process\.env$/,/^ : Infinity$/ {
|
||||
s/^export const HIGH_PROFILE_FOLLOWER_THRESHOLD = process\.env$/export const HIGH_PROFILE_FOLLOWER_THRESHOLD = env('\''NEXT_PUBLIC_HIGH_PROFILE_FOLLOWER_THRESHOLD'\'')/
|
||||
/^ \.NEXT_PUBLIC_HIGH_PROFILE_FOLLOWER_THRESHOLD$/d
|
||||
}' lib/constants.ts 2>/dev/null || true
|
||||
# Fix parseInt() to handle undefined by adding || ''
|
||||
sediment "s/parseInt(env('\([^']*\)'))/parseInt(env('\1') || '0')/g" lib/constants.ts 2>/dev/null || true
|
||||
popd > /dev/null
|
||||
#cp -rf $d/repos/atproto/service/ozone/* $d/ozone/service/
|
||||
}
|
||||
|
||||
function at-repos-build-docker-atproto() {
|
||||
function at-repos-docker() {
|
||||
cd $d
|
||||
docker image prune -a
|
||||
if [ -z "$1" ];then
|
||||
for ((i=1; i<=${#services}; i++)); do
|
||||
service=${services[$i]}
|
||||
docker compose build --no-cache $service
|
||||
if [ "$service" = "ozone" ]; then
|
||||
docker compose build --no-cache ${service}-web
|
||||
fi
|
||||
done
|
||||
else
|
||||
docker compose build --no-cache $1
|
||||
fi
|
||||
docker compose build
|
||||
# docker compose up -d
|
||||
# docker compose up -d --no-build
|
||||
# docker compose up -d --pull always
|
||||
}
|
||||
|
||||
function at-repos-push-reset() {
|
||||
if [ -n "$(docker ps -q -f name=registry)" ]; then
|
||||
echo "Registry is already running."
|
||||
docker restart registry
|
||||
docker stop registry
|
||||
docker rm registry
|
||||
docker volume rm registry-data 2>/dev/null || true
|
||||
fi
|
||||
docker run -d -p ${dport}:${dport} --name registry \
|
||||
--restart=always \
|
||||
-v registry-data:/var/lib/registry \
|
||||
registry:2
|
||||
}
|
||||
function at-regi-docker() {
|
||||
docker run -d -p ${dport}:${dport} --name registry --restart=always registry:2
|
||||
docker tag at-pds:latest localhost:${dport}/pds:latest
|
||||
docker tag at-ozone-web:latest localhost:${dport}/ozone-web:latest
|
||||
docker tag at-bgs:latest localhost:${dport}/bgs:latest
|
||||
docker tag at-jetstream:latest localhost:${dport}/jetstream:latest
|
||||
docker tag at-bsky:latest localhost:${dport}/bsky:latest
|
||||
docker tag at-ozone-daemon:latest localhost:${dport}/ozone-daemon:latest
|
||||
docker tag at-ozone:latest localhost:${dport}/ozone:latest
|
||||
docker tag at-plc:latest localhost:${dport}/plc:latest
|
||||
docker tag at-social-app:latest localhost:${dport}/social-app:latest
|
||||
|
||||
function at-repos-push-docker() {
|
||||
if [ -z "$1" ] || [ "$1" = "push" ]; then
|
||||
for service in "${services[@]}"; do
|
||||
docker tag at-${service}:latest localhost:${dport}/${service}:latest
|
||||
docker push localhost:${dport}/${service}:latest
|
||||
if [ "$service" = "ozone" ]; then
|
||||
docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest
|
||||
docker push localhost:${dport}/${service}-web:latest
|
||||
fi
|
||||
done
|
||||
else
|
||||
docker tag at-${1}:latest localhost:${dport}/${1}:latest
|
||||
docker push localhost:${dport}/${1}:latest
|
||||
fi
|
||||
}
|
||||
docker push localhost:${dport}/pds:latest
|
||||
docker push localhost:${dport}/ozone-web:latest
|
||||
docker push localhost:${dport}/bgs:latest
|
||||
docker push localhost:${dport}/jetstream:latest
|
||||
docker push localhost:${dport}/bsky:latest
|
||||
docker push localhost:${dport}/ozone-daemon:latest
|
||||
docker push localhost:${dport}/ozone:latest
|
||||
docker push localhost:${dport}/plc:latest
|
||||
docker push localhost:${dport}/social-app:latest
|
||||
|
||||
function at-repos-pull-docker() {
|
||||
cd $d
|
||||
docker image prune -a
|
||||
docker compose up -d --pull always
|
||||
}
|
||||
|
||||
function at-repos-reset-bgs-db() {
|
||||
dp=at-database-1
|
||||
BGS_ADMIN_KEY=`cat $d/envs/bgs | grep BGS_ADMIN_KEY | cut -d '=' -f 2`
|
||||
|
||||
echo "🛑 Stopping BGS..."
|
||||
docker compose stop bgs
|
||||
|
||||
echo "🗑️ Cleaning data..."
|
||||
sudo rm -rf $d/data/bgs/*
|
||||
|
||||
echo "♻️ Resetting Databases..."
|
||||
docker exec -i $dp psql -U postgres -c "DROP DATABASE IF EXISTS bgs;"
|
||||
docker exec -i $dp psql -U postgres -c "CREATE DATABASE bgs;"
|
||||
docker exec -i $dp psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE bgs TO postgres;"
|
||||
|
||||
docker exec -i $dp psql -U postgres -c "DROP DATABASE IF EXISTS carstore;"
|
||||
docker exec -i $dp psql -U postgres -c "CREATE DATABASE carstore;"
|
||||
docker exec -i $dp psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE carstore TO postgres;"
|
||||
|
||||
echo "🚀 Starting BGS to initialize tables..."
|
||||
docker compose up -d bgs
|
||||
|
||||
echo "⏳ Waiting 10s for BGS migration..."
|
||||
sleep 10
|
||||
|
||||
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;"
|
||||
|
||||
# host=pds:3000
|
||||
echo "🔗 Registering Trusted Domain..."
|
||||
# Retry loop for addTrustedDomain as BGS might still be warming up
|
||||
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
|
||||
echo ""
|
||||
echo "✅ Trusted domain registered"
|
||||
break
|
||||
fi
|
||||
echo "Failed to contact BGS (attempt $i/5)... waiting 5s"
|
||||
sleep 5
|
||||
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
|
||||
handle=${handles[$i]}
|
||||
did=$(curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" | jq -r .did)
|
||||
if [ -n "$did" ] && [ "$did" != "null" ]; then
|
||||
echo " Syncing repo: $handle ($did)"
|
||||
# Use takedown=false to trigger a resync without actually taking down
|
||||
curl -s -X POST "https://bgs.${host}/admin/repo/takedown?did=${did}&takedown=false" \
|
||||
-H "Authorization: Bearer ${BGS_ADMIN_KEY}" || true
|
||||
else
|
||||
echo " Skipping $handle (DID not found)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ BGS reset complete!"
|
||||
echo " PDS should now be subscribed and syncing repos."
|
||||
}
|
||||
|
||||
function at-repos-feed-generator-start-push() {
|
||||
cd $d/repos/feed-generator
|
||||
yarn install
|
||||
FEEDGEN_HANDLE=${handle}
|
||||
FEEDGEN_PASSWORD=${APP_PASSWORD}
|
||||
FEEDGEN_RECORD_NAME=app
|
||||
FEEDGEN_AVATAR=$d/repos/atproto/packages/dev-env/assets/at.png
|
||||
npx tsx scripts/publish.ts
|
||||
}
|
||||
|
||||
function at-repos-feed-generator-update() {
|
||||
|
||||
resp=$(curl -sL -X POST -H "Content-Type: application/json" -d "{\"identifier\":\"$handle\",\"password\":\"${APP_PASSWORD}\"}" https://${host}/xrpc/com.atproto.server.createSession)
|
||||
token=$(echo $resp | jq -r .accessJwt)
|
||||
if [ -z "$token" ] || [ "$token" == "null" ]; then
|
||||
echo "Login failed: $resp"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
avatar_json="{\"\$type\":\"blob\",\"ref\":{\"\$link\":\"${img_id}\"},\"mimeType\":\"image/jpeg\",\"size\":375259}"
|
||||
|
||||
# 3. Delete cmd record
|
||||
#curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" \
|
||||
# -d "{\"repo\":\"$handle\",\"collection\":\"app.bsky.feed.generator\",\"rkey\":\"cmd\"}" \
|
||||
# https://${host}/xrpc/com.atproto.repo.deleteRecord
|
||||
|
||||
# 4. Put app record
|
||||
echo "Creating app record..."
|
||||
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Create JSON payload
|
||||
# Note: feeding avatar_json directly into jq
|
||||
payload=$(jq -n \
|
||||
--arg repo "$handle" \
|
||||
--arg collection "app.bsky.feed.generator" \
|
||||
--arg rkey "app" \
|
||||
--arg did "did:web:feed.${host}" \
|
||||
--arg type "app.bsky.feed.generator" \
|
||||
--arg created "$now" \
|
||||
--arg display "App Feed" \
|
||||
--arg desc "Automated App Feed" \
|
||||
--argjson avatar "$avatar_json" \
|
||||
'{
|
||||
repo: $repo,
|
||||
collection: $collection,
|
||||
rkey: $rkey,
|
||||
record: {
|
||||
did: $did,
|
||||
"$type": $type,
|
||||
createdAt: $created,
|
||||
displayName: $display,
|
||||
description: $desc,
|
||||
avatar: $avatar
|
||||
}
|
||||
}')
|
||||
curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" \
|
||||
-d @- \
|
||||
https://${host}/xrpc/com.atproto.repo.putRecord
|
||||
docker restart registry
|
||||
}
|
||||
|
||||
at-repos-env
|
||||
case "$1" in
|
||||
pull)
|
||||
at-repos-clone
|
||||
at-repos-pull
|
||||
exit
|
||||
;;
|
||||
patch)
|
||||
at-repos-social-app-ios-patch
|
||||
at-repos-patch-apply-all
|
||||
at-repos-ozone-patch
|
||||
show-failed-patches
|
||||
exit
|
||||
;;
|
||||
build)
|
||||
at-repos-build-docker-atproto $2
|
||||
exit
|
||||
;;
|
||||
push)
|
||||
at-repos-push-docker $2
|
||||
exit
|
||||
;;
|
||||
reset)
|
||||
at-repos-push-reset
|
||||
exit
|
||||
;;
|
||||
down)
|
||||
cd $d;docker compose down
|
||||
exit
|
||||
;;
|
||||
feed-push)
|
||||
at-repos-feed-generator-start-push
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
at-repos-clone
|
||||
at-repos-pull
|
||||
at-repos-social-app-icon
|
||||
at-repos-social-app-icon-origin
|
||||
at-repos-social-app-write
|
||||
at-repos-bsky-patch
|
||||
at-repos-social-app-patch
|
||||
at-repos-ozone-patch
|
||||
at-repos-docker
|
||||
|
||||
case "`cat /etc/hostname`" in
|
||||
at)
|
||||
if [ "$1" = "bgs-reset" ];then
|
||||
at-repos-reset-bgs-db
|
||||
exit
|
||||
fi
|
||||
at-repos-pull-docker
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
at-repos-clone
|
||||
at-repos-pull
|
||||
at-repos-checkout-pinned
|
||||
at-repos-social-app-ios-patch
|
||||
at-repos-patch-apply-all
|
||||
at-repos-ozone-patch
|
||||
show-failed-patches
|
||||
at-repos-build-docker-atproto
|
||||
at-repos-push-docker
|
||||
cd $d; docker compose down
|
||||
;;
|
||||
esac
|
||||
# at-regi-docker
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
APP_NAME="Aiat"
|
||||
REPO_DIR="../repos/social-app"
|
||||
APP_SLUG="aiat"
|
||||
APP_SCHEME="syui"
|
||||
APP_GROUP="group.ai.syui.at"
|
||||
APP_MAIL=user@example.com
|
||||
APP_KEYCHAIN=@keychain:KEYCHAIN_NAME
|
||||
BUNDLE_ID="ai.syui.at"
|
||||
SERVICE_URL="https://syu.is"
|
||||
HELP_URL="https://syu.is/about/support/help"
|
||||
PRIVACY_URL="https://syu.is/about/support/privacy-policy"
|
||||
TERMS_URL="https://syu.is/about/support/tos"
|
||||
REPO_DIR="../repos/social-app"
|
||||
CONFIG_FILE="$REPO_DIR/app.config.js"
|
||||
CONSTANTS_FILE="$REPO_DIR/src/lib/constants.ts"
|
||||
IOS_CERTIFICATE_NAME="Apple Distribution: $TEAM($TEAM_ID)"
|
||||
PDS_HOST=syu.is
|
||||
@@ -1,98 +0,0 @@
|
||||
今回の./ios (social-app)開発の要点をまとめます。
|
||||
|
||||
1. MITのライセンスを遵守すること、iosアプリとして出品しても問題ないようにすること
|
||||
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
|
||||
```
|
||||
|
||||
## App Storeへのアップロード
|
||||
|
||||
```sh
|
||||
# 1. 事前にXcodeでサイニング設定を完了させる
|
||||
# 2. store.mobileprovision を repos/social-app/ に配置
|
||||
# 3. キーチェーンに AC_PASSWORD を登録
|
||||
|
||||
$ cd /Users/syui/ai/at/ios
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
**必要な準備:**
|
||||
- Apple Distribution証明書: `Apple Distribution: syutaro inagaki (WN6KD5ZT49)`
|
||||
- App Store用Provisioning Profile: `store.mobileprovision`
|
||||
- App-Specific Password: キーチェーンに`APP_KEYCHAIN_PASSWORD`として登録
|
||||
|
||||
```sh
|
||||
# App-Specific Passwordの登録
|
||||
security add-generic-password -a "syui@syui.ai" -w "your-app-specific-password" -s "AC_PASSWORD"
|
||||
```
|
||||
|
||||
## 実装済み
|
||||
|
||||
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. 年齢保証、年齢確認ページがでてくるのを削除。誕生日を入力する処理を削除。アプリ配布国は限定します。
|
||||
|
||||
## 追加
|
||||
|
||||
1. 生年月日をサインイン時に要求しないよう削除
|
||||
2. 検索で、Discoverを表示しない
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"fill" : {
|
||||
"automatic-gradient" : "srgb:0.00000,0.41569,1.00000,1.00000"
|
||||
},
|
||||
"groups" : [
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"fill" : "none",
|
||||
"glass" : false,
|
||||
"image-name" : "iOS transparent.png",
|
||||
"name" : "iOS transparent"
|
||||
}
|
||||
],
|
||||
"shadow" : {
|
||||
"kind" : "neutral",
|
||||
"opacity" : 0.5
|
||||
},
|
||||
"translucency" : {
|
||||
"enabled" : true,
|
||||
"value" : 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"supported-platforms" : {
|
||||
"circles" : [
|
||||
"watchOS"
|
||||
],
|
||||
"squares" : "shared"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 574 B |
|
Before Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 28 KiB |
202
ios/build.zsh
@@ -1,202 +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..."
|
||||
# Ensure PATH includes Homebrew ruby gems if needed
|
||||
export PATH="/opt/homebrew/lib/ruby/gems/3.4.0/bin:$PATH"
|
||||
cd ios
|
||||
pod install
|
||||
cd ..
|
||||
|
||||
# 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)
|
||||
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
|
||||
echo "Error: store.mobileprovision not found at $MOBILEPROVISION"
|
||||
exit 1
|
||||
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"
|
||||
BIN
ios/icon.png
|
Before Width: | Height: | Size: 28 KiB |
@@ -1,166 +0,0 @@
|
||||
diff --git a/app.config.js b/app.config.js
|
||||
index 246d8abd3..ed8f7b2b2 100644
|
||||
--- a/app.config.js
|
||||
+++ b/app.config.js
|
||||
@@ -18,10 +18,7 @@ module.exports = function (_config) {
|
||||
const IS_DEV = !IS_TESTFLIGHT || !IS_PRODUCTION
|
||||
|
||||
const ASSOCIATED_DOMAINS = [
|
||||
- 'applinks:bsky.app',
|
||||
- 'applinks:staging.bsky.app',
|
||||
- 'appclips:bsky.app',
|
||||
- 'appclips:go.bsky.app', // Allows App Clip to work when scanning QR codes
|
||||
+ 'applinks:syu.is',
|
||||
// When testing local services, enter an ngrok (et al) domain here. It must use a standard HTTP/HTTPS port.
|
||||
...(IS_DEV || IS_TESTFLIGHT ? [] : []),
|
||||
]
|
||||
@@ -33,27 +30,25 @@ module.exports = function (_config) {
|
||||
return {
|
||||
expo: {
|
||||
version: VERSION,
|
||||
- name: 'Bluesky',
|
||||
- slug: 'bluesky',
|
||||
- scheme: 'bluesky',
|
||||
+ name: 'Aiat',
|
||||
+ slug: 'aiat',
|
||||
+ scheme: 'syui',
|
||||
owner: 'blueskysocial',
|
||||
runtimeVersion: {
|
||||
policy: 'appVersion',
|
||||
},
|
||||
- icon: './assets/app-icons/ios_icon_default_next.png',
|
||||
+ icon: './assets/logo.png',
|
||||
userInterfaceStyle: 'automatic',
|
||||
primaryColor: '#1083fe',
|
||||
newArchEnabled: false,
|
||||
ios: {
|
||||
supportsTablet: false,
|
||||
- bundleIdentifier: 'xyz.blueskyweb.app',
|
||||
+ bundleIdentifier: 'ai.syui.at',
|
||||
+ buildNumber: '__BUILD_NUMBER__',
|
||||
config: {
|
||||
usesNonExemptEncryption: false,
|
||||
},
|
||||
- icon:
|
||||
- PLATFORM === 'web' // web build doesn't like .icon files
|
||||
- ? './assets/app-icons/ios_icon_default_next.png'
|
||||
- : './assets/app-icons/ios_icon_default.icon',
|
||||
+ icon: './assets/logo.png',
|
||||
infoPlist: {
|
||||
UIBackgroundModes: ['remote-notification'],
|
||||
NSCameraUsageDescription:
|
||||
@@ -113,7 +107,7 @@ module.exports = function (_config) {
|
||||
entitlements: {
|
||||
'com.apple.developer.kernel.increased-memory-limit': true,
|
||||
'com.apple.developer.kernel.extended-virtual-addressing': true,
|
||||
- 'com.apple.security.application-groups': 'group.app.bsky',
|
||||
+ 'com.apple.security.application-groups': 'group.ai.syui.at',
|
||||
},
|
||||
privacyManifests: {
|
||||
NSPrivacyCollectedDataTypes: [
|
||||
@@ -175,14 +169,14 @@ module.exports = function (_config) {
|
||||
barStyle: 'light-content',
|
||||
},
|
||||
android: {
|
||||
- icon: './assets/app-icons/android_icon_default_next.png',
|
||||
+ icon: './assets/logo.png',
|
||||
adaptiveIcon: {
|
||||
foregroundImage: './assets/icon-android-foreground.png',
|
||||
monochromeImage: './assets/icon-android-monochrome.png',
|
||||
backgroundColor: '#006AFF',
|
||||
},
|
||||
googleServicesFile: './google-services.json',
|
||||
- package: 'xyz.blueskyweb.app',
|
||||
+ package: 'ai.syui.at',
|
||||
intentFilters: [
|
||||
{
|
||||
action: 'VIEW',
|
||||
@@ -190,7 +184,7 @@ module.exports = function (_config) {
|
||||
data: [
|
||||
{
|
||||
scheme: 'https',
|
||||
- host: 'bsky.app',
|
||||
+ host: 'syu.is',
|
||||
},
|
||||
IS_DEV && {
|
||||
scheme: 'http',
|
||||
@@ -213,9 +207,9 @@ module.exports = function (_config) {
|
||||
: undefined,
|
||||
codeSigningMetadata: UPDATES_ENABLED
|
||||
? {
|
||||
- keyid: 'main',
|
||||
- alg: 'rsa-v1_5-sha256',
|
||||
- }
|
||||
+ keyid: 'main',
|
||||
+ alg: 'rsa-v1_5-sha256',
|
||||
+ }
|
||||
: undefined,
|
||||
checkAutomatically: 'NEVER',
|
||||
},
|
||||
@@ -225,7 +219,7 @@ module.exports = function (_config) {
|
||||
'expo-web-browser',
|
||||
[
|
||||
'react-native-edge-to-edge',
|
||||
- {android: {enforceNavigationBarContrast: false}},
|
||||
+ { android: { enforceNavigationBarContrast: false } },
|
||||
],
|
||||
USE_SENTRY && [
|
||||
'@sentry/react-native/expo',
|
||||
@@ -264,7 +258,6 @@ module.exports = function (_config) {
|
||||
networkInstrumentation: true,
|
||||
},
|
||||
],
|
||||
- './plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
|
||||
'./plugins/withGradleJVMHeapSizeIncrease.js',
|
||||
'./plugins/withAndroidManifestLargeHeapPlugin.js',
|
||||
'./plugins/withAndroidManifestFCMIconPlugin.js',
|
||||
@@ -272,8 +265,6 @@ module.exports = function (_config) {
|
||||
'./plugins/withAndroidStylesAccentColorPlugin.js',
|
||||
'./plugins/withAndroidDayNightThemePlugin.js',
|
||||
'./plugins/withAndroidNoJitpackPlugin.js',
|
||||
- './plugins/shareExtension/withShareExtensions.js',
|
||||
- './plugins/notificationsExtension/withNotificationsExtension.js',
|
||||
[
|
||||
'expo-font',
|
||||
{
|
||||
@@ -386,7 +377,7 @@ module.exports = function (_config) {
|
||||
},
|
||||
},
|
||||
],
|
||||
- ['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}],
|
||||
+ ['expo-screen-orientation', { initialOrientation: 'PORTRAIT_UP' }],
|
||||
['expo-location'],
|
||||
].filter(Boolean),
|
||||
extra: {
|
||||
@@ -394,30 +385,7 @@ module.exports = function (_config) {
|
||||
build: {
|
||||
experimental: {
|
||||
ios: {
|
||||
- appExtensions: [
|
||||
- {
|
||||
- targetName: 'Share-with-Bluesky',
|
||||
- bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
|
||||
- entitlements: {
|
||||
- 'com.apple.security.application-groups': [
|
||||
- 'group.app.bsky',
|
||||
- ],
|
||||
- },
|
||||
- },
|
||||
- {
|
||||
- targetName: 'BlueskyNSE',
|
||||
- bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE',
|
||||
- entitlements: {
|
||||
- 'com.apple.security.application-groups': [
|
||||
- 'group.app.bsky',
|
||||
- ],
|
||||
- },
|
||||
- },
|
||||
- {
|
||||
- targetName: 'BlueskyClip',
|
||||
- bundleIdentifier: 'xyz.blueskyweb.app.AppClip',
|
||||
- },
|
||||
- ],
|
||||
+ appExtensions: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1,217 +0,0 @@
|
||||
diff --git a/src/lib/api/feed/home.ts b/src/lib/api/feed/home.ts
|
||||
index 7a0d72d91..93554dc3e 100644
|
||||
--- a/src/lib/api/feed/home.ts
|
||||
+++ b/src/lib/api/feed/home.ts
|
||||
@@ -45,7 +45,7 @@ export class HomeFeedAPI implements FeedAPI {
|
||||
this.following = new FollowingFeedAPI({agent})
|
||||
this.discover = new CustomFeedAPI({
|
||||
agent,
|
||||
- feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
|
||||
+ feedParams: {feed: PROD_DEFAULT_FEED('app')},
|
||||
})
|
||||
this.userInterests = userInterests
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export class HomeFeedAPI implements FeedAPI {
|
||||
this.following = new FollowingFeedAPI({agent: this.agent})
|
||||
this.discover = new CustomFeedAPI({
|
||||
agent: this.agent,
|
||||
- feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
|
||||
+ feedParams: {feed: PROD_DEFAULT_FEED('app')},
|
||||
userInterests: this.userInterests,
|
||||
})
|
||||
this.usingDiscover = false
|
||||
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
|
||||
index 231447b4f..a44b3da05 100644
|
||||
--- a/src/lib/constants.ts
|
||||
+++ b/src/lib/constants.ts
|
||||
@@ -7,12 +7,12 @@ import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
|
||||
export const LOCAL_DEV_SERVICE =
|
||||
Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583'
|
||||
export const STAGING_SERVICE = 'https://staging.bsky.dev'
|
||||
-export const BSKY_SERVICE = 'https://bsky.social'
|
||||
-export const BSKY_SERVICE_DID = 'did:web:bsky.social'
|
||||
-export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'
|
||||
+export const BSKY_SERVICE = 'https://syu.is'
|
||||
+export const BSKY_SERVICE_DID = 'did:web:syu.is'
|
||||
+export const PUBLIC_BSKY_SERVICE = 'https://bsky.syu.is'
|
||||
export const DEFAULT_SERVICE = BSKY_SERVICE
|
||||
-const HELP_DESK_LANG = 'en-us'
|
||||
-export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
|
||||
+const HELP_DESK_LANG = 'ja-jp'
|
||||
+export const HELP_DESK_URL = 'https://syu.is/about/support/help'
|
||||
export const EMBED_SERVICE = 'https://embed.bsky.app'
|
||||
export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
|
||||
export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download'
|
||||
@@ -79,19 +79,17 @@ export function IS_PROD_SERVICE(url?: string) {
|
||||
}
|
||||
|
||||
export const PROD_DEFAULT_FEED = (rkey: string) =>
|
||||
- `at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/${rkey}`
|
||||
+ `at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/${rkey}`
|
||||
|
||||
export const STAGING_DEFAULT_FEED = (rkey: string) =>
|
||||
`at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/${rkey}`
|
||||
|
||||
export const PROD_FEEDS = [
|
||||
- `feedgen|${PROD_DEFAULT_FEED('whats-hot')}`,
|
||||
- `feedgen|${PROD_DEFAULT_FEED('thevids')}`,
|
||||
+ `feedgen|${PROD_DEFAULT_FEED('app')}`,
|
||||
]
|
||||
|
||||
export const STAGING_FEEDS = [
|
||||
- `feedgen|${STAGING_DEFAULT_FEED('whats-hot')}`,
|
||||
- `feedgen|${STAGING_DEFAULT_FEED('thevids')}`,
|
||||
+ `feedgen|${STAGING_DEFAULT_FEED('app')}`,
|
||||
]
|
||||
|
||||
export const POST_IMG_MAX = {
|
||||
@@ -129,7 +127,7 @@ export const LANG_DROPDOWN_HITSLOP = {top: 10, bottom: 10, left: 4, right: 4}
|
||||
export const BACK_HITSLOP = HITSLOP_30
|
||||
export const MAX_POST_LINES = 25
|
||||
|
||||
-export const BSKY_APP_ACCOUNT_DID = 'did:plc:z72i7hdynmk6r22z27h6tvur'
|
||||
+export const BSKY_APP_ACCOUNT_DID = 'did:plc:6qyecktefllvenje24fcxnie'
|
||||
|
||||
export const BSKY_FEED_OWNER_DIDS = [
|
||||
BSKY_APP_ACCOUNT_DID,
|
||||
@@ -138,9 +136,9 @@ export const BSKY_FEED_OWNER_DIDS = [
|
||||
]
|
||||
|
||||
export const DISCOVER_FEED_URI =
|
||||
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot'
|
||||
+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
|
||||
export const VIDEO_FEED_URI =
|
||||
'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids'
|
||||
export const STAGING_VIDEO_FEED_URI =
|
||||
'at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/thevids'
|
||||
export const VIDEO_FEED_URIS = [VIDEO_FEED_URI, STAGING_VIDEO_FEED_URI]
|
||||
@@ -209,8 +207,8 @@ export const urls = {
|
||||
},
|
||||
}
|
||||
|
||||
-export const PUBLIC_APPVIEW = 'https://api.bsky.app'
|
||||
-export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'
|
||||
+export const PUBLIC_APPVIEW = 'https://bsky.syu.is'
|
||||
+export const PUBLIC_APPVIEW_DID = 'did:web:bsky.syu.is'
|
||||
export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
|
||||
|
||||
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
|
||||
@@ -236,8 +234,8 @@ export const BLUESKY_MOD_SERVICE_HEADERS = {
|
||||
}
|
||||
|
||||
export const webLinks = {
|
||||
- tos: `https://bsky.social/about/support/tos`,
|
||||
- privacy: `https://bsky.social/about/support/privacy-policy`,
|
||||
+ tos: `https://syu.is/about/support/tos`,
|
||||
+ privacy: `https://syu.is/about/support/privacy-policy`,
|
||||
community: `https://bsky.social/about/support/community-guidelines`,
|
||||
communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`,
|
||||
}
|
||||
diff --git a/src/lib/demo.ts b/src/lib/demo.ts
|
||||
index 5ead62c9d..7c80dfe15 100644
|
||||
--- a/src/lib/demo.ts
|
||||
+++ b/src/lib/demo.ts
|
||||
@@ -1,7 +1,7 @@
|
||||
import {type AppBskyFeedGetFeed} from '@atproto/api'
|
||||
import {subDays, subMinutes} from 'date-fns'
|
||||
|
||||
-const DID = `did:plc:z72i7hdynmk6r22z27h6tvur`
|
||||
+const DID = `did:plc:6qyecktefllvenje24fcxnie`
|
||||
const NOW = new Date()
|
||||
const POST_1_DATE = subMinutes(NOW, 2).toISOString()
|
||||
const POST_2_DATE = subMinutes(NOW, 4).toISOString()
|
||||
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
|
||||
index 6088e2806..0f6787a4d 100644
|
||||
--- a/src/lib/strings/url-helpers.ts
|
||||
+++ b/src/lib/strings/url-helpers.ts
|
||||
@@ -53,7 +53,7 @@ export function toNiceDomain(url: string): string {
|
||||
try {
|
||||
const urlp = new URL(url)
|
||||
if (`https://${urlp.host}` === BSKY_SERVICE) {
|
||||
- return 'Bluesky Social'
|
||||
+ return 'syu.is'
|
||||
}
|
||||
return urlp.host ? urlp.host : url
|
||||
} catch (e) {
|
||||
@@ -338,7 +338,7 @@ export function createProxiedUrl(url: string): string {
|
||||
return url
|
||||
}
|
||||
|
||||
- return `https://go.bsky.app/redirect?u=${encodeURIComponent(url)}`
|
||||
+ return url
|
||||
}
|
||||
|
||||
export function isShortLink(url: string): boolean {
|
||||
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
|
||||
index de1e92533..3d1566800 100644
|
||||
--- a/src/state/queries/feed.ts
|
||||
+++ b/src/state/queries/feed.ts
|
||||
@@ -201,14 +201,6 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
||||
// for the ones we know need it
|
||||
// -prf
|
||||
export const KNOWN_AUTHED_ONLY_FEEDS = [
|
||||
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app
|
||||
- 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed
|
||||
- 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed
|
||||
- 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow
|
||||
- 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz
|
||||
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky
|
||||
- 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz
|
||||
- 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why
|
||||
]
|
||||
|
||||
type GetPopularFeedsOptions = {limit?: number; enabled?: boolean}
|
||||
diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts
|
||||
index 0cf6ab546..399e592bc 100644
|
||||
--- a/src/state/queries/preferences/index.ts
|
||||
+++ b/src/state/queries/preferences/index.ts
|
||||
@@ -270,7 +270,7 @@ export function useReplaceForYouWithDiscoverFeedMutation() {
|
||||
await agent.addSavedFeeds([
|
||||
{
|
||||
type: 'feed',
|
||||
- value: PROD_DEFAULT_FEED('whats-hot'),
|
||||
+ value: PROD_DEFAULT_FEED('app'),
|
||||
pinned: true,
|
||||
},
|
||||
])
|
||||
diff --git a/src/view/com/posts/FeedShutdownMsg.tsx b/src/view/com/posts/FeedShutdownMsg.tsx
|
||||
index 620382175..928480da2 100644
|
||||
--- a/src/view/com/posts/FeedShutdownMsg.tsx
|
||||
+++ b/src/view/com/posts/FeedShutdownMsg.tsx
|
||||
@@ -32,7 +32,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||
f => f.value === feedUri && f.pinned,
|
||||
)
|
||||
const discoverFeedConfig = preferences?.savedFeeds?.find(
|
||||
- f => f.value === PROD_DEFAULT_FEED('whats-hot'),
|
||||
+ f => f.value === PROD_DEFAULT_FEED('app'),
|
||||
)
|
||||
const hasFeedPinned = Boolean(feedConfig)
|
||||
const hasDiscoverPinned = Boolean(discoverFeedConfig?.pinned)
|
||||
@@ -44,7 +44,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||
Toast.show(_(msg`Removed from your feeds`))
|
||||
}
|
||||
if (hasDiscoverPinned) {
|
||||
- setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
|
||||
+ setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('app')}`)
|
||||
}
|
||||
} catch (err: any) {
|
||||
Toast.show(
|
||||
@@ -63,7 +63,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||
forYouFeedConfig: feedConfig,
|
||||
discoverFeedConfig,
|
||||
})
|
||||
- setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
|
||||
+ setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('app')}`)
|
||||
Toast.show(_(msg`The feed has been replaced with Discover.`))
|
||||
} catch (err: any) {
|
||||
Toast.show(
|
||||
@@ -100,7 +100,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||
This feed is no longer online. We are showing{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`The Discover feed`)}
|
||||
- to="/profile/bsky.app/feed/whats-hot"
|
||||
+ to="/profile/did:plc:6qyecktefllvenje24fcxnie/feed/app"
|
||||
style={[a.text_md]}>
|
||||
Discover
|
||||
</InlineLinkText>{' '}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
diff --git a/src/Splash.tsx b/src/Splash.tsx
|
||||
index 47e70b375..616f351ed 100644
|
||||
--- a/src/Splash.tsx
|
||||
+++ b/src/Splash.tsx
|
||||
@@ -15,8 +15,8 @@ import Animated, {
|
||||
withTiming,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
-import Svg, {Path, type SvgProps} from 'react-native-svg'
|
||||
import {Image} from 'expo-image'
|
||||
+import {type SvgProps} from 'react-native-svg'
|
||||
import * as SplashScreen from 'expo-splash-screen'
|
||||
|
||||
import {Logotype} from '#/view/icons/Logotype'
|
||||
@@ -29,21 +29,18 @@ const darkSplashImageUri = RNImage.resolveAssetSource(
|
||||
darkSplashImagePointer,
|
||||
).uri
|
||||
|
||||
-export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) {
|
||||
- const width = 1000
|
||||
- const height = width * (67 / 64)
|
||||
+export const Logo = React.forwardRef(function LogoImpl(props: SvgProps & {fill?: string}, ref) {
|
||||
+ const size = 1000
|
||||
+ // @ts-ignore
|
||||
return (
|
||||
- <Svg
|
||||
- fill="none"
|
||||
- // @ts-ignore it's fiiiiine
|
||||
+ <Image
|
||||
+ // @ts-ignore
|
||||
ref={ref}
|
||||
- viewBox="0 0 64 66"
|
||||
- style={[{width, height}, props.style]}>
|
||||
- <Path
|
||||
- fill={props.fill || '#fff'}
|
||||
- d="M13.873 3.77C21.21 9.243 29.103 20.342 32 26.3v15.732c0-.335-.13.043-.41.858-1.512 4.414-7.418 21.642-20.923 7.87-7.111-7.252-3.819-14.503 9.125-16.692-7.405 1.252-15.73-.817-18.014-8.93C1.12 22.804 0 8.431 0 6.488 0-3.237 8.579-.18 13.873 3.77ZM50.127 3.77C42.79 9.243 34.897 20.342 32 26.3v15.732c0-.335.13.043.41.858 1.512 4.414 7.418 21.642 20.923 7.87 7.111-7.252 3.819-14.503-9.125-16.692 7.405 1.252 15.73-.817 18.014-8.93C62.88 22.804 64 8.431 64 6.488 64-3.237 55.422-.18 50.127 3.77Z"
|
||||
- />
|
||||
- </Svg>
|
||||
+ source={require('../assets/logo.png')}
|
||||
+ style={[{width: size, height: size}, props.style]}
|
||||
+ contentFit="contain"
|
||||
+ accessibilityLabel="Logo"
|
||||
+ />
|
||||
)
|
||||
})
|
||||
|
||||
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
|
||||
index 8a9e51a33..65d643b89 100644
|
||||
--- a/src/view/com/util/UserAvatar.tsx
|
||||
+++ b/src/view/com/util/UserAvatar.tsx
|
||||
@@ -444,7 +444,7 @@ let EditableUserAvatar = ({
|
||||
<HighPriorityImage
|
||||
testID="userAvatarImage"
|
||||
style={aviStyle}
|
||||
- source={{uri: avatar}}
|
||||
+ source={{ uri: hackModifyThumbnailPath(avatar, 1 > 0), }}
|
||||
accessibilityRole="image"
|
||||
/>
|
||||
) : (
|
||||
@@ -618,9 +618,8 @@ export {PreviewableUserAvatar}
|
||||
// manually string-replace to use the smaller ones
|
||||
// -prf
|
||||
function hackModifyThumbnailPath(uri: string, isEnabled: boolean): string {
|
||||
- return isEnabled
|
||||
- ? uri.replace('/img/avatar/plain/', '/img/avatar_thumbnail/plain/')
|
||||
- : uri
|
||||
+ // syu.is: avatars are served directly from bsky.syu.is, no CDN transformation needed
|
||||
+ return uri
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx
|
||||
index d7208df13..2763800ac 100644
|
||||
--- a/src/view/icons/Logo.tsx
|
||||
+++ b/src/view/icons/Logo.tsx
|
||||
@@ -1,75 +1,17 @@
|
||||
import React from 'react'
|
||||
-import {type TextProps} from 'react-native'
|
||||
-import Svg, {
|
||||
- Defs,
|
||||
- LinearGradient,
|
||||
- Path,
|
||||
- type PathProps,
|
||||
- Stop,
|
||||
- type SvgProps,
|
||||
-} from 'react-native-svg'
|
||||
import {Image} from 'expo-image'
|
||||
+import {flatten} from '#/alf'
|
||||
|
||||
-import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||
-import {flatten, useTheme} from '#/alf'
|
||||
-
|
||||
-const ratio = 57 / 64
|
||||
-
|
||||
-type Props = {
|
||||
- fill?: PathProps['fill']
|
||||
- style?: TextProps['style']
|
||||
-} & Omit<SvgProps, 'style'>
|
||||
-
|
||||
-export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
|
||||
- const t = useTheme()
|
||||
- const {fill, ...rest} = props
|
||||
- const gradient = fill === 'sky'
|
||||
- const styles = flatten(props.style)
|
||||
- const _fill = gradient
|
||||
- ? 'url(#sky)'
|
||||
- : fill || styles?.color || t.palette.primary_500
|
||||
- // @ts-ignore it's fiiiiine
|
||||
- const size = parseInt(rest.width || 32, 10)
|
||||
-
|
||||
- const isKawaii = useKawaiiMode()
|
||||
-
|
||||
- if (isKawaii) {
|
||||
- return (
|
||||
- <Image
|
||||
- source={
|
||||
- size > 100
|
||||
- ? require('../../../assets/kawaii.png')
|
||||
- : require('../../../assets/kawaii_smol.png')
|
||||
- }
|
||||
- accessibilityLabel="Bluesky"
|
||||
- accessibilityHint=""
|
||||
- accessibilityIgnoresInvertColors
|
||||
- style={[{height: size, aspectRatio: 1.4}]}
|
||||
- />
|
||||
- )
|
||||
- }
|
||||
-
|
||||
+export const Logo = React.forwardRef(function LogoImpl(props: any, ref) {
|
||||
+ const {width, style} = props
|
||||
+ // @ts-ignore
|
||||
+ const size = parseInt(width || 32, 10)
|
||||
return (
|
||||
- <Svg
|
||||
- fill="none"
|
||||
- // @ts-ignore it's fiiiiine
|
||||
- ref={ref}
|
||||
- viewBox="0 0 64 57"
|
||||
- {...rest}
|
||||
- style={[{width: size, height: size * ratio}, styles]}>
|
||||
- {gradient && (
|
||||
- <Defs>
|
||||
- <LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
||||
- <Stop offset="0" stopColor="#0A7AFF" stopOpacity="1" />
|
||||
- <Stop offset="1" stopColor="#59B9FF" stopOpacity="1" />
|
||||
- </LinearGradient>
|
||||
- </Defs>
|
||||
- )}
|
||||
-
|
||||
- <Path
|
||||
- fill={_fill}
|
||||
- d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z"
|
||||
- />
|
||||
- </Svg>
|
||||
+ <Image
|
||||
+ source={require('../../../assets/logo.png')}
|
||||
+ style={[{width: size, height: size}, flatten(style)]}
|
||||
+ contentFit="contain"
|
||||
+ accessibilityLabel="Logo"
|
||||
+ />
|
||||
)
|
||||
})
|
||||
diff --git a/src/view/icons/Logotype.tsx b/src/view/icons/Logotype.tsx
|
||||
index 270c913fc..a60ffe07c 100644
|
||||
--- a/src/view/icons/Logotype.tsx
|
||||
+++ b/src/view/icons/Logotype.tsx
|
||||
@@ -1,28 +1,22 @@
|
||||
-import Svg, {Path, type PathProps, type SvgProps} from 'react-native-svg'
|
||||
-
|
||||
-import {usePalette} from '#/lib/hooks/usePalette'
|
||||
-
|
||||
-const ratio = 17 / 64
|
||||
-
|
||||
-export function Logotype({
|
||||
- fill,
|
||||
- ...rest
|
||||
-}: {fill?: PathProps['fill']} & SvgProps) {
|
||||
- const pal = usePalette('default')
|
||||
- // @ts-ignore it's fiiiiine
|
||||
- const size = parseInt(rest.width || 32)
|
||||
+import React from 'react'
|
||||
+import {Text} from 'react-native'
|
||||
+import {useTheme, atoms as a} from '#/alf'
|
||||
|
||||
+export function Logotype({width, fill, style}: any) {
|
||||
+ const t = useTheme()
|
||||
+ const fontSize = width ? parseInt(width) / 3.5 : 22
|
||||
+
|
||||
return (
|
||||
- <Svg
|
||||
- fill="none"
|
||||
- viewBox="0 0 64 17"
|
||||
- {...rest}
|
||||
- width={size}
|
||||
- height={Number(size) * ratio}>
|
||||
- <Path
|
||||
- fill={fill || pal.text.color}
|
||||
- d="M8.478 6.252c1.503.538 2.3 1.78 2.3 3.172 0 2.356-1.576 3.785-4.6 3.785H0V0h5.974c2.875 0 4.267 1.466 4.267 3.413 0 1.3-.594 2.245-1.763 2.839Zm-2.69-4.193H2.504v3.45h3.284c1.28 0 1.967-.667 1.967-1.78 0-1.02-.705-1.67-1.967-1.67Zm-3.284 9.072h3.544c1.41 0 2.17-.65 2.17-1.818 0-1.224-.723-1.837-2.17-1.837H2.504v3.655ZM14.251 13.209h-2.337V0h2.337v13.209ZM22.001 8.998V3.636h2.338v9.573h-2.263v-1.392c-.724 1.076-1.726 1.614-3.006 1.614-2.022 0-3.34-1.224-3.34-3.45V3.636h2.338v5.955c0 1.206.594 1.818 1.8 1.818 1.132 0 2.133-.835 2.133-2.411ZM34.979 8.59v.556h-7.161c.167 1.651 1.076 2.467 2.486 2.467 1.076 0 1.8-.463 2.189-1.372h2.244c-.5 1.947-2.17 3.19-4.452 3.19-1.428 0-2.579-.463-3.45-1.372-.872-.91-1.318-2.115-1.318-3.637 0-1.502.427-2.708 1.299-3.636.872-.909 2.004-1.372 3.432-1.372 1.447 0 2.597.482 3.45 1.428.854.946 1.28 2.208 1.28 3.747Zm-4.75-3.358c-1.28 0-2.17.742-2.393 2.281h4.805c-.204-1.391-1.057-2.281-2.411-2.281ZM40.16 13.469c-2.783 0-4.249-1.095-4.379-3.303h2.282c.13 1.188.724 1.633 2.134 1.633 1.261 0 1.892-.39 1.892-1.15 0-.687-.445-1.02-1.874-1.262l-1.094-.185c-2.097-.353-3.136-1.318-3.136-2.894 0-1.8 1.429-2.894 3.97-2.894 2.728 0 4.138 1.075 4.23 3.246h-2.207c-.056-1.169-.742-1.577-2.023-1.577-1.113 0-1.67.371-1.67 1.113 0 .668.483.965 1.596 1.169l1.206.186c2.32.426 3.32 1.28 3.32 2.912 0 1.93-1.557 3.006-4.247 3.006ZM54.667 13.209h-2.671l-2.783-4.453-1.447 1.447v3.006h-2.3V0h2.3v7.606l3.896-3.97h2.783l-3.618 3.618 3.84 5.955ZM60.772 6.048l.78-2.412H64l-3.692 10.352c-.39 1.057-.872 1.818-1.484 2.245-.612.426-1.484.63-2.634.63-.39 0-.724-.018-1.02-.055V14.97h.89c1.057 0 1.577-.65 1.577-1.54 0-.445-.149-1.094-.446-1.929l-2.746-7.866h2.487l.779 2.393c.575 1.8 1.076 3.58 1.521 5.343.408-1.521.928-3.302 1.54-5.324Z"
|
||||
- />
|
||||
- </Svg>
|
||||
+ <Text style={[
|
||||
+ a.font_bold,
|
||||
+ {
|
||||
+ fontSize,
|
||||
+ color: fill || t.palette.primary_500,
|
||||
+ letterSpacing: -0.5
|
||||
+ },
|
||||
+ style
|
||||
+ ]}>
|
||||
+ Aiat
|
||||
+ </Text>
|
||||
)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
diff --git a/src/App.native.tsx b/src/App.native.tsx
|
||||
index fb3008627..539ebc055 100644
|
||||
--- a/src/App.native.tsx
|
||||
+++ b/src/App.native.tsx
|
||||
@@ -92,7 +92,7 @@ if (isAndroid) {
|
||||
* Begin geolocation ASAP
|
||||
*/
|
||||
Geo.resolve()
|
||||
-prefetchAgeAssuranceConfig()
|
||||
+// // // prefetchAgeAssuranceConfig()
|
||||
|
||||
function InnerApp() {
|
||||
const [isReady, setIsReady] = React.useState(false)
|
||||
diff --git a/src/routes.ts b/src/routes.ts
|
||||
index 1ed913bb2..c80340edb 100644
|
||||
--- a/src/routes.ts
|
||||
+++ b/src/routes.ts
|
||||
@@ -71,8 +71,8 @@ export const router = new Router<AllNavigatableRoutes>({
|
||||
MiscellaneousNotificationSettings: '/settings/notifications/miscellaneous',
|
||||
// support
|
||||
Support: '/support',
|
||||
- PrivacyPolicy: '/support/privacy',
|
||||
- TermsOfService: '/support/tos',
|
||||
+ PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
|
||||
+ TermsOfService: 'https://syu.is/about/support/tos',
|
||||
CommunityGuidelines: '/support/community-guidelines',
|
||||
CopyrightPolicy: '/support/copyright',
|
||||
// hashtags
|
||||
diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts
|
||||
index 5c8ce3b97..ee85beb08 100644
|
||||
--- a/src/state/session/agent.ts
|
||||
+++ b/src/state/session/agent.ts
|
||||
@@ -47,7 +47,8 @@ export function createPublicAgent() {
|
||||
configureModerationForGuest() // Side effect but only relevant for tests
|
||||
|
||||
const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE})
|
||||
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
+ // Disable proxy for self-hosted environments
|
||||
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
return agent
|
||||
}
|
||||
|
||||
@@ -88,7 +89,8 @@ export async function createAgentAndResume(
|
||||
// after session is attached
|
||||
const aa = prefetchAgeAssuranceData({agent})
|
||||
|
||||
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
+ // Disable proxy for self-hosted environments
|
||||
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
|
||||
return agent.prepare({
|
||||
resolvers: [gates, moderation, aa],
|
||||
@@ -127,7 +129,8 @@ export async function createAgentAndLogin(
|
||||
const moderation = configureModerationForAccount(agent, account)
|
||||
const aa = prefetchAgeAssuranceData({agent})
|
||||
|
||||
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
+ // Disable proxy for self-hosted environments
|
||||
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
|
||||
return agent.prepare({
|
||||
resolvers: [gates, moderation, aa],
|
||||
@@ -299,7 +302,8 @@ export async function createAgentAndCreateAccount(
|
||||
logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`})
|
||||
}
|
||||
|
||||
- agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
+ // Disable proxy for self-hosted environments
|
||||
+ // agent.configureProxy(BLUESKY_PROXY_HEADER.get())
|
||||
|
||||
return agent.prepare({
|
||||
resolvers: [gates, moderation, aa],
|
||||
@@ -1,593 +0,0 @@
|
||||
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
|
||||
index 6b8257b91..48ba7909e 100644
|
||||
--- a/src/screens/Settings/AboutSettings.tsx
|
||||
+++ b/src/screens/Settings/AboutSettings.tsx
|
||||
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.LinkItem
|
||||
- to="https://bsky.social/about/support/tos"
|
||||
+ to="https://syu.is/about/support/tos"
|
||||
label={_(msg`Terms of Service`)}>
|
||||
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
||||
<SettingsList.ItemText>
|
||||
@@ -88,7 +88,7 @@ export function AboutSettingsScreen({}: Props) {
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
- to="https://bsky.social/about/support/privacy-policy"
|
||||
+ to="https://syu.is/about/support/privacy-policy"
|
||||
label={_(msg`Privacy Policy`)}>
|
||||
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
||||
<SettingsList.ItemText>
|
||||
diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx
|
||||
index 77f219e55..53f5e0cc0 100644
|
||||
--- a/src/screens/Takendown.tsx
|
||||
+++ b/src/screens/Takendown.tsx
|
||||
@@ -217,10 +217,10 @@ export function Takendown() {
|
||||
<Trans>
|
||||
Your account was found to be in violation of the{' '}
|
||||
<SimpleInlineLinkText
|
||||
- label={_(msg`Bluesky Social Terms of Service`)}
|
||||
- to="https://bsky.social/about/support/tos"
|
||||
+ label={_(msg`syu.is Terms of Service`)}
|
||||
+ to="https://syu.is/about/support/tos"
|
||||
style={[a.text_md, a.leading_normal]}>
|
||||
- Bluesky Social Terms of Service
|
||||
+ syu.is Terms of Service
|
||||
</SimpleInlineLinkText>
|
||||
. You have been sent an email outlining the specific violation
|
||||
and suspension period, if applicable. You can appeal this
|
||||
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
|
||||
index e058e2883..8daf41089 100644
|
||||
--- a/src/view/screens/Home.tsx
|
||||
+++ b/src/view/screens/Home.tsx
|
||||
@@ -1,23 +1,16 @@
|
||||
import React from 'react'
|
||||
import {ActivityIndicator, StyleSheet} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
-
|
||||
import {PROD_DEFAULT_FEED} from '#/lib/constants'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useOTAUpdates} from '#/lib/hooks/useOTAUpdates'
|
||||
import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
import {useRequestNotificationsPermission} from '#/lib/notifications/notifications'
|
||||
-import {
|
||||
- type HomeTabNavigatorParams,
|
||||
- type NativeStackScreenProps,
|
||||
-} from '#/lib/routes/types'
|
||||
+import {type HomeTabNavigatorParams, type NativeStackScreenProps} from '#/lib/routes/types'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {isWeb} from '#/platform/detection'
|
||||
import {emitSoftReset} from '#/state/events'
|
||||
-import {
|
||||
- type SavedFeedSourceInfo,
|
||||
- usePinnedFeedsInfos,
|
||||
-} from '#/state/queries/feed'
|
||||
+import {type SavedFeedSourceInfo, usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||
import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {type UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
|
||||
@@ -27,11 +20,7 @@ import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
|
||||
import {FeedPage} from '#/view/com/feeds/FeedPage'
|
||||
import {HomeHeader} from '#/view/com/home/HomeHeader'
|
||||
-import {
|
||||
- Pager,
|
||||
- type PagerRef,
|
||||
- type RenderTabBarFnProps,
|
||||
-} from '#/view/com/pager/Pager'
|
||||
+import {Pager, type PagerRef, type RenderTabBarFnProps} from '#/view/com/pager/Pager'
|
||||
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
|
||||
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
|
||||
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
|
||||
@@ -39,97 +28,90 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {useDemoMode} from '#/storage/hooks/demo-mode'
|
||||
|
||||
+const SYU_IS_FEED_URI = 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
|
||||
+
|
||||
+const DEFAULT_PINNED_FEEDS: any[] = [{
|
||||
+ feedDescriptor: 'following',
|
||||
+ displayName: 'Following',
|
||||
+ id: 'following',
|
||||
+ uri: 'following',
|
||||
+ type: 'feed',
|
||||
+ savedFeed: undefined,
|
||||
+ pinned: true,
|
||||
+ route: { href: '/', name: 'Home', params: {} },
|
||||
+ cid: '',
|
||||
+ avatar: '',
|
||||
+ creatorDid: '',
|
||||
+ creatorHandle: '',
|
||||
+}, {
|
||||
+ feedDescriptor: `feedgen|${SYU_IS_FEED_URI}`,
|
||||
+ displayName: 'Feeds',
|
||||
+ id: SYU_IS_FEED_URI,
|
||||
+ uri: SYU_IS_FEED_URI,
|
||||
+ type: 'feed',
|
||||
+ savedFeed: {
|
||||
+ type: 'feed',
|
||||
+ value: SYU_IS_FEED_URI,
|
||||
+ pinned: true,
|
||||
+ },
|
||||
+ pinned: true,
|
||||
+ route: { href: '/', name: 'Home', params: {} },
|
||||
+ cid: '',
|
||||
+ avatar: '',
|
||||
+ creatorDid: '',
|
||||
+ creatorHandle: '',
|
||||
+}]
|
||||
+
|
||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
|
||||
export function HomeScreen(props: Props) {
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {currentAccount} = useSession()
|
||||
- const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
|
||||
- usePinnedFeedsInfos()
|
||||
+ const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
|
||||
+
|
||||
+ const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any
|
||||
+ // Use user's pinned feeds when logged in and available, otherwise use defaults
|
||||
+ const safePinnedFeedInfos = !currentAccount
|
||||
+ ? DEFAULT_PINNED_FEEDS.filter(f => f.feedDescriptor !== 'following')
|
||||
+ : (pinnedFeedInfos && pinnedFeedInfos.length > 0)
|
||||
+ ? pinnedFeedInfos
|
||||
+ : DEFAULT_PINNED_FEEDS
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isWeb && !currentAccount) {
|
||||
const getParams = new URLSearchParams(window.location.search)
|
||||
const splash = getParams.get('splash')
|
||||
- if (splash === 'true') {
|
||||
- setShowLoggedOut(true)
|
||||
- return
|
||||
- }
|
||||
+ if (splash === 'true') { setShowLoggedOut(true); return }
|
||||
}
|
||||
-
|
||||
const params = props.route.params
|
||||
- if (
|
||||
- currentAccount &&
|
||||
- props.route.name === 'Start' &&
|
||||
- params?.name &&
|
||||
- params?.rkey
|
||||
- ) {
|
||||
- props.navigation.navigate('StarterPack', {
|
||||
- rkey: params.rkey,
|
||||
- name: params.name,
|
||||
- })
|
||||
+ if (currentAccount && props.route.name === 'Start' && params?.name && params?.rkey) {
|
||||
+ props.navigation.navigate('StarterPack', { rkey: params.rkey, name: params.name })
|
||||
}
|
||||
- }, [
|
||||
- currentAccount,
|
||||
- props.navigation,
|
||||
- props.route.name,
|
||||
- props.route.params,
|
||||
- setShowLoggedOut,
|
||||
- ])
|
||||
+ }, [currentAccount, props.navigation, props.route.name, props.route.params, setShowLoggedOut])
|
||||
|
||||
- if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) {
|
||||
- return (
|
||||
- <Layout.Screen testID="HomeScreen">
|
||||
- <HomeScreenReady
|
||||
- {...props}
|
||||
- preferences={preferences}
|
||||
- pinnedFeedInfos={pinnedFeedInfos}
|
||||
- />
|
||||
- </Layout.Screen>
|
||||
- )
|
||||
- } else {
|
||||
- return (
|
||||
- <Layout.Screen>
|
||||
- <Layout.Center style={styles.loading}>
|
||||
- <ActivityIndicator size="large" />
|
||||
- </Layout.Center>
|
||||
- </Layout.Screen>
|
||||
- )
|
||||
- }
|
||||
+ return (
|
||||
+ <Layout.Screen testID="HomeScreen">
|
||||
+ <HomeScreenReady {...props} preferences={safePreferences} pinnedFeedInfos={safePinnedFeedInfos as any} />
|
||||
+ </Layout.Screen>
|
||||
+ )
|
||||
}
|
||||
|
||||
-function HomeScreenReady({
|
||||
- preferences,
|
||||
- pinnedFeedInfos,
|
||||
-}: Props & {
|
||||
- preferences: UsePreferencesQueryResponse
|
||||
- pinnedFeedInfos: SavedFeedSourceInfo[]
|
||||
-}) {
|
||||
- const allFeeds = React.useMemo(
|
||||
- () => pinnedFeedInfos.map(f => f.feedDescriptor),
|
||||
- [pinnedFeedInfos],
|
||||
- )
|
||||
- const maybeRawSelectedFeed: FeedDescriptor | undefined =
|
||||
- useSelectedFeed() ?? allFeeds[0]
|
||||
+function HomeScreenReady({preferences, pinnedFeedInfos}: any) {
|
||||
+ const allFeeds = React.useMemo(() => pinnedFeedInfos.map(f => f.feedDescriptor), [pinnedFeedInfos])
|
||||
+ const maybeRawSelectedFeed = useSelectedFeed() ?? allFeeds[0]
|
||||
const setSelectedFeed = useSetSelectedFeed()
|
||||
const maybeFoundIndex = allFeeds.indexOf(maybeRawSelectedFeed)
|
||||
const selectedIndex = Math.max(0, maybeFoundIndex)
|
||||
- const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex]
|
||||
+ const maybeSelectedFeed = allFeeds[selectedIndex]
|
||||
const requestNotificationsPermission = useRequestNotificationsPermission()
|
||||
|
||||
useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName)
|
||||
useOTAUpdates()
|
||||
-
|
||||
- React.useEffect(() => {
|
||||
- requestNotificationsPermission('Home')
|
||||
- }, [requestNotificationsPermission])
|
||||
+ React.useEffect(() => { requestNotificationsPermission('Home') }, [requestNotificationsPermission])
|
||||
|
||||
const pagerRef = React.useRef<PagerRef>(null)
|
||||
const lastPagerReportedIndexRef = React.useRef(selectedIndex)
|
||||
React.useLayoutEffect(() => {
|
||||
- // Since the pager is not a controlled component, adjust it imperatively
|
||||
- // if the selected index gets out of sync with what it last reported.
|
||||
- // This is supposed to only happen on the web when you use the right nav.
|
||||
if (selectedIndex !== lastPagerReportedIndexRef.current) {
|
||||
lastPagerReportedIndexRef.current = selectedIndex
|
||||
pagerRef.current?.setPage(selectedIndex)
|
||||
@@ -138,205 +120,43 @@ function HomeScreenReady({
|
||||
|
||||
const {hasSession} = useSession()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
- useFocusEffect(
|
||||
- React.useCallback(() => {
|
||||
- setMinimalShellMode(false)
|
||||
- }, [setMinimalShellMode]),
|
||||
- )
|
||||
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode]))
|
||||
|
||||
- useFocusEffect(
|
||||
- useNonReactiveCallback(() => {
|
||||
- if (maybeSelectedFeed) {
|
||||
- logEvent('home:feedDisplayed', {
|
||||
- index: selectedIndex,
|
||||
- feedType: maybeSelectedFeed.split('|')[0],
|
||||
- feedUrl: maybeSelectedFeed,
|
||||
- reason: 'focus',
|
||||
- })
|
||||
- }
|
||||
- }),
|
||||
- )
|
||||
-
|
||||
- const onPageSelected = React.useCallback(
|
||||
- (index: number) => {
|
||||
- setMinimalShellMode(false)
|
||||
- const maybeFeed = allFeeds[index]
|
||||
+ const onPageSelected = React.useCallback((index) => {
|
||||
+ setMinimalShellMode(false)
|
||||
+ const maybeFeed = allFeeds[index]
|
||||
+ lastPagerReportedIndexRef.current = index
|
||||
+ setSelectedFeed(maybeFeed)
|
||||
+ }, [setSelectedFeed, setMinimalShellMode, allFeeds])
|
||||
|
||||
- // Mutate the ref before setting state to avoid the imperative syncing effect
|
||||
- // above from starting a loop on Android when swiping back and forth.
|
||||
- lastPagerReportedIndexRef.current = index
|
||||
- setSelectedFeed(maybeFeed)
|
||||
-
|
||||
- if (maybeFeed) {
|
||||
- logEvent('home:feedDisplayed', {
|
||||
- index,
|
||||
- feedType: maybeFeed.split('|')[0],
|
||||
- feedUrl: maybeFeed,
|
||||
- })
|
||||
- }
|
||||
- },
|
||||
- [setSelectedFeed, setMinimalShellMode, allFeeds],
|
||||
- )
|
||||
-
|
||||
- const onPressSelected = React.useCallback(() => {
|
||||
- emitSoftReset()
|
||||
- }, [])
|
||||
-
|
||||
- const onPageScrollStateChanged = React.useCallback(
|
||||
- (state: 'idle' | 'dragging' | 'settling') => {
|
||||
- 'worklet'
|
||||
- if (state === 'dragging') {
|
||||
- setMinimalShellMode(false)
|
||||
- }
|
||||
- },
|
||||
- [setMinimalShellMode],
|
||||
- )
|
||||
+ const onPressSelected = React.useCallback(() => { emitSoftReset() }, [])
|
||||
+ const onPageScrollStateChanged = React.useCallback((state) => {
|
||||
+ 'worklet'
|
||||
+ if (state === 'dragging') setMinimalShellMode(false)
|
||||
+ }, [setMinimalShellMode])
|
||||
|
||||
const [demoMode] = useDemoMode()
|
||||
+ const renderTabBar = React.useCallback((props) => {
|
||||
+ return <HomeHeader key="FEEDS_TAB_BAR" {...props} testID="homeScreenFeedTabs" onPressSelected={onPressSelected} feeds={pinnedFeedInfos} />
|
||||
+ }, [onPressSelected, pinnedFeedInfos])
|
||||
|
||||
- const renderTabBar = React.useCallback(
|
||||
- (props: RenderTabBarFnProps) => {
|
||||
- if (demoMode) {
|
||||
- return (
|
||||
- <HomeHeader
|
||||
- key="FEEDS_TAB_BAR"
|
||||
- {...props}
|
||||
- testID="homeScreenFeedTabs"
|
||||
- onPressSelected={onPressSelected}
|
||||
- // @ts-ignore
|
||||
- feeds={[{displayName: 'Following'}, {displayName: 'Discover'}]}
|
||||
- />
|
||||
- )
|
||||
- }
|
||||
- return (
|
||||
- <HomeHeader
|
||||
- key="FEEDS_TAB_BAR"
|
||||
- {...props}
|
||||
- testID="homeScreenFeedTabs"
|
||||
- onPressSelected={onPressSelected}
|
||||
- feeds={pinnedFeedInfos}
|
||||
- />
|
||||
- )
|
||||
- },
|
||||
- [onPressSelected, pinnedFeedInfos, demoMode],
|
||||
- )
|
||||
-
|
||||
- const renderFollowingEmptyState = React.useCallback(() => {
|
||||
- return <FollowingEmptyState />
|
||||
- }, [])
|
||||
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, [])
|
||||
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
|
||||
|
||||
- const renderCustomFeedEmptyState = React.useCallback(() => {
|
||||
- return <CustomFeedEmptyState />
|
||||
- }, [])
|
||||
+ const homeFeedParams = React.useMemo(() => ({
|
||||
+ mergeFeedEnabled: false, mergeFeedSources: []
|
||||
+ }), [preferences])
|
||||
|
||||
- const homeFeedParams = React.useMemo<FeedParams>(() => {
|
||||
- return {
|
||||
- mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
||||
- mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
|
||||
- ? preferences.savedFeeds
|
||||
- .filter(f => f.type === 'feed' || f.type === 'list')
|
||||
- .map(f => f.value)
|
||||
- : [],
|
||||
- }
|
||||
- }, [preferences])
|
||||
-
|
||||
- if (demoMode) {
|
||||
- return (
|
||||
- <Pager
|
||||
- ref={pagerRef}
|
||||
- testID="homeScreen"
|
||||
- onPageSelected={onPageSelected}
|
||||
- onPageScrollStateChanged={onPageScrollStateChanged}
|
||||
- renderTabBar={renderTabBar}
|
||||
- initialPage={selectedIndex}>
|
||||
- <FeedPage
|
||||
- testID="demoFeedPage"
|
||||
- isPageFocused
|
||||
- isPageAdjacent={false}
|
||||
- feed="demo"
|
||||
- renderEmptyState={renderCustomFeedEmptyState}
|
||||
- feedInfo={pinnedFeedInfos[0]}
|
||||
- />
|
||||
- <FeedPage
|
||||
- testID="customFeedPage"
|
||||
- isPageFocused
|
||||
- isPageAdjacent={false}
|
||||
- feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
|
||||
- renderEmptyState={renderCustomFeedEmptyState}
|
||||
- feedInfo={pinnedFeedInfos[0]}
|
||||
- />
|
||||
- </Pager>
|
||||
- )
|
||||
- }
|
||||
-
|
||||
- return hasSession ? (
|
||||
- <Pager
|
||||
- key={allFeeds.join(',')}
|
||||
- ref={pagerRef}
|
||||
- testID="homeScreen"
|
||||
- initialPage={selectedIndex}
|
||||
- onPageSelected={onPageSelected}
|
||||
- onPageScrollStateChanged={onPageScrollStateChanged}
|
||||
- renderTabBar={renderTabBar}>
|
||||
- {pinnedFeedInfos.length ? (
|
||||
- pinnedFeedInfos.map((feedInfo, index) => {
|
||||
+ return (
|
||||
+ <Pager ref={pagerRef} testID="homeScreen" initialPage={selectedIndex} onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}>
|
||||
+ {pinnedFeedInfos.map((feedInfo, index) => {
|
||||
const feed = feedInfo.feedDescriptor
|
||||
if (feed === 'following') {
|
||||
- return (
|
||||
- <FeedPage
|
||||
- key={feed}
|
||||
- testID="followingFeedPage"
|
||||
- isPageFocused={maybeSelectedFeed === feed}
|
||||
- isPageAdjacent={Math.abs(selectedIndex - index) === 1}
|
||||
- feed={feed}
|
||||
- feedParams={homeFeedParams}
|
||||
- renderEmptyState={renderFollowingEmptyState}
|
||||
- renderEndOfFeed={FollowingEndOfFeed}
|
||||
- feedInfo={feedInfo}
|
||||
- />
|
||||
- )
|
||||
+ return <FeedPage key={feed} testID="followingFeedPage" isPageFocused={maybeSelectedFeed === feed} isPageAdjacent={Math.abs(selectedIndex - index) === 1} feed={feed} feedParams={homeFeedParams} renderEmptyState={renderFollowingEmptyState} renderEndOfFeed={FollowingEndOfFeed} feedInfo={feedInfo} />
|
||||
}
|
||||
- const savedFeedConfig = feedInfo.savedFeed
|
||||
- return (
|
||||
- <FeedPage
|
||||
- key={feed}
|
||||
- testID="customFeedPage"
|
||||
- isPageFocused={maybeSelectedFeed === feed}
|
||||
- isPageAdjacent={Math.abs(selectedIndex - index) === 1}
|
||||
- feed={feed}
|
||||
- renderEmptyState={renderCustomFeedEmptyState}
|
||||
- savedFeedConfig={savedFeedConfig}
|
||||
- feedInfo={feedInfo}
|
||||
- />
|
||||
- )
|
||||
- })
|
||||
- ) : (
|
||||
- <NoFeedsPinned preferences={preferences} />
|
||||
- )}
|
||||
- </Pager>
|
||||
- ) : (
|
||||
- <Pager
|
||||
- testID="homeScreen"
|
||||
- onPageSelected={onPageSelected}
|
||||
- onPageScrollStateChanged={onPageScrollStateChanged}
|
||||
- renderTabBar={renderTabBar}>
|
||||
- <FeedPage
|
||||
- testID="customFeedPage"
|
||||
- isPageFocused
|
||||
- isPageAdjacent={false}
|
||||
- feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
|
||||
- renderEmptyState={renderCustomFeedEmptyState}
|
||||
- feedInfo={pinnedFeedInfos[0]}
|
||||
- />
|
||||
+ return <FeedPage key={feed} testID="customFeedPage" isPageFocused={maybeSelectedFeed === feed} isPageAdjacent={Math.abs(selectedIndex - index) === 1} feed={feed} renderEmptyState={renderCustomFeedEmptyState} savedFeedConfig={feedInfo.savedFeed} feedInfo={feedInfo} />
|
||||
+ })}
|
||||
</Pager>
|
||||
)
|
||||
}
|
||||
-
|
||||
-const styles = StyleSheet.create({
|
||||
- loading: {
|
||||
- height: '100%',
|
||||
- alignContent: 'center',
|
||||
- justifyContent: 'center',
|
||||
- paddingBottom: 100,
|
||||
- },
|
||||
-})
|
||||
+const styles = StyleSheet.create({ loading: { height: '100%', alignContent: 'center', justifyContent: 'center', paddingBottom: 100 } })
|
||||
diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
|
||||
index a89eaadc4..1da393f03 100644
|
||||
--- a/src/view/screens/PrivacyPolicy.tsx
|
||||
+++ b/src/view/screens/PrivacyPolicy.tsx
|
||||
@@ -1,52 +1,13 @@
|
||||
import React from 'react'
|
||||
-import {View} from 'react-native'
|
||||
-import {msg, Trans} from '@lingui/macro'
|
||||
-import {useLingui} from '@lingui/react'
|
||||
-import {useFocusEffect} from '@react-navigation/native'
|
||||
-
|
||||
-import {usePalette} from '#/lib/hooks/usePalette'
|
||||
-import {
|
||||
- type CommonNavigatorParams,
|
||||
- type NativeStackScreenProps,
|
||||
-} from '#/lib/routes/types'
|
||||
-import {s} from '#/lib/styles'
|
||||
-import {useSetMinimalShellMode} from '#/state/shell'
|
||||
-import {TextLink} from '#/view/com/util/Link'
|
||||
-import {Text} from '#/view/com/util/text/Text'
|
||||
-import {ScrollView} from '#/view/com/util/Views'
|
||||
+import { WebView } from 'react-native-webview'
|
||||
import * as Layout from '#/components/Layout'
|
||||
-import {ViewHeader} from '../com/util/ViewHeader'
|
||||
-
|
||||
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'>
|
||||
-export const PrivacyPolicyScreen = (_props: Props) => {
|
||||
- const pal = usePalette('default')
|
||||
- const {_} = useLingui()
|
||||
- const setMinimalShellMode = useSetMinimalShellMode()
|
||||
-
|
||||
- useFocusEffect(
|
||||
- React.useCallback(() => {
|
||||
- setMinimalShellMode(false)
|
||||
- }, [setMinimalShellMode]),
|
||||
- )
|
||||
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
|
||||
+export function PrivacyPolicyScreen() {
|
||||
+ useSetTitle('Privacy Policy')
|
||||
return (
|
||||
<Layout.Screen>
|
||||
- <ViewHeader title={_(msg`Privacy Policy`)} />
|
||||
- <ScrollView style={[s.hContentRegion, pal.view]}>
|
||||
- <View style={[s.p20]}>
|
||||
- <Text style={pal.text}>
|
||||
- <Trans>
|
||||
- The Privacy Policy has been moved to{' '}
|
||||
- <TextLink
|
||||
- style={pal.link}
|
||||
- href="https://bsky.social/about/support/privacy-policy"
|
||||
- text="bsky.social/about/support/privacy-policy"
|
||||
- />
|
||||
- </Trans>
|
||||
- </Text>
|
||||
- </View>
|
||||
- <View style={s.footerSpacer} />
|
||||
- </ScrollView>
|
||||
+ <WebView source={{ uri: 'https://syu.is/about/support/privacy-policy' }} style={{ flex: 1 }} />
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
|
||||
index d843c713c..b81767bd5 100644
|
||||
--- a/src/view/screens/TermsOfService.tsx
|
||||
+++ b/src/view/screens/TermsOfService.tsx
|
||||
@@ -1,50 +1,13 @@
|
||||
import React from 'react'
|
||||
-import {View} from 'react-native'
|
||||
-import {msg, Trans} from '@lingui/macro'
|
||||
-import {useLingui} from '@lingui/react'
|
||||
-import {useFocusEffect} from '@react-navigation/native'
|
||||
-
|
||||
-import {usePalette} from '#/lib/hooks/usePalette'
|
||||
-import {
|
||||
- type CommonNavigatorParams,
|
||||
- type NativeStackScreenProps,
|
||||
-} from '#/lib/routes/types'
|
||||
-import {s} from '#/lib/styles'
|
||||
-import {useSetMinimalShellMode} from '#/state/shell'
|
||||
-import {TextLink} from '#/view/com/util/Link'
|
||||
-import {Text} from '#/view/com/util/text/Text'
|
||||
-import {ScrollView} from '#/view/com/util/Views'
|
||||
+import { WebView } from 'react-native-webview'
|
||||
import * as Layout from '#/components/Layout'
|
||||
-import {ViewHeader} from '../com/util/ViewHeader'
|
||||
-
|
||||
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
|
||||
-export const TermsOfServiceScreen = (_props: Props) => {
|
||||
- const pal = usePalette('default')
|
||||
- const setMinimalShellMode = useSetMinimalShellMode()
|
||||
- const {_} = useLingui()
|
||||
-
|
||||
- useFocusEffect(
|
||||
- React.useCallback(() => {
|
||||
- setMinimalShellMode(false)
|
||||
- }, [setMinimalShellMode]),
|
||||
- )
|
||||
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
|
||||
+export function TermsOfServiceScreen() {
|
||||
+ useSetTitle('Terms of Service')
|
||||
return (
|
||||
<Layout.Screen>
|
||||
- <ViewHeader title={_(msg`Terms of Service`)} />
|
||||
- <ScrollView style={[s.hContentRegion, pal.view]}>
|
||||
- <View style={[s.p20]}>
|
||||
- <Text style={pal.text}>
|
||||
- <Trans>The Terms of Service have been moved to</Trans>{' '}
|
||||
- <TextLink
|
||||
- style={pal.link}
|
||||
- href="https://bsky.social/about/support/tos"
|
||||
- text="bsky.social/about/support/tos"
|
||||
- />
|
||||
- </Text>
|
||||
- </View>
|
||||
- <View style={s.footerSpacer} />
|
||||
- </ScrollView>
|
||||
+ <WebView source={{ uri: 'https://syu.is/about/support/tos' }} style={{ flex: 1 }} />
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
|
||||
index f76147ccf..36b4d7de1 100644
|
||||
--- a/src/view/shell/Drawer.tsx
|
||||
+++ b/src/view/shell/Drawer.tsx
|
||||
@@ -292,17 +292,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||
<>
|
||||
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
|
||||
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
|
||||
- <ChatMenuItem isActive={isAtMessages} onPress={onPressMessages} />
|
||||
<NotificationsMenuItem
|
||||
isActive={isAtNotifications}
|
||||
onPress={onPressNotifications}
|
||||
/>
|
||||
<FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
|
||||
- <ListsMenuItem onPress={onPressLists} />
|
||||
- <BookmarksMenuItem
|
||||
- isActive={isAtBookmarks}
|
||||
- onPress={onPressBookmarks}
|
||||
- />
|
||||
<ProfileMenuItem
|
||||
isActive={isAtMyProfile}
|
||||
onPress={onPressProfile}
|
||||
@@ -357,17 +351,7 @@ let DrawerFooter = ({
|
||||
),
|
||||
},
|
||||
]}>
|
||||
- <Button
|
||||
- label={_(msg`Send feedback`)}
|
||||
- size="small"
|
||||
- variant="solid"
|
||||
- color="secondary"
|
||||
- onPress={onPressFeedback}>
|
||||
- <ButtonIcon icon={Message} position="left" />
|
||||
- <ButtonText>
|
||||
- <Trans>Feedback</Trans>
|
||||
- </ButtonText>
|
||||
- </Button>
|
||||
+{/* Feedback button removed for syu.is */}
|
||||
<Button
|
||||
label={_(msg`Get help`)}
|
||||
size="small"
|
||||
@@ -695,12 +679,12 @@ function ExtraLinks() {
|
||||
<InlineLinkText
|
||||
style={[a.text_md]}
|
||||
label={_(msg`Terms of Service`)}
|
||||
- to="https://bsky.social/about/support/tos">
|
||||
+ to="https://syu.is/about/support/tos">
|
||||
<Trans>Terms of Service</Trans>
|
||||
</InlineLinkText>
|
||||
<InlineLinkText
|
||||
style={[a.text_md]}
|
||||
- to="https://bsky.social/about/support/privacy-policy"
|
||||
+ to="https://syu.is/about/support/privacy-policy"
|
||||
label={_(msg`Privacy Policy`)}>
|
||||
<Trans>Privacy Policy</Trans>
|
||||
</InlineLinkText>
|
||||
@@ -1,32 +0,0 @@
|
||||
diff --git a/plugins/notificationsExtension/withNotificationsExtension.js b/plugins/notificationsExtension/withNotificationsExtension.js
|
||||
index 6a00cfd23..f91decc08 100644
|
||||
--- a/plugins/notificationsExtension/withNotificationsExtension.js
|
||||
+++ b/plugins/notificationsExtension/withNotificationsExtension.js
|
||||
@@ -10,7 +10,7 @@ const EXTENSION_NAME = 'BlueskyNSE'
|
||||
const EXTENSION_CONTROLLER_NAME = 'NotificationService'
|
||||
|
||||
const withNotificationsExtension = config => {
|
||||
- const soundFiles = ['dm.aiff']
|
||||
+ const soundFiles = []
|
||||
|
||||
return withPlugins(config, [
|
||||
// IOS
|
||||
diff --git a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
||||
index 8365057e8..59c8506a2 100644
|
||||
--- a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
||||
+++ b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
||||
@@ -26,12 +26,12 @@ export function Content({state}: {state: PolicyUpdateState}) {
|
||||
const links = {
|
||||
terms: {
|
||||
overridePresentation: false,
|
||||
- to: `https://bsky.social/about/support/tos`,
|
||||
+ to: `https://syu.is/about/support/tos`,
|
||||
label: _(msg`Terms of Service`),
|
||||
},
|
||||
privacy: {
|
||||
overridePresentation: false,
|
||||
- to: `https://bsky.social/about/support/privacy-policy`,
|
||||
+ to: `https://syu.is/about/support/privacy-policy`,
|
||||
label: _(msg`Privacy Policy`),
|
||||
},
|
||||
copyright: {
|
||||
@@ -1,174 +0,0 @@
|
||||
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
|
||||
index fa33a9d56..13af087c2 100644
|
||||
--- a/src/Navigation.tsx
|
||||
+++ b/src/Navigation.tsx
|
||||
@@ -62,6 +62,7 @@ import {NotFoundScreen} from '#/view/screens/NotFound'
|
||||
import {NotificationsScreen} from '#/view/screens/Notifications'
|
||||
import {PostThreadScreen} from '#/view/screens/PostThread'
|
||||
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
|
||||
+import {LicenseScreen} from '#/view/screens/License'
|
||||
import {ProfileScreen} from '#/view/screens/Profile'
|
||||
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
|
||||
import {Storybook} from '#/view/screens/Storybook'
|
||||
@@ -335,6 +336,11 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
|
||||
getComponent={() => TermsOfServiceScreen}
|
||||
options={{title: title(msg`Terms of Service`)}}
|
||||
/>
|
||||
+ <Stack.Screen
|
||||
+ name="License"
|
||||
+ getComponent={() => LicenseScreen}
|
||||
+ options={{title: title(msg`License`)}}
|
||||
+ />
|
||||
<Stack.Screen
|
||||
name="CommunityGuidelines"
|
||||
getComponent={() => CommunityGuidelinesScreen}
|
||||
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
|
||||
index c315a8341..9b2f50a83 100644
|
||||
--- a/src/lib/routes/types.ts
|
||||
+++ b/src/lib/routes/types.ts
|
||||
@@ -39,6 +39,7 @@ export type CommonNavigatorParams = {
|
||||
Support: undefined
|
||||
PrivacyPolicy: undefined
|
||||
TermsOfService: undefined
|
||||
+ License: undefined
|
||||
CommunityGuidelines: undefined
|
||||
CopyrightPolicy: undefined
|
||||
LanguageSettings: undefined
|
||||
diff --git a/src/view/screens/License.tsx b/src/view/screens/License.tsx
|
||||
new file mode 100644
|
||||
index 000000000..87f52a972
|
||||
--- /dev/null
|
||||
+++ b/src/view/screens/License.tsx
|
||||
@@ -0,0 +1,132 @@
|
||||
+import React from 'react'
|
||||
+import { ScrollView, Text as RNText, StyleSheet } from 'react-native'
|
||||
+import * as Layout from '#/components/Layout'
|
||||
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
+import {atoms as a, useTheme} from '#/alf'
|
||||
+
|
||||
+export function LicenseScreen() {
|
||||
+ useSetTitle('License')
|
||||
+ const t = useTheme()
|
||||
+
|
||||
+ return (
|
||||
+ <Layout.Screen>
|
||||
+ <Layout.Header.Outer>
|
||||
+ <Layout.Header.BackButton />
|
||||
+ <Layout.Header.Content>
|
||||
+ <Layout.Header.TitleText>License</Layout.Header.TitleText>
|
||||
+ </Layout.Header.Content>
|
||||
+ <Layout.Header.Slot />
|
||||
+ </Layout.Header.Outer>
|
||||
+ <Layout.Content>
|
||||
+ <ScrollView
|
||||
+ style={[a.flex_1]}
|
||||
+ contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
|
||||
+ <RNText style={styles.text}>
|
||||
+ This application is based on Bluesky Social App.
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.link}>
|
||||
+ https://github.com/bluesky-social/social-app
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.sectionTitle}>MIT License</RNText>
|
||||
+
|
||||
+ <RNText style={styles.mono}>
|
||||
+ Copyright (c) 2022-2025 Bluesky PBC
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.text}>
|
||||
+ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
+ of this software and associated documentation files (the "Software"), to deal
|
||||
+ in the Software without restriction, including without limitation the rights
|
||||
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
+ copies of the Software, and to permit persons to whom the Software is
|
||||
+ furnished to do so, subject to the following conditions:
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.text}>
|
||||
+ The above copyright notice and this permission notice shall be included in all
|
||||
+ copies or substantial portions of the Software.
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.text}>
|
||||
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
+ SOFTWARE.
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.sectionTitle2}>日本語訳(参考)</RNText>
|
||||
+
|
||||
+ <RNText style={styles.text}>
|
||||
+ 本ソフトウェアおよび関連文書ファイル(以下「ソフトウェア」)のコピーを取得する
|
||||
+ すべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、
|
||||
+ ソフトウェアのコピーを使用、複製、変更、結合、公開、配布、サブライセンス、
|
||||
+ および/または販売する権利、ならびにソフトウェアを提供する相手にそうした行為を
|
||||
+ 許可する権利が含まれますが、これらに限定されません。
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.text}>
|
||||
+ 上記の著作権表示および本許諾表示を、ソフトウェアのすべてのコピーまたは
|
||||
+ 重要な部分に記載するものとします。
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.text}>
|
||||
+ ソフトウェアは「現状のまま」で提供され、明示黙示を問わず、商品性、特定目的への
|
||||
+ 適合性、および権利非侵害についての保証を含む、いかなる種類の保証もなされません。
|
||||
+ いかなる場合においても、作者または著作権者は、契約行為、不法行為、またはそれ以外で
|
||||
+ あろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用または
|
||||
+ その他の扱いによって生じる一切の請求、損害、その他の義務について責任を負わないものとします。
|
||||
+ </RNText>
|
||||
+
|
||||
+ <RNText style={styles.footer}>
|
||||
+ Original License: https://github.com/bluesky-social/social-app/blob/main/LICENSE
|
||||
+ </RNText>
|
||||
+ </ScrollView>
|
||||
+ </Layout.Content>
|
||||
+ </Layout.Screen>
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+const styles = StyleSheet.create({
|
||||
+ title: {
|
||||
+ fontSize: 24,
|
||||
+ fontWeight: 'bold',
|
||||
+ marginBottom: 16,
|
||||
+ },
|
||||
+ text: {
|
||||
+ fontSize: 14,
|
||||
+ marginBottom: 12,
|
||||
+ lineHeight: 20,
|
||||
+ },
|
||||
+ link: {
|
||||
+ fontSize: 14,
|
||||
+ marginBottom: 12,
|
||||
+ color: '#0066cc',
|
||||
+ },
|
||||
+ sectionTitle: {
|
||||
+ fontSize: 18,
|
||||
+ fontWeight: 'bold',
|
||||
+ marginTop: 16,
|
||||
+ marginBottom: 12,
|
||||
+ },
|
||||
+ sectionTitle2: {
|
||||
+ fontSize: 18,
|
||||
+ fontWeight: 'bold',
|
||||
+ marginTop: 24,
|
||||
+ marginBottom: 12,
|
||||
+ },
|
||||
+ mono: {
|
||||
+ fontSize: 14,
|
||||
+ marginBottom: 12,
|
||||
+ fontFamily: 'monospace',
|
||||
+ },
|
||||
+ footer: {
|
||||
+ fontSize: 12,
|
||||
+ marginTop: 24,
|
||||
+ color: '#666666',
|
||||
+ },
|
||||
+})
|
||||
@@ -1,25 +0,0 @@
|
||||
diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx
|
||||
index aa6cd4156..37c7a38b0 100644
|
||||
--- a/src/screens/Signup/index.tsx
|
||||
+++ b/src/screens/Signup/index.tsx
|
||||
@@ -211,20 +211,6 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
a.align_center,
|
||||
]}>
|
||||
<AppLanguageDropdown />
|
||||
- <Text
|
||||
- style={[
|
||||
- a.flex_1,
|
||||
- t.atoms.text_contrast_medium,
|
||||
- !gtMobile && a.text_md,
|
||||
- ]}>
|
||||
- <Trans>Having trouble?</Trans>{' '}
|
||||
- <InlineLinkText
|
||||
- label={_(msg`Contact support`)}
|
||||
- to={FEEDBACK_FORM_URL({email: state.email})}
|
||||
- style={[!gtMobile && a.text_md]}>
|
||||
- <Trans>Contact support</Trans>
|
||||
- </InlineLinkText>
|
||||
- </Text>
|
||||
</View>
|
||||
</View>
|
||||
</ScreenTransition>
|
||||
@@ -1,77 +0,0 @@
|
||||
diff --git a/src/routes.ts b/src/routes.ts
|
||||
--- a/src/routes.ts
|
||||
+++ b/src/routes.ts
|
||||
@@ -74,6 +74,7 @@ export const router = new Router<AllNavigatableRoutes>({
|
||||
PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
|
||||
TermsOfService: 'https://syu.is/about/support/tos',
|
||||
CommunityGuidelines: '/support/community-guidelines',
|
||||
+ License: 'https://syu.is/about/support/license',
|
||||
CopyrightPolicy: '/support/copyright',
|
||||
// hashtags
|
||||
Hashtag: '/hashtag/:tag',
|
||||
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
|
||||
--- a/src/view/com/auth/SplashScreen.tsx
|
||||
+++ b/src/view/com/auth/SplashScreen.tsx
|
||||
@@ -1,4 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
+import {Pressable, Linking} from 'react-native'
|
||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
@@ -40,16 +41,6 @@ export const SplashScreen = ({
|
||||
<View style={[a.pb_sm, a.pt_5xl]}>
|
||||
<Logotype width={161} fill={t.atoms.text.color} />
|
||||
</View>
|
||||
-
|
||||
- <Text
|
||||
- style={[
|
||||
- a.text_md,
|
||||
- a.font_semi_bold,
|
||||
- t.atoms.text_contrast_medium,
|
||||
- a.text_center,
|
||||
- ]}>
|
||||
- <Trans>What's up?</Trans>
|
||||
- </Text>
|
||||
</View>
|
||||
|
||||
<View
|
||||
@@ -102,6 +93,21 @@ export const SplashScreen = ({
|
||||
<AppLanguageDropdown />
|
||||
</View>
|
||||
</View>
|
||||
+ <View style={[a.pb_sm, a.justify_center, a.align_center]}>
|
||||
+ <Pressable onPress={() => Linking.openURL('https://syu.is/about/support/license')}>
|
||||
+ <Text
|
||||
+ style={[
|
||||
+ a.text_xs,
|
||||
+ t.atoms.text_contrast_low,
|
||||
+ {textDecorationLine: 'underline'},
|
||||
+ ]}>
|
||||
+ License
|
||||
+ </Text>
|
||||
+ </Pressable>
|
||||
+ </View>
|
||||
+ <View style={[a.pb_xl, a.justify_center, a.align_center]}>
|
||||
+ <Text style={[a.text_xs, t.atoms.text_contrast_low]}>© syui</Text>
|
||||
+ </View>
|
||||
<View style={{height: insets.bottom}} />
|
||||
</ErrorBoundary>
|
||||
</Animated.View>
|
||||
diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
|
||||
--- a/src/view/com/auth/SplashScreen.web.tsx
|
||||
+++ b/src/view/com/auth/SplashScreen.web.tsx
|
||||
@@ -94,14 +94,6 @@ export const SplashScreen = ({
|
||||
</View>
|
||||
)}
|
||||
|
||||
- <Text
|
||||
- style={[
|
||||
- a.text_md,
|
||||
- a.font_semi_bold,
|
||||
- t.atoms.text_contrast_medium,
|
||||
- ]}>
|
||||
- <Trans>What's up?</Trans>
|
||||
- </Text>
|
||||
</View>
|
||||
|
||||
<View
|
||||
@@ -1,48 +0,0 @@
|
||||
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
|
||||
index 6b0e184c0..42b609c9e 100644
|
||||
--- a/src/screens/Settings/Settings.tsx
|
||||
+++ b/src/screens/Settings/Settings.tsx
|
||||
@@ -203,24 +203,8 @@ export function SettingsScreen({}: Props) {
|
||||
<Trans>Notifications</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
- <SettingsList.LinkItem
|
||||
- to="/settings/content-and-media"
|
||||
- label={_(msg`Content and media`)}>
|
||||
- <SettingsList.ItemIcon icon={WindowIcon} />
|
||||
- <SettingsList.ItemText>
|
||||
- <Trans>Content and media</Trans>
|
||||
- </SettingsList.ItemText>
|
||||
- </SettingsList.LinkItem>
|
||||
- {isNative && findContactsEnabled && (
|
||||
- <SettingsList.LinkItem
|
||||
- to="/settings/find-contacts"
|
||||
- label={_(msg`Find friends from contacts`)}>
|
||||
- <SettingsList.ItemIcon icon={ContactsIcon} />
|
||||
- <SettingsList.ItemText>
|
||||
- <Trans>Find friends from contacts</Trans>
|
||||
- </SettingsList.ItemText>
|
||||
- </SettingsList.LinkItem>
|
||||
- )}
|
||||
+{/* Content and media removed for syu.is */}
|
||||
+{/* Find friends from contacts removed for syu.is */}
|
||||
<SettingsList.LinkItem
|
||||
to="/settings/appearance"
|
||||
label={_(msg`Appearance`)}>
|
||||
@@ -245,16 +229,6 @@ export function SettingsScreen({}: Props) {
|
||||
<Trans>Languages</Trans>
|
||||
</SettingsList.ItemText>
|
||||
</SettingsList.LinkItem>
|
||||
- <SettingsList.PressableItem
|
||||
- onPress={() => Linking.openURL(HELP_DESK_URL)}
|
||||
- label={_(msg`Help`)}
|
||||
- accessibilityHint={_(msg`Opens helpdesk in browser`)}>
|
||||
- <SettingsList.ItemIcon icon={CircleQuestionIcon} />
|
||||
- <SettingsList.ItemText>
|
||||
- <Trans>Help</Trans>
|
||||
- </SettingsList.ItemText>
|
||||
- <SettingsList.Chevron />
|
||||
- </SettingsList.PressableItem>
|
||||
<SettingsList.LinkItem to="/settings/about" label={_(msg`About`)}>
|
||||
<SettingsList.ItemIcon icon={BubbleInfoIcon} />
|
||||
<SettingsList.ItemText>
|
||||
@@ -1,31 +0,0 @@
|
||||
diff --git a/plugins/withCodeSignEntitlements.js b/plugins/withCodeSignEntitlements.js
|
||||
new file mode 100644
|
||||
index 000000000..b03b6bd68
|
||||
--- /dev/null
|
||||
+++ b/plugins/withCodeSignEntitlements.js
|
||||
@@ -0,0 +1,25 @@
|
||||
+/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
+const { withXcodeProject } = require('@expo/config-plugins')
|
||||
+
|
||||
+const withCodeSignEntitlements = (config) => {
|
||||
+ return withXcodeProject(config, (config) => {
|
||||
+ const xcodeProject = config.modResults
|
||||
+ const configurations = xcodeProject.pbxXCBuildConfigurationSection()
|
||||
+
|
||||
+ for (const key in configurations) {
|
||||
+ const configuration = configurations[key]
|
||||
+ if (
|
||||
+ configuration.buildSettings &&
|
||||
+ configuration.comment &&
|
||||
+ !configuration.comment.includes('TEST')
|
||||
+ ) {
|
||||
+ configuration.buildSettings.CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION =
|
||||
+ 'YES'
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return config
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+module.exports = withCodeSignEntitlements
|
||||
@@ -1,30 +0,0 @@
|
||||
diff --git a/src/ageAssurance/index.tsx b/src/ageAssurance/index.tsx
|
||||
index 9a0a9c9d5..5a6563e52 100644
|
||||
--- a/src/ageAssurance/index.tsx
|
||||
+++ b/src/ageAssurance/index.tsx
|
||||
@@ -88,19 +88,16 @@ function InnerProvider({children}: {children: React.ReactNode}) {
|
||||
return (
|
||||
<AgeAssuranceStateContext.Provider
|
||||
value={useMemo(() => {
|
||||
- const chatDisabled = state.access !== AgeAssuranceAccess.Full
|
||||
- const isUnderage = data?.birthdate
|
||||
- ? isUserUnderAdultAge(data.birthdate)
|
||||
- : true
|
||||
- const adultContentDisabled =
|
||||
- state.access !== AgeAssuranceAccess.Full || isUnderage
|
||||
return {
|
||||
Access: AgeAssuranceAccess,
|
||||
Status: AgeAssuranceStatus,
|
||||
- state,
|
||||
+ state: {
|
||||
+ ...state,
|
||||
+ access: AgeAssuranceAccess.Full,
|
||||
+ },
|
||||
flags: {
|
||||
- adultContentDisabled,
|
||||
- chatDisabled,
|
||||
+ adultContentDisabled: false,
|
||||
+ chatDisabled: false,
|
||||
},
|
||||
}
|
||||
}, [state, data])}>
|
||||
@@ -1,247 +0,0 @@
|
||||
diff --git a/src/view/com/posts/DiscoverFallbackHeader.tsx b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||
index e35a33aaf..a36f84ae0 100644
|
||||
--- a/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||
+++ b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||
@@ -7,37 +7,5 @@ import {TextLink} from '../util/Link'
|
||||
import {Text} from '../util/text/Text'
|
||||
|
||||
export function DiscoverFallbackHeader() {
|
||||
- const pal = usePalette('default')
|
||||
- return (
|
||||
- <View
|
||||
- style={[
|
||||
- {
|
||||
- flexDirection: 'row',
|
||||
- alignItems: 'center',
|
||||
- paddingVertical: 12,
|
||||
- paddingHorizontal: 12,
|
||||
- borderTopWidth: 1,
|
||||
- },
|
||||
- pal.border,
|
||||
- pal.viewLight,
|
||||
- ]}>
|
||||
- <View style={{width: 68, paddingLeft: 12}}>
|
||||
- <InfoCircleIcon size={36} style={pal.textLight} strokeWidth={1.5} />
|
||||
- </View>
|
||||
- <View style={{flex: 1}}>
|
||||
- <Text type="md" style={pal.text}>
|
||||
- <Trans>
|
||||
- We ran out of posts from your follows. Here's the latest from{' '}
|
||||
- <TextLink
|
||||
- type="md-medium"
|
||||
- href="/profile/bsky.app/feed/whats-hot"
|
||||
- text="Discover"
|
||||
- style={pal.link}
|
||||
- />
|
||||
- .
|
||||
- </Trans>
|
||||
- </Text>
|
||||
- </View>
|
||||
- </View>
|
||||
- )
|
||||
+ return null
|
||||
}
|
||||
diff --git a/src/view/com/posts/FollowingEmptyState.tsx b/src/view/com/posts/FollowingEmptyState.tsx
|
||||
index 352cc1dc0..f477521af 100644
|
||||
--- a/src/view/com/posts/FollowingEmptyState.tsx
|
||||
+++ b/src/view/com/posts/FollowingEmptyState.tsx
|
||||
@@ -1,37 +1,14 @@
|
||||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
-import {
|
||||
- FontAwesomeIcon,
|
||||
- type FontAwesomeIconStyle,
|
||||
-} from '@fortawesome/react-native-fontawesome'
|
||||
import {Trans} from '@lingui/macro'
|
||||
-import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {MagnifyingGlassIcon} from '#/lib/icons'
|
||||
-import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {s} from '#/lib/styles'
|
||||
-import {isWeb} from '#/platform/detection'
|
||||
-import {Button} from '../util/forms/Button'
|
||||
import {Text} from '../util/text/Text'
|
||||
|
||||
export function FollowingEmptyState() {
|
||||
const pal = usePalette('default')
|
||||
- const palInverted = usePalette('inverted')
|
||||
- const navigation = useNavigation<NavigationProp>()
|
||||
-
|
||||
- const onPressFindAccounts = React.useCallback(() => {
|
||||
- if (isWeb) {
|
||||
- navigation.navigate('Search', {})
|
||||
- } else {
|
||||
- navigation.navigate('SearchTab')
|
||||
- navigation.popToTop()
|
||||
- }
|
||||
- }, [navigation])
|
||||
-
|
||||
- const onPressDiscoverFeeds = React.useCallback(() => {
|
||||
- navigation.navigate('Feeds')
|
||||
- }, [navigation])
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@@ -45,36 +22,6 @@ export function FollowingEmptyState() {
|
||||
happening.
|
||||
</Trans>
|
||||
</Text>
|
||||
- <Button
|
||||
- type="inverted"
|
||||
- style={styles.emptyBtn}
|
||||
- onPress={onPressFindAccounts}>
|
||||
- <Text type="lg-medium" style={palInverted.text}>
|
||||
- <Trans>Find accounts to follow</Trans>
|
||||
- </Text>
|
||||
- <FontAwesomeIcon
|
||||
- icon="angle-right"
|
||||
- style={palInverted.text as FontAwesomeIconStyle}
|
||||
- size={14}
|
||||
- />
|
||||
- </Button>
|
||||
-
|
||||
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||
- </Text>
|
||||
- <Button
|
||||
- type="inverted"
|
||||
- style={[styles.emptyBtn, s.mt10]}
|
||||
- onPress={onPressDiscoverFeeds}>
|
||||
- <Text type="lg-medium" style={palInverted.text}>
|
||||
- <Trans>Discover new custom feeds</Trans>
|
||||
- </Text>
|
||||
- <FontAwesomeIcon
|
||||
- icon="angle-right"
|
||||
- style={palInverted.text as FontAwesomeIconStyle}
|
||||
- size={14}
|
||||
- />
|
||||
- </Button>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
@@ -98,13 +45,4 @@ const styles = StyleSheet.create({
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
- emptyBtn: {
|
||||
- marginVertical: 20,
|
||||
- flexDirection: 'row',
|
||||
- alignItems: 'center',
|
||||
- justifyContent: 'space-between',
|
||||
- paddingVertical: 18,
|
||||
- paddingHorizontal: 24,
|
||||
- borderRadius: 30,
|
||||
- },
|
||||
})
|
||||
diff --git a/src/view/com/posts/FollowingEndOfFeed.tsx b/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||
index e3c84d782..efb55d406 100644
|
||||
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||
@@ -1,36 +1,13 @@
|
||||
import React from 'react'
|
||||
import {Dimensions, StyleSheet, View} from 'react-native'
|
||||
-import {
|
||||
- FontAwesomeIcon,
|
||||
- type FontAwesomeIconStyle,
|
||||
-} from '@fortawesome/react-native-fontawesome'
|
||||
import {Trans} from '@lingui/macro'
|
||||
-import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
-import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {s} from '#/lib/styles'
|
||||
-import {isWeb} from '#/platform/detection'
|
||||
-import {Button} from '../util/forms/Button'
|
||||
import {Text} from '../util/text/Text'
|
||||
|
||||
export function FollowingEndOfFeed() {
|
||||
const pal = usePalette('default')
|
||||
- const palInverted = usePalette('inverted')
|
||||
- const navigation = useNavigation<NavigationProp>()
|
||||
-
|
||||
- const onPressFindAccounts = React.useCallback(() => {
|
||||
- if (isWeb) {
|
||||
- navigation.navigate('Search', {})
|
||||
- } else {
|
||||
- navigation.navigate('SearchTab')
|
||||
- navigation.popToTop()
|
||||
- }
|
||||
- }, [navigation])
|
||||
-
|
||||
- const onPressDiscoverFeeds = React.useCallback(() => {
|
||||
- navigation.navigate('Feeds')
|
||||
- }, [navigation])
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -41,41 +18,8 @@ export function FollowingEndOfFeed() {
|
||||
]}>
|
||||
<View style={styles.inner}>
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||
- <Trans>
|
||||
- You've reached the end of your feed! Find some more accounts to
|
||||
- follow.
|
||||
- </Trans>
|
||||
- </Text>
|
||||
- <Button
|
||||
- type="inverted"
|
||||
- style={styles.emptyBtn}
|
||||
- onPress={onPressFindAccounts}>
|
||||
- <Text type="lg-medium" style={palInverted.text}>
|
||||
- <Trans>Find accounts to follow</Trans>
|
||||
- </Text>
|
||||
- <FontAwesomeIcon
|
||||
- icon="angle-right"
|
||||
- style={palInverted.text as FontAwesomeIconStyle}
|
||||
- size={14}
|
||||
- />
|
||||
- </Button>
|
||||
-
|
||||
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||
+ <Trans>You've reached the end of your feed!</Trans>
|
||||
</Text>
|
||||
- <Button
|
||||
- type="inverted"
|
||||
- style={[styles.emptyBtn, s.mt10]}
|
||||
- onPress={onPressDiscoverFeeds}>
|
||||
- <Text type="lg-medium" style={palInverted.text}>
|
||||
- <Trans>Discover new custom feeds</Trans>
|
||||
- </Text>
|
||||
- <FontAwesomeIcon
|
||||
- icon="angle-right"
|
||||
- style={palInverted.text as FontAwesomeIconStyle}
|
||||
- size={14}
|
||||
- />
|
||||
- </Button>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
@@ -93,13 +37,4 @@ const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
maxWidth: 460,
|
||||
},
|
||||
- emptyBtn: {
|
||||
- marginVertical: 20,
|
||||
- flexDirection: 'row',
|
||||
- alignItems: 'center',
|
||||
- justifyContent: 'space-between',
|
||||
- paddingVertical: 18,
|
||||
- paddingHorizontal: 24,
|
||||
- borderRadius: 30,
|
||||
- },
|
||||
})
|
||||
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
|
||||
index 4f25468c9..a72a10b80 100644
|
||||
--- a/src/view/com/posts/PostFeed.tsx
|
||||
+++ b/src/view/com/posts/PostFeed.tsx
|
||||
@@ -766,7 +766,7 @@ let PostFeed = ({
|
||||
} else if (row.type === 'feedShutdownMsg') {
|
||||
return <FeedShutdownMsg feedUri={feedUriOrActorDid} />
|
||||
} else if (row.type === 'interstitialFollows') {
|
||||
- return <SuggestedFollows feed={feed} />
|
||||
+ return null
|
||||
} else if (row.type === 'interstitialProgressGuide') {
|
||||
return <ProgressGuide />
|
||||
} else if (row.type === 'ageAssuranceBanner') {
|
||||
@@ -1,51 +0,0 @@
|
||||
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
|
||||
index 790f211ee..ec05a8bcd 100644
|
||||
--- a/bskyweb/cmd/bskyweb/server.go
|
||||
+++ b/bskyweb/cmd/bskyweb/server.go
|
||||
@@ -317,6 +317,12 @@ func serve(cctx *cli.Context) error {
|
||||
e.GET("/support/tos", server.WebGeneric)
|
||||
e.GET("/support/community-guidelines", server.WebGeneric)
|
||||
e.GET("/support/copyright", server.WebGeneric)
|
||||
+ // about/support pages (syu.is specific)
|
||||
+ e.GET("/about/support/tos", server.WebAboutTOS)
|
||||
+ e.GET("/about/support/privacy-policy", server.WebAboutPrivacy)
|
||||
+ e.GET("/about/support/help", server.WebAboutHelp)
|
||||
+ e.GET("/about/support/license", server.WebAboutLicense)
|
||||
+ e.GET("/about/support/app", server.WebAboutApp)
|
||||
e.GET("/intent/compose", server.WebGeneric)
|
||||
e.GET("/intent/verify-email", server.WebGeneric)
|
||||
e.GET("/intent/age-assurance", server.WebGeneric)
|
||||
@@ -825,3 +831,33 @@ func (srv *Server) serveSitemapRequest(c echo.Context, url, sitemapType string)
|
||||
|
||||
return nil
|
||||
}
|
||||
+
|
||||
+// Handler for About TOS page (syu.is specific)
|
||||
+func (srv *Server) WebAboutTOS(c echo.Context) error {
|
||||
+ data := srv.NewTemplateContext()
|
||||
+ return c.Render(http.StatusOK, "about-tos.html", data)
|
||||
+}
|
||||
+
|
||||
+// Handler for About Privacy Policy page (syu.is specific)
|
||||
+func (srv *Server) WebAboutPrivacy(c echo.Context) error {
|
||||
+ data := srv.NewTemplateContext()
|
||||
+ return c.Render(http.StatusOK, "about-privacy.html", data)
|
||||
+}
|
||||
+
|
||||
+// Handler for About Help page (syu.is specific)
|
||||
+func (srv *Server) WebAboutHelp(c echo.Context) error {
|
||||
+ data := srv.NewTemplateContext()
|
||||
+ return c.Render(http.StatusOK, "about-help.html", data)
|
||||
+}
|
||||
+
|
||||
+// Handler for About License page (syu.is specific)
|
||||
+func (srv *Server) WebAboutLicense(c echo.Context) error {
|
||||
+ data := srv.NewTemplateContext()
|
||||
+ return c.Render(http.StatusOK, "about-license.html", data)
|
||||
+}
|
||||
+
|
||||
+// Handler for About App page (syu.is specific)
|
||||
+func (srv *Server) WebAboutApp(c echo.Context) error {
|
||||
+ data := srv.NewTemplateContext()
|
||||
+ return c.Render(http.StatusOK, "about-app.html", data)
|
||||
+}
|
||||
@@ -1,70 +0,0 @@
|
||||
diff --git a/src/state/messages/events/index.tsx b/src/state/messages/events/index.tsx
|
||||
index 2ff0784ae..dc314ecc5 100644
|
||||
--- a/src/state/messages/events/index.tsx
|
||||
+++ b/src/state/messages/events/index.tsx
|
||||
@@ -10,13 +10,7 @@ const MessagesEventBusContext = React.createContext<MessagesEventBus | null>(
|
||||
MessagesEventBusContext.displayName = 'MessagesEventBusContext'
|
||||
|
||||
export function useMessagesEventBus() {
|
||||
- const ctx = React.useContext(MessagesEventBusContext)
|
||||
- if (!ctx) {
|
||||
- throw new Error(
|
||||
- 'useMessagesEventBus must be used within a MessagesEventBusProvider',
|
||||
- )
|
||||
- }
|
||||
- return ctx
|
||||
+ return React.useContext(MessagesEventBusContext)
|
||||
}
|
||||
|
||||
export function MessagesEventBusProvider({
|
||||
@@ -24,18 +18,11 @@ export function MessagesEventBusProvider({
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
- const {currentAccount} = useSession()
|
||||
-
|
||||
- if (!currentAccount) {
|
||||
- return (
|
||||
- <MessagesEventBusContext.Provider value={null}>
|
||||
- {children}
|
||||
- </MessagesEventBusContext.Provider>
|
||||
- )
|
||||
- }
|
||||
-
|
||||
+ // DM functionality is disabled for syu.is
|
||||
return (
|
||||
- <MessagesEventBusProviderInner>{children}</MessagesEventBusProviderInner>
|
||||
+ <MessagesEventBusContext.Provider value={null}>
|
||||
+ {children}
|
||||
+ </MessagesEventBusContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
diff --git a/src/state/queries/messages/list-conversations.tsx b/src/state/queries/messages/list-conversations.tsx
|
||||
index c5457d1cb..5bc37bdce 100644
|
||||
--- a/src/state/queries/messages/list-conversations.tsx
|
||||
+++ b/src/state/queries/messages/list-conversations.tsx
|
||||
@@ -74,17 +74,12 @@ export function useListConvos() {
|
||||
|
||||
const empty = {accepted: [], request: []}
|
||||
export function ListConvosProvider({children}: {children: React.ReactNode}) {
|
||||
- const {hasSession} = useSession()
|
||||
-
|
||||
- if (!hasSession) {
|
||||
- return (
|
||||
- <ListConvosContext.Provider value={empty}>
|
||||
- {children}
|
||||
- </ListConvosContext.Provider>
|
||||
- )
|
||||
- }
|
||||
-
|
||||
- return <ListConvosProviderInner>{children}</ListConvosProviderInner>
|
||||
+ // DM functionality is disabled for syu.is - always return empty
|
||||
+ return (
|
||||
+ <ListConvosContext.Provider value={empty}>
|
||||
+ {children}
|
||||
+ </ListConvosContext.Provider>
|
||||
+ )
|
||||
}
|
||||
|
||||
export function ListConvosProviderInner({
|
||||
@@ -1,15 +0,0 @@
|
||||
diff --git a/src/env/common.ts b/src/env/common.ts
|
||||
--- a/src/env/common.ts
|
||||
+++ b/src/env/common.ts
|
||||
@@ -107,9 +107,8 @@ export const GCP_PROJECT_ID: number =
|
||||
/**
|
||||
* URLs for the app config web worker. Can be a
|
||||
* locally running server, see `env.example` for more.
|
||||
+ * Disabled for self-hosted environment to avoid CORS errors
|
||||
*/
|
||||
export const BAPP_CONFIG_DEV_URL = process.env.BAPP_CONFIG_DEV_URL
|
||||
export const BAPP_CONFIG_PROD_URL = `https://ip.bsky.app`
|
||||
-export const BAPP_CONFIG_URL = IS_DEV
|
||||
- ? (BAPP_CONFIG_DEV_URL ?? BAPP_CONFIG_PROD_URL)
|
||||
- : BAPP_CONFIG_PROD_URL
|
||||
+export const BAPP_CONFIG_URL = null
|
||||
@@ -1,91 +0,0 @@
|
||||
diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
|
||||
--- a/bskyweb/templates/base.html
|
||||
+++ b/bskyweb/templates/base.html
|
||||
@@ -7,9 +7,9 @@
|
||||
<!--
|
||||
Preconnect to essential domains
|
||||
-->
|
||||
- <link rel="preconnect" href="https://bsky.social">
|
||||
- <link rel="preconnect" href="https://go.bsky.app">
|
||||
- <title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
|
||||
+ <link rel="preconnect" href="https://syu.is">
|
||||
+ <link rel="preconnect" href="https://bsky.syu.is">
|
||||
+ <title>{%- block head_title -%}syu.is{%- endblock -%}</title>
|
||||
|
||||
<!-- Hello Humans! API docs at https://atproto.com -->
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<noscript>
|
||||
<h1 lang="en">JavaScript Required</h1>
|
||||
<p lang="en">This is a heavily interactive web application, and JavaScript is required. Simple HTML interfaces are possible, but that is not what this is.
|
||||
- <p lang="en">Learn more about Bluesky at <a href="https://bsky.social">bsky.social</a> and <a href="https://atproto.com">atproto.com</a>.
|
||||
+ <p lang="en">Learn more at <a href="https://syu.is">syu.is</a> and <a href="https://atproto.com">atproto.com</a>.
|
||||
{% block noscript_extra %}{% endblock %}
|
||||
</noscript>
|
||||
{% endblock -%}
|
||||
diff --git a/bskyweb/templates/home.html b/bskyweb/templates/home.html
|
||||
--- a/bskyweb/templates/home.html
|
||||
+++ b/bskyweb/templates/home.html
|
||||
@@ -1,17 +1,17 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
-{% block head_title %}Bluesky{% endblock %}
|
||||
+{% block head_title %}syu.is{% endblock %}
|
||||
|
||||
{% block html_head_extra -%}
|
||||
- <meta property="og:title" content="Bluesky" />
|
||||
- <meta name="twitter:title" content="Bluesky" />
|
||||
+ <meta property="og:title" content="syu.is" />
|
||||
+ <meta name="twitter:title" content="syu.is" />
|
||||
|
||||
<meta name="description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
|
||||
<meta name="og:description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
|
||||
<meta name="twitter:description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
|
||||
|
||||
- <meta property="og:url" content="https://bsky.app" />
|
||||
- <meta name="twitter:url" content="https://bsky.app" />
|
||||
- <link rel="canonical" href="https://bsky.app" />
|
||||
+ <meta property="og:url" content="https://syu.is" />
|
||||
+ <meta name="twitter:url" content="https://syu.is" />
|
||||
+ <link rel="canonical" href="https://syu.is" />
|
||||
|
||||
<meta property="og:image" content="https://bsky.app/static/social-card-default-gradient.png" />
|
||||
diff --git a/bskyweb/templates/error.html b/bskyweb/templates/error.html
|
||||
--- a/bskyweb/templates/error.html
|
||||
+++ b/bskyweb/templates/error.html
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
-{% block head_title %}Error {{ statusCode }} - Bluesky{% endblock %}
|
||||
+{% block head_title %}Error {{ statusCode }} - syu.is{% endblock %}
|
||||
|
||||
{% block noscript_extra %}
|
||||
{%- if statusCode == 404 %}
|
||||
diff --git a/bskyweb/templates/starterpack.html b/bskyweb/templates/starterpack.html
|
||||
--- a/bskyweb/templates/starterpack.html
|
||||
+++ b/bskyweb/templates/starterpack.html
|
||||
@@ -17,8 +17,8 @@
|
||||
<meta property="og:title" content="{{ title }}" />
|
||||
<meta name="twitter:title" content="{{ title }}" />
|
||||
{%- else -%}
|
||||
- <meta property="og:title" content="Bluesky" />
|
||||
- <meta name="twitter:title" content="Bluesky" />
|
||||
+ <meta property="og:title" content="syu.is" />
|
||||
+ <meta name="twitter:title" content="syu.is" />
|
||||
{% endif -%}
|
||||
<meta name="description" content="Join the conversation" />
|
||||
<meta name="og:description" content="Join the conversation" />
|
||||
diff --git a/web/index.html b/web/index.html
|
||||
--- a/web/index.html
|
||||
+++ b/web/index.html
|
||||
@@ -14,8 +14,8 @@
|
||||
<!--
|
||||
Preconnect to essential domains
|
||||
-->
|
||||
- <link rel="preconnect" href="https://bsky.social">
|
||||
- <link rel="preconnect" href="https://go.bsky.app">
|
||||
+ <link rel="preconnect" href="https://syu.is">
|
||||
+ <link rel="preconnect" href="https://bsky.syu.is">
|
||||
<title>%WEB_TITLE%</title>
|
||||
|
||||
<link rel="preload" as="font" type="font/woff2" href="/static/media/InterVariable.c504db5c06caaf7cdfba.woff2" crossorigin>
|
||||
@@ -1,101 +0,0 @@
|
||||
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
|
||||
--- a/src/screens/Signup/StepInfo/index.tsx
|
||||
+++ b/src/screens/Signup/StepInfo/index.tsx
|
||||
@@ -7,11 +7,9 @@
|
||||
|
||||
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
||||
import {logger} from '#/logger'
|
||||
-import {is13, is18, useSignupContext} from '#/screens/Signup/state'
|
||||
+import {useSignupContext} from '#/screens/Signup/state'
|
||||
import {Policies} from '#/screens/Signup/StepInfo/Policies'
|
||||
import {atoms as a, native} from '#/alf'
|
||||
-import * as DateField from '#/components/forms/DateField'
|
||||
-import {type DateFieldRef} from '#/components/forms/DateField/types'
|
||||
import {FormError} from '#/components/forms/FormError'
|
||||
import {HostingProvider} from '#/components/forms/HostingProvider'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
@@ -22,16 +20,6 @@
|
||||
import {usePreemptivelyCompleteActivePolicyUpdate} from '#/components/PolicyUpdateOverlay/usePreemptivelyCompleteActivePolicyUpdate'
|
||||
import {BackNextButtons} from '../BackNextButtons'
|
||||
|
||||
-function sanitizeDate(date: Date): Date {
|
||||
- if (!date || date.toString() === 'Invalid Date') {
|
||||
- logger.error(`Create account: handled invalid date for birthDate`, {
|
||||
- hasDate: !!date,
|
||||
- })
|
||||
- return new Date()
|
||||
- }
|
||||
- return date
|
||||
-}
|
||||
-
|
||||
export function StepInfo({
|
||||
onPressBack,
|
||||
isServerError,
|
||||
@@ -55,7 +43,6 @@
|
||||
|
||||
const emailInputRef = useRef<TextInput>(null)
|
||||
const passwordInputRef = useRef<TextInput>(null)
|
||||
- const birthdateInputRef = useRef<DateFieldRef>(null)
|
||||
|
||||
const [hasWarnedEmail, setHasWarnedEmail] = React.useState<boolean>(false)
|
||||
|
||||
@@ -76,10 +63,6 @@
|
||||
const emailChanged = prevEmailValueRef.current !== email
|
||||
const password = passwordValueRef.current
|
||||
|
||||
- if (!is13(state.dateOfBirth)) {
|
||||
- return
|
||||
- }
|
||||
-
|
||||
if (state.serviceDescription?.inviteCodeRequired && !inviteCode) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
@@ -246,44 +229,21 @@
|
||||
secureTextEntry
|
||||
autoComplete="new-password"
|
||||
autoCapitalize="none"
|
||||
- returnKeyType="next"
|
||||
- submitBehavior={native('blurAndSubmit')}
|
||||
- onSubmitEditing={native(() =>
|
||||
- birthdateInputRef.current?.focus(),
|
||||
- )}
|
||||
+ returnKeyType="done"
|
||||
passwordRules="minlength: 8;"
|
||||
/>
|
||||
</TextField.Root>
|
||||
</View>
|
||||
- <View>
|
||||
- <DateField.LabelText>
|
||||
- <Trans>Your birth date</Trans>
|
||||
- </DateField.LabelText>
|
||||
- <DateField.DateField
|
||||
- testID="date"
|
||||
- inputRef={birthdateInputRef}
|
||||
- value={state.dateOfBirth}
|
||||
- onChangeDate={date => {
|
||||
- dispatch({
|
||||
- type: 'setDateOfBirth',
|
||||
- value: sanitizeDate(new Date(date)),
|
||||
- })
|
||||
- }}
|
||||
- label={_(msg`Date of birth`)}
|
||||
- accessibilityHint={_(msg`Select your date of birth`)}
|
||||
- maximumDate={new Date()}
|
||||
- />
|
||||
- </View>
|
||||
<Policies
|
||||
serviceDescription={state.serviceDescription}
|
||||
- needsGuardian={!is18(state.dateOfBirth)}
|
||||
- under13={!is13(state.dateOfBirth)}
|
||||
+ needsGuardian={false}
|
||||
+ under13={false}
|
||||
/>
|
||||
</>
|
||||
) : undefined}
|
||||
</View>
|
||||
<BackNextButtons
|
||||
- hideNext={!is13(state.dateOfBirth)}
|
||||
+ hideNext={false}
|
||||
showRetry={isServerError}
|
||||
isLoading={state.isLoading}
|
||||
onBackPress={onPressBack}
|
||||
@@ -1,16 +0,0 @@
|
||||
diff --git a/src/screens/Search/Explore.tsx b/src/screens/Search/Explore.tsx
|
||||
--- a/src/screens/Search/Explore.tsx
|
||||
+++ b/src/screens/Search/Explore.tsx
|
||||
@@ -687,12 +687,7 @@ export function Explore({
|
||||
|
||||
if (useFullExperience) {
|
||||
i.push(trendingTopicsModule)
|
||||
- i.push(...suggestedFeedsModule)
|
||||
- i.push(...suggestedFollowsModule)
|
||||
- i.push(...suggestedStarterPacksModule)
|
||||
i.push(...feedPreviewsModule)
|
||||
- } else {
|
||||
- i.push(...suggestedFollowsModule)
|
||||
}
|
||||
|
||||
return i
|
||||
@@ -1,84 +0,0 @@
|
||||
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
|
||||
--- a/src/view/screens/Feeds.tsx
|
||||
+++ b/src/view/screens/Feeds.tsx
|
||||
@@ -288,80 +288,7 @@ export function FeedsScreen(_props: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!hasSession || (hasSession && canShowDiscoverSection)) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsHeader',
|
||||
- type: 'popularFeedsHeader',
|
||||
- })
|
||||
|
||||
- if (popularFeedsError || searchError) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsError',
|
||||
- type: 'error',
|
||||
- error: cleanError(
|
||||
- popularFeedsError?.toString() ?? searchError?.toString() ?? '',
|
||||
- ),
|
||||
- })
|
||||
- } else {
|
||||
- if (isUserSearching) {
|
||||
- if (isSearchPending || !searchResults) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsLoading',
|
||||
- type: 'popularFeedsLoading',
|
||||
- })
|
||||
- } else {
|
||||
- if (!searchResults || searchResults?.length === 0) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsNoResults',
|
||||
- type: 'popularFeedsNoResults',
|
||||
- })
|
||||
- } else {
|
||||
- slices = slices.concat(
|
||||
- searchResults.map(feed => ({
|
||||
- key: `popularFeed:${feed.uri}`,
|
||||
- type: 'popularFeed',
|
||||
- feedUri: feed.uri,
|
||||
- feed,
|
||||
- })),
|
||||
- )
|
||||
- }
|
||||
- }
|
||||
- } else {
|
||||
- if (isPopularFeedsFetching && !popularFeeds?.pages) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsLoading',
|
||||
- type: 'popularFeedsLoading',
|
||||
- })
|
||||
- } else {
|
||||
- if (!popularFeeds?.pages) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsNoResults',
|
||||
- type: 'popularFeedsNoResults',
|
||||
- })
|
||||
- } else {
|
||||
- for (const page of popularFeeds.pages || []) {
|
||||
- slices = slices.concat(
|
||||
- page.feeds.map(feed => ({
|
||||
- key: `popularFeed:${feed.uri}`,
|
||||
- type: 'popularFeed',
|
||||
- feedUri: feed.uri,
|
||||
- feed,
|
||||
- })),
|
||||
- )
|
||||
- }
|
||||
-
|
||||
- if (isPopularFeedsFetchingNextPage) {
|
||||
- slices.push({
|
||||
- key: 'popularFeedsLoadingMore',
|
||||
- type: 'popularFeedsLoadingMore',
|
||||
- })
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
return slices
|
||||
}, [
|
||||
hasSession,
|
||||
@@ -1,361 +0,0 @@
|
||||
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
|
||||
--- a/src/Navigation.tsx
|
||||
+++ b/src/Navigation.tsx
|
||||
@@ -63,6 +63,7 @@ import {NotificationsScreen} from '#/view/screens/Notifications'
|
||||
import {PostThreadScreen} from '#/view/screens/PostThread'
|
||||
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
|
||||
import {LicenseScreen} from '#/view/screens/License'
|
||||
+import {AppInfoScreen} from '#/view/screens/AppInfo'
|
||||
import {ProfileScreen} from '#/view/screens/Profile'
|
||||
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
|
||||
import {Storybook} from '#/view/screens/Storybook'
|
||||
@@ -341,6 +342,11 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
|
||||
getComponent={() => LicenseScreen}
|
||||
options={{title: title(msg`License`)}}
|
||||
/>
|
||||
+ <Stack.Screen
|
||||
+ name="AppInfo"
|
||||
+ getComponent={() => AppInfoScreen}
|
||||
+ options={{title: title(msg`App Info`)}}
|
||||
+ />
|
||||
<Stack.Screen
|
||||
name="CommunityGuidelines"
|
||||
getComponent={() => CommunityGuidelinesScreen}
|
||||
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
|
||||
--- a/src/lib/routes/types.ts
|
||||
+++ b/src/lib/routes/types.ts
|
||||
@@ -40,6 +40,7 @@ export type CommonNavigatorParams = {
|
||||
PrivacyPolicy: undefined
|
||||
TermsOfService: undefined
|
||||
License: undefined
|
||||
+ AppInfo: undefined
|
||||
CommunityGuidelines: undefined
|
||||
CopyrightPolicy: undefined
|
||||
LanguageSettings: undefined
|
||||
diff --git a/src/routes.ts b/src/routes.ts
|
||||
--- a/src/routes.ts
|
||||
+++ b/src/routes.ts
|
||||
@@ -75,6 +75,7 @@ export const router = new Router<AllNavigatableRoutes>({
|
||||
TermsOfService: 'https://syu.is/about/support/tos',
|
||||
CommunityGuidelines: '/support/community-guidelines',
|
||||
License: 'https://syu.is/about/support/license',
|
||||
+ AppInfo: 'https://syu.is/about/support/app',
|
||||
CopyrightPolicy: '/support/copyright',
|
||||
// hashtags
|
||||
Hashtag: '/hashtag/:tag',
|
||||
diff --git a/src/view/screens/AppInfo.tsx b/src/view/screens/AppInfo.tsx
|
||||
new file mode 100644
|
||||
index 000000000..000000001
|
||||
--- /dev/null
|
||||
+++ b/src/view/screens/AppInfo.tsx
|
||||
@@ -0,0 +1,310 @@
|
||||
+import React, {useState} from 'react'
|
||||
+import {
|
||||
+ View,
|
||||
+ ScrollView,
|
||||
+ StyleSheet,
|
||||
+ Pressable,
|
||||
+ Image,
|
||||
+} from 'react-native'
|
||||
+import * as Clipboard from 'expo-clipboard'
|
||||
+import * as WebBrowser from 'expo-web-browser'
|
||||
+import {Trans} from '@lingui/macro'
|
||||
+
|
||||
+import * as Layout from '#/components/Layout'
|
||||
+import {Text} from '#/components/Typography'
|
||||
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
+import {atoms as a, useTheme} from '#/alf'
|
||||
+
|
||||
+const APP_VERSION = '1.111.0'
|
||||
+const APP_NAME = 'Aiat'
|
||||
+const BITCOIN_ADDRESS = '3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr'
|
||||
+
|
||||
+export function AppInfoScreen() {
|
||||
+ useSetTitle('App Info')
|
||||
+ const t = useTheme()
|
||||
+ const [copied, setCopied] = useState(false)
|
||||
+
|
||||
+ const copyToClipboard = async (text: string) => {
|
||||
+ try {
|
||||
+ await Clipboard.setStringAsync(text)
|
||||
+ setCopied(true)
|
||||
+ setTimeout(() => setCopied(false), 2000)
|
||||
+ } catch (e) {
|
||||
+ console.log('Clipboard not available')
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const openUrl = (url: string) => {
|
||||
+ WebBrowser.openBrowserAsync(url)
|
||||
+ }
|
||||
+
|
||||
+ return (
|
||||
+ <Layout.Screen>
|
||||
+ <Layout.Header.Outer>
|
||||
+ <Layout.Header.BackButton />
|
||||
+ <Layout.Header.Content>
|
||||
+ <Layout.Header.TitleText>
|
||||
+ <Trans>App Info</Trans>
|
||||
+ </Layout.Header.TitleText>
|
||||
+ </Layout.Header.Content>
|
||||
+ <Layout.Header.Slot />
|
||||
+ </Layout.Header.Outer>
|
||||
+ <Layout.Content>
|
||||
+ <ScrollView
|
||||
+ style={[a.flex_1]}
|
||||
+ contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
|
||||
+ {/* App Header */}
|
||||
+ <View style={styles.appHeader}>
|
||||
+ <View style={[styles.appIconContainer, t.atoms.bg_contrast_25]}>
|
||||
+ <Image
|
||||
+ source={require('../../../assets/logo.png')}
|
||||
+ style={styles.appIcon}
|
||||
+ resizeMode="cover"
|
||||
+ />
|
||||
+ </View>
|
||||
+ <Text style={[styles.appName, t.atoms.text]}>
|
||||
+ {APP_NAME}
|
||||
+ </Text>
|
||||
+ <Text style={[styles.appVersion, t.atoms.text_contrast_medium]}>
|
||||
+ v{APP_VERSION}
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+
|
||||
+ {/* Description Section */}
|
||||
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
+ <Text style={[styles.description, t.atoms.text]}>
|
||||
+ {APP_NAME} is a social networking application based on AT Protocol.
|
||||
+ Connect with your community on syu.is.
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+
|
||||
+ {/* App Information Section */}
|
||||
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||
+ App Information
|
||||
+ </Text>
|
||||
+ <View style={styles.infoGrid}>
|
||||
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
+ Version
|
||||
+ </Text>
|
||||
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||
+ {APP_VERSION}
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
+ Category
|
||||
+ </Text>
|
||||
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||
+ Social
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
+ Supported OS
|
||||
+ </Text>
|
||||
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||
+ iOS 26.0+
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
+ Price
|
||||
+ </Text>
|
||||
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||
+ Free
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+ </View>
|
||||
+ </View>
|
||||
+
|
||||
+ {/* Developer Section */}
|
||||
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||
+ Developer
|
||||
+ </Text>
|
||||
+ <View style={styles.developerCard}>
|
||||
+ <Text style={[styles.developerName, t.atoms.text]}>syui</Text>
|
||||
+ </View>
|
||||
+
|
||||
+ <Pressable
|
||||
+ onPress={() => openUrl('https://github.com/syui')}
|
||||
+ style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||
+ <Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||
+ GitHub
|
||||
+ </Text>
|
||||
+ <Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||
+ github.com/syui
|
||||
+ </Text>
|
||||
+ <Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||
+ </Pressable>
|
||||
+
|
||||
+ <Pressable
|
||||
+ onPress={() => openUrl('https://syu.is/syui')}
|
||||
+ style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||
+ <Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||
+ ATProto
|
||||
+ </Text>
|
||||
+ <Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||
+ syu.is/syui
|
||||
+ </Text>
|
||||
+ <Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||
+ </Pressable>
|
||||
+ </View>
|
||||
+
|
||||
+ {/* Bitcoin Section */}
|
||||
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||
+ Bitcoin
|
||||
+ </Text>
|
||||
+ <Pressable
|
||||
+ onPress={() => copyToClipboard(BITCOIN_ADDRESS)}
|
||||
+ style={styles.bitcoinRow}>
|
||||
+ <Text style={styles.bitcoinLabel}>₿</Text>
|
||||
+ <Text style={[styles.bitcoinAddress, t.atoms.text_contrast_medium]}>
|
||||
+ {BITCOIN_ADDRESS}
|
||||
+ </Text>
|
||||
+ <Text style={[styles.copyHint, copied && styles.copiedHint]}>
|
||||
+ {copied ? 'copied!' : 'copy'}
|
||||
+ </Text>
|
||||
+ </Pressable>
|
||||
+ </View>
|
||||
+
|
||||
+ {/* Copyright */}
|
||||
+ <View style={styles.copyright}>
|
||||
+ <Text style={[styles.copyrightText, t.atoms.text_contrast_low]}>
|
||||
+ © syui
|
||||
+ </Text>
|
||||
+ </View>
|
||||
+ </ScrollView>
|
||||
+ </Layout.Content>
|
||||
+ </Layout.Screen>
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+const styles = StyleSheet.create({
|
||||
+ appHeader: {
|
||||
+ alignItems: 'center',
|
||||
+ marginBottom: 24,
|
||||
+ },
|
||||
+ appIconContainer: {
|
||||
+ width: 80,
|
||||
+ height: 80,
|
||||
+ borderRadius: 18,
|
||||
+ overflow: 'hidden',
|
||||
+ marginBottom: 12,
|
||||
+ },
|
||||
+ appIcon: {
|
||||
+ width: '100%',
|
||||
+ height: '100%',
|
||||
+ },
|
||||
+ appName: {
|
||||
+ fontSize: 24,
|
||||
+ fontWeight: 'bold',
|
||||
+ marginBottom: 4,
|
||||
+ },
|
||||
+ appVersion: {
|
||||
+ fontSize: 14,
|
||||
+ },
|
||||
+ section: {
|
||||
+ marginBottom: 16,
|
||||
+ borderRadius: 16,
|
||||
+ padding: 16,
|
||||
+ },
|
||||
+ sectionTitle: {
|
||||
+ fontSize: 13,
|
||||
+ fontWeight: '600',
|
||||
+ textTransform: 'uppercase',
|
||||
+ letterSpacing: 0.5,
|
||||
+ marginBottom: 12,
|
||||
+ },
|
||||
+ description: {
|
||||
+ fontSize: 15,
|
||||
+ lineHeight: 22,
|
||||
+ },
|
||||
+ infoGrid: {
|
||||
+ flexDirection: 'row',
|
||||
+ flexWrap: 'wrap',
|
||||
+ gap: 8,
|
||||
+ },
|
||||
+ infoItem: {
|
||||
+ flex: 1,
|
||||
+ minWidth: '45%',
|
||||
+ alignItems: 'center',
|
||||
+ borderRadius: 12,
|
||||
+ padding: 12,
|
||||
+ },
|
||||
+ infoLabel: {
|
||||
+ fontSize: 11,
|
||||
+ textTransform: 'uppercase',
|
||||
+ letterSpacing: 0.5,
|
||||
+ marginBottom: 4,
|
||||
+ },
|
||||
+ infoValue: {
|
||||
+ fontSize: 16,
|
||||
+ fontWeight: '600',
|
||||
+ textAlign: 'center',
|
||||
+ },
|
||||
+ developerCard: {
|
||||
+ marginBottom: 12,
|
||||
+ },
|
||||
+ developerName: {
|
||||
+ fontSize: 18,
|
||||
+ fontWeight: '600',
|
||||
+ },
|
||||
+ linkRow: {
|
||||
+ flexDirection: 'row',
|
||||
+ alignItems: 'center',
|
||||
+ paddingVertical: 12,
|
||||
+ borderTopWidth: 1,
|
||||
+ },
|
||||
+ linkIcon: {
|
||||
+ fontSize: 14,
|
||||
+ fontWeight: '600',
|
||||
+ width: 70,
|
||||
+ },
|
||||
+ linkValue: {
|
||||
+ flex: 1,
|
||||
+ fontSize: 14,
|
||||
+ },
|
||||
+ linkArrow: {
|
||||
+ fontSize: 16,
|
||||
+ },
|
||||
+ bitcoinRow: {
|
||||
+ flexDirection: 'row',
|
||||
+ alignItems: 'center',
|
||||
+ backgroundColor: 'rgba(247, 147, 26, 0.08)',
|
||||
+ borderRadius: 12,
|
||||
+ padding: 14,
|
||||
+ gap: 10,
|
||||
+ },
|
||||
+ bitcoinLabel: {
|
||||
+ fontSize: 18,
|
||||
+ fontWeight: '600',
|
||||
+ color: '#f7931a',
|
||||
+ },
|
||||
+ bitcoinAddress: {
|
||||
+ flex: 1,
|
||||
+ fontSize: 11,
|
||||
+ fontFamily: 'monospace',
|
||||
+ },
|
||||
+ copyHint: {
|
||||
+ fontSize: 12,
|
||||
+ color: '#999999',
|
||||
+ minWidth: 50,
|
||||
+ textAlign: 'right',
|
||||
+ },
|
||||
+ copiedHint: {
|
||||
+ color: '#4CAF50',
|
||||
+ fontWeight: '600',
|
||||
+ },
|
||||
+ copyright: {
|
||||
+ alignItems: 'center',
|
||||
+ marginTop: 20,
|
||||
+ marginBottom: 20,
|
||||
+ },
|
||||
+ copyrightText: {
|
||||
+ fontSize: 12,
|
||||
+ },
|
||||
+})
|
||||
@@ -1,71 +0,0 @@
|
||||
diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts
|
||||
index 18bb8c8f0..bab286d7a 100644
|
||||
--- a/src/lib/api/feed/custom.ts
|
||||
+++ b/src/lib/api/feed/custom.ts
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
jsonStringToLex,
|
||||
} from '@atproto/api'
|
||||
|
||||
+import {PUBLIC_APPVIEW} from '#/lib/constants'
|
||||
import {
|
||||
getAppLanguageAsContentLanguage,
|
||||
getContentLanguages,
|
||||
@@ -12,6 +13,17 @@ import {
|
||||
import {type FeedAPI, type FeedAPIResponse} from './types'
|
||||
import {createBskyTopicsHeader, isBlueskyOwnedFeed} from './utils'
|
||||
|
||||
+// Check if the feed is hosted on syu.is network
|
||||
+function isSyuIsFeed(feedUri: string): boolean {
|
||||
+ return feedUri.includes('did:plc:6qyecktefllvenje24fcxnie') || feedUri.includes('syu.is')
|
||||
+}
|
||||
+
|
||||
+// Check if the agent is connected to syu.is
|
||||
+function isAgentOnSyuIs(agent: BskyAgent): boolean {
|
||||
+ const serviceUrl = agent.service?.toString() || ''
|
||||
+ return serviceUrl.includes('syu.is')
|
||||
+}
|
||||
+
|
||||
export class CustomFeedAPI implements FeedAPI {
|
||||
agent: BskyAgent
|
||||
params: GetCustomFeed.QueryParams
|
||||
@@ -54,8 +66,12 @@ export class CustomFeedAPI implements FeedAPI {
|
||||
const agent = this.agent
|
||||
const isBlueskyOwned = isBlueskyOwnedFeed(this.params.feed)
|
||||
|
||||
- const res = agent.did
|
||||
- ? await this.agent.app.bsky.feed.getFeed(
|
||||
+ // For syu.is feeds accessed from non-syu.is accounts, use PUBLIC_APPVIEW
|
||||
+ const needsPublicAppView = isSyuIsFeed(this.params.feed) && !isAgentOnSyuIs(agent)
|
||||
+
|
||||
+ const res = !agent.did || needsPublicAppView
|
||||
+ ? await loggedOutFetch({...this.params, cursor, limit})
|
||||
+ : await this.agent.app.bsky.feed.getFeed(
|
||||
{
|
||||
...this.params,
|
||||
cursor,
|
||||
@@ -70,7 +86,6 @@ export class CustomFeedAPI implements FeedAPI {
|
||||
},
|
||||
},
|
||||
)
|
||||
- : await loggedOutFetch({...this.params, cursor, limit})
|
||||
if (res.success) {
|
||||
// NOTE
|
||||
// some custom feeds fail to enforce the pagination limit
|
||||
@@ -120,7 +135,7 @@ async function loggedOutFetch({
|
||||
|
||||
// manually construct fetch call so we can add the `lang` cache-busting param
|
||||
let res = await fetch(
|
||||
- `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
|
||||
+ `${PUBLIC_APPVIEW}/xrpc/app.bsky.feed.getFeed?feed=${encodeURIComponent(feed)}${
|
||||
cursor ? `&cursor=${cursor}` : ''
|
||||
}&limit=${limit}&lang=${contentLangs}`,
|
||||
{
|
||||
@@ -140,7 +155,7 @@ async function loggedOutFetch({
|
||||
|
||||
// no data, try again with language headers removed
|
||||
res = await fetch(
|
||||
- `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
|
||||
+ `${PUBLIC_APPVIEW}/xrpc/app.bsky.feed.getFeed?feed=${encodeURIComponent(feed)}${
|
||||
cursor ? `&cursor=${cursor}` : ''
|
||||
}&limit=${limit}`,
|
||||
{method: 'GET', headers: {'Accept-Language': '', ...labelersHeader}},
|
||||
@@ -1,23 +0,0 @@
|
||||
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
|
||||
index 123456789..987654321 100644
|
||||
--- a/src/view/screens/Profile.tsx
|
||||
+++ b/src/view/screens/Profile.tsx
|
||||
@@ -218,13 +218,13 @@ function ProfileScreenLoaded({
|
||||
const showPostsTab = true
|
||||
const showRepliesTab = hasSession
|
||||
const showMediaTab = !hasLabeler
|
||||
- const showVideosTab = !hasLabeler
|
||||
+ const showVideosTab = false
|
||||
const showLikesTab = isMe
|
||||
const feedGenCount = profile.associated?.feedgens || 0
|
||||
- const showFeedsTab = isMe || feedGenCount > 0
|
||||
+ const showFeedsTab = feedGenCount > 0
|
||||
const starterPackCount = profile.associated?.starterPacks || 0
|
||||
- const showStarterPacksTab = isMe || starterPackCount > 0
|
||||
+ const showStarterPacksTab = false
|
||||
// subtract starterpack count from list count, since starterpacks are a type of list
|
||||
const listCount = (profile.associated?.lists || 0) - starterPackCount
|
||||
- const showListsTab = hasSession && (isMe || listCount > 0)
|
||||
+ const showListsTab = false
|
||||
|
||||
const sectionTitles = [
|
||||
@@ -1,28 +0,0 @@
|
||||
diff --git a/src/view/com/home/HomeHeader.tsx b/src/view/com/home/HomeHeader.tsx
|
||||
--- a/src/view/com/home/HomeHeader.tsx
|
||||
+++ b/src/view/com/home/HomeHeader.tsx
|
||||
@@ -3,7 +3,6 @@ import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {type FeedSourceInfo} from '#/state/queries/feed'
|
||||
-import {useSession} from '#/state/session'
|
||||
import {type RenderTabBarFnProps} from '#/view/com/pager/Pager'
|
||||
import {TabBar} from '../pager/TabBar'
|
||||
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
||||
@@ -16,17 +15,15 @@ export function HomeHeader(
|
||||
) {
|
||||
const {feeds, onSelect: onSelectProp} = props
|
||||
- const {hasSession} = useSession()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
||||
const hasPinnedCustom = React.useMemo<boolean>(() => {
|
||||
- if (!hasSession) return false
|
||||
return feeds.some(tab => {
|
||||
const isFollowing = tab.uri === 'following'
|
||||
return !isFollowing
|
||||
})
|
||||
- }, [feeds, hasSession])
|
||||
+ }, [feeds])
|
||||
|
||||
const items = React.useMemo(() => {
|
||||
const pinnedNames = feeds.map(f => f.displayName)
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
|
||||
index 63e11a7f4..70fa993cf 100644
|
||||
--- a/src/components/dialogs/nuxs/index.tsx
|
||||
+++ b/src/components/dialogs/nuxs/index.tsx
|
||||
@@ -46,7 +46,7 @@ const queuedNuxs: {
|
||||
enabled: ({currentProfile}) => {
|
||||
return (
|
||||
isNative &&
|
||||
- isExistingUserAsOf('2025-12-16T00:00:00.000Z', currentProfile.createdAt)
|
||||
+ isExistingUserAsOf('2099-12-16T00:00:00.000Z', currentProfile.createdAt)
|
||||
)
|
||||
},
|
||||
},
|
||||
@@ -1,310 +0,0 @@
|
||||
import React, {useState} from 'react'
|
||||
import {
|
||||
View,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Pressable,
|
||||
Image,
|
||||
} from 'react-native'
|
||||
import * as Clipboard from 'expo-clipboard'
|
||||
import * as WebBrowser from 'expo-web-browser'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
|
||||
const APP_VERSION = '1.111.0'
|
||||
const APP_NAME = 'Aiat'
|
||||
const BITCOIN_ADDRESS = '3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr'
|
||||
|
||||
export function AppInfoScreen() {
|
||||
useSetTitle('App Info')
|
||||
const t = useTheme()
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const copyToClipboard = async (text: string) => {
|
||||
try {
|
||||
await Clipboard.setStringAsync(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
} catch (e) {
|
||||
console.log('Clipboard not available')
|
||||
}
|
||||
}
|
||||
|
||||
const openUrl = (url: string) => {
|
||||
WebBrowser.openBrowserAsync(url)
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>App Info</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<ScrollView
|
||||
style={[a.flex_1]}
|
||||
contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
|
||||
{/* App Header */}
|
||||
<View style={styles.appHeader}>
|
||||
<View style={[styles.appIconContainer, t.atoms.bg_contrast_25]}>
|
||||
<Image
|
||||
source={require('../../../assets/logo.png')}
|
||||
style={styles.appIcon}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.appName, t.atoms.text]}>
|
||||
{APP_NAME}
|
||||
</Text>
|
||||
<Text style={[styles.appVersion, t.atoms.text_contrast_medium]}>
|
||||
v{APP_VERSION}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Description Section */}
|
||||
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[styles.description, t.atoms.text]}>
|
||||
{APP_NAME} is a social networking application based on AT Protocol.
|
||||
Connect with your community on syu.is.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* App Information Section */}
|
||||
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||
App Information
|
||||
</Text>
|
||||
<View style={styles.infoGrid}>
|
||||
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
Version
|
||||
</Text>
|
||||
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||
{APP_VERSION}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
Category
|
||||
</Text>
|
||||
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||
Social
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
Supported OS
|
||||
</Text>
|
||||
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||
iOS 26.0+
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||
Price
|
||||
</Text>
|
||||
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||
Free
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Developer Section */}
|
||||
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||
Developer
|
||||
</Text>
|
||||
<View style={styles.developerCard}>
|
||||
<Text style={[styles.developerName, t.atoms.text]}>syui</Text>
|
||||
</View>
|
||||
|
||||
<Pressable
|
||||
onPress={() => openUrl('https://git.syui.ai/syui')}
|
||||
style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||
<Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||
Git
|
||||
</Text>
|
||||
<Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||
git.syui.ai/syui
|
||||
</Text>
|
||||
<Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
onPress={() => openUrl('https://syu.is/syui')}
|
||||
style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||
<Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||
ATProto
|
||||
</Text>
|
||||
<Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||
syu.is/syui
|
||||
</Text>
|
||||
<Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* Bitcoin Section */}
|
||||
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||
Bitcoin
|
||||
</Text>
|
||||
<Pressable
|
||||
onPress={() => copyToClipboard(BITCOIN_ADDRESS)}
|
||||
style={styles.bitcoinRow}>
|
||||
<Text style={styles.bitcoinLabel}>₿</Text>
|
||||
<Text style={[styles.bitcoinAddress, t.atoms.text_contrast_medium]}>
|
||||
{BITCOIN_ADDRESS}
|
||||
</Text>
|
||||
<Text style={[styles.copyHint, copied && styles.copiedHint]}>
|
||||
{copied ? 'copied!' : 'copy'}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* Copyright */}
|
||||
<View style={styles.copyright}>
|
||||
<Text style={[styles.copyrightText, t.atoms.text_contrast_low]}>
|
||||
© syui
|
||||
</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
appHeader: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 24,
|
||||
},
|
||||
appIconContainer: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: 18,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 12,
|
||||
},
|
||||
appIcon: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
appName: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 4,
|
||||
},
|
||||
appVersion: {
|
||||
fontSize: 14,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 16,
|
||||
borderRadius: 16,
|
||||
padding: 16,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 12,
|
||||
},
|
||||
description: {
|
||||
fontSize: 15,
|
||||
lineHeight: 22,
|
||||
},
|
||||
infoGrid: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
},
|
||||
infoItem: {
|
||||
flex: 1,
|
||||
minWidth: '45%',
|
||||
alignItems: 'center',
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
},
|
||||
infoLabel: {
|
||||
fontSize: 11,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 4,
|
||||
},
|
||||
infoValue: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
developerCard: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
developerName: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
},
|
||||
linkRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 12,
|
||||
borderTopWidth: 1,
|
||||
},
|
||||
linkIcon: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
width: 70,
|
||||
},
|
||||
linkValue: {
|
||||
flex: 1,
|
||||
fontSize: 14,
|
||||
},
|
||||
linkArrow: {
|
||||
fontSize: 16,
|
||||
},
|
||||
bitcoinRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(247, 147, 26, 0.08)',
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
gap: 10,
|
||||
},
|
||||
bitcoinLabel: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
color: '#f7931a',
|
||||
},
|
||||
bitcoinAddress: {
|
||||
flex: 1,
|
||||
fontSize: 11,
|
||||
fontFamily: 'monospace',
|
||||
},
|
||||
copyHint: {
|
||||
fontSize: 12,
|
||||
color: '#999999',
|
||||
minWidth: 50,
|
||||
textAlign: 'right',
|
||||
},
|
||||
copiedHint: {
|
||||
color: '#4CAF50',
|
||||
fontWeight: '600',
|
||||
},
|
||||
copyright: {
|
||||
alignItems: 'center',
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
copyrightText: {
|
||||
fontSize: 12,
|
||||
},
|
||||
})
|
||||
@@ -1,86 +0,0 @@
|
||||
import React from 'react'
|
||||
import { ScrollView } from 'react-native'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function LicenseScreen() {
|
||||
useSetTitle('License')
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<ScrollView
|
||||
style={[a.flex_1, {backgroundColor: t.palette.white}]}
|
||||
contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
|
||||
<Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>License</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
This application is based on Bluesky Social App.
|
||||
</Text>
|
||||
|
||||
<Text style={[a.text_md, a.mb_md, {color: t.palette.primary_500}]}>
|
||||
https://github.com/bluesky-social/social-app
|
||||
</Text>
|
||||
|
||||
<Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>MIT License</Text>
|
||||
|
||||
<Text style={[a.mb_md, {fontFamily: 'monospace'}]}>
|
||||
Copyright (c) 2022-2025 Bluesky PBC
|
||||
</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</Text>
|
||||
|
||||
<Text style={[a.text_lg, a.font_bold, a.mt_xl, a.mb_md]}>日本語訳(参考)</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
本ソフトウェアおよび関連文書ファイル(以下「ソフトウェア」)のコピーを取得する
|
||||
すべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、
|
||||
ソフトウェアのコピーを使用、複製、変更、結合、公開、配布、サブライセンス、
|
||||
および/または販売する権利、ならびにソフトウェアを提供する相手にそうした行為を
|
||||
許可する権利が含まれますが、これらに限定されません。
|
||||
</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
上記の著作権表示および本許諾表示を、ソフトウェアのすべてのコピーまたは
|
||||
重要な部分に記載するものとします。
|
||||
</Text>
|
||||
|
||||
<Text style={[a.mb_md]}>
|
||||
ソフトウェアは「現状のまま」で提供され、明示黙示を問わず、商品性、特定目的への
|
||||
適合性、および権利非侵害についての保証を含む、いかなる種類の保証もなされません。
|
||||
いかなる場合においても、作者または著作権者は、契約行為、不法行為、またはそれ以外で
|
||||
あろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用または
|
||||
その他の扱いによって生じる一切の請求、損害、その他の義務について責任を負わないものとします。
|
||||
</Text>
|
||||
|
||||
<Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
||||
Original License: https://github.com/bluesky-social/social-app/blob/main/LICENSE
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
# iOS Social App Patches
|
||||
|
||||
このディレクトリには、iOS版social-appのカスタマイズパッチが含まれています。
|
||||
|
||||
## パッチファイル一覧
|
||||
|
||||
- `001-social-app-ios-config.patch` - app.config.js の設定変更(アプリ名、Bundle ID、アイコンパス、ドメイン等)
|
||||
- `002-social-app-ios-lib.patch` - lib/constants.ts, lib/statsig, lib/url-helpers の変更
|
||||
- `003-social-app-ios-view.patch` - Logo, Logotype, UserAvatar, Splash.tsx の UI 変更(Bluesky 蝶ロゴを logo.png に変更)
|
||||
- `004-social-app-ios-core.patch` - agent.ts, App.native.tsx, routes.ts のコア変更
|
||||
- `005-social-app-ios-screens.patch` - Settings, Home, Privacy, TOS 画面の変更
|
||||
- `006-social-app-ios-shell.patch` - Drawer, BottomBar, RightNav, ServerInput などシェル変更
|
||||
- `007-social-app-ios-misc.patch` - notifications, ageAssurance, PolicyUpdate などその他変更
|
||||
- `008-social-app-ios-policy-tos-error.patch` - プライバシーポリシー・利用規約をネイティブコンポーネントで表示(WebView から ScrollView + Text へ変更)
|
||||
- `009-social-app-ios-license.patch` - ライセンスページの追加(Drawer, Navigation, routes, types)
|
||||
- `010-social-app-ios-remove-contact-support.patch` - アカウント作成時の「Contact support」リンクを削除
|
||||
- `011-social-app-ios-splash-license-footer.patch` - スプラッシュ画面の「What's up?」削除、© syui フッター追加
|
||||
- `012-social-app-ios-settings-about-help.patch` - About 設定と routes.ts のリンクを内部ルートに変更(/support/tos, /support/privacy-policy, /support/license)
|
||||
- `013-social-app-ios-settings-remove-help.patch` - Settings から Help 項目を削除
|
||||
- `019-social-app-ios-entitlements-plugin.patch` - iOS entitlements プラグイン設定
|
||||
- `020-social-app-ios-bypass-age-assurance.patch` - 年齢確認を完全に無効化(access を Full に固定、chatDisabled と adultContentDisabled を false に固定)
|
||||
- `021-social-app-ios-clean-feed.patch` - Following フィードのシンプル化(DiscoverFallbackHeader の (i) アイコンと Discover リンク削除、SuggestedFollows インタースティシャル無効化、おすすめボタン削除)
|
||||
- `License.tsx` - ライセンス表示画面(新規ファイル)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### パッチの適用
|
||||
|
||||
```bash
|
||||
cd /Users/syui/ai/at/ios
|
||||
./setup.zsh patch
|
||||
```
|
||||
|
||||
**注意**: setup.zsh が自動的に以下を実行します:
|
||||
- パッチファイルの適用
|
||||
- License.tsx のコピー
|
||||
- Xcode AppIcon を logo.png から 1024x1024 にリサイズして配置(GraphicsMagick または sips を使用)
|
||||
|
||||
### リポジトリのリセット
|
||||
|
||||
```bash
|
||||
cd /Users/syui/ai/at/ios
|
||||
./setup.zsh reset
|
||||
```
|
||||
|
||||
### すべてのパッチを適用(デフォルト)
|
||||
|
||||
```bash
|
||||
cd /Users/syui/ai/at/ios
|
||||
./setup.zsh
|
||||
```
|
||||
|
||||
## パッチの更新方法
|
||||
|
||||
repos/social-app で変更を加えた後:
|
||||
|
||||
```bash
|
||||
cd /Users/syui/ai/at/repos/social-app
|
||||
git diff [ファイル名] > /Users/syui/ai/at/ios/patching/新しいパッチ.patch
|
||||
```
|
||||
|
||||
その後、`setup.zsh` の `PATCH_FILES_IOS` 配列に新しいパッチファイル名を追加してください。
|
||||
147
ios/preview.zsh
@@ -1,147 +0,0 @@
|
||||
#!/bin/zsh
|
||||
set -e
|
||||
d=${0:a:h}
|
||||
cd $d
|
||||
|
||||
source $d/.env
|
||||
|
||||
function sediment() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
#xcrun simctl uninstall booted $BUNDLE_ID
|
||||
|
||||
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..."
|
||||
ASSETS_DIR="${0:a:h}/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
|
||||
|
||||
# 1.8. Update package.json version (prevent App Store version conflict)
|
||||
echo "1.8. Updating package.json version..."
|
||||
if [ -n "$APP_VERSION" ]; then
|
||||
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..."
|
||||
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..."
|
||||
# Ensure PATH includes Homebrew ruby gems if needed
|
||||
export PATH="/opt/homebrew/lib/ruby/gems/3.4.0/bin:$PATH"
|
||||
cd ios
|
||||
pod install
|
||||
cd ..
|
||||
|
||||
# 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"
|
||||
# Add DEVELOPMENT_TEAM to all build configurations
|
||||
sediment "s/PRODUCT_BUNDLE_IDENTIFIER = /DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; PRODUCT_BUNDLE_IDENTIFIER = /g" "$PBXPROJ"
|
||||
# Also set where it might already exist but be empty
|
||||
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>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>${APP_GROUP}</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
# Add CODE_SIGN_ENTITLEMENTS to pbxproj if not present
|
||||
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"
|
||||
# echo "========================================================"
|
||||
# echo " [ACTION REQUIRED] "
|
||||
# echo " Xcode opened ($XCODE_PROJ)."
|
||||
# echo " 1. Go to 'Signing & Capabilities' tab."
|
||||
# echo " 2. Select your Team."
|
||||
# echo " 3. Verify 'App Clip' target is gone."
|
||||
# echo " 4. Ensure no red errors exist."
|
||||
# echo " Press ENTER here once you are done to continue building."
|
||||
# echo "========================================================"
|
||||
# read
|
||||
|
||||
# 5. Run
|
||||
echo "5. Building and Running..."
|
||||
# If user wants specific device ID, uncomment below, otherwise let Expo ask/pick boot simulator
|
||||
|
||||
case $1 in
|
||||
d|devicei)
|
||||
npx expo run:ios --device "$DEVICE_ID" --configuration Release
|
||||
;;
|
||||
*)
|
||||
npx expo run:ios
|
||||
;;
|
||||
esac
|
||||
295
ios/setup.zsh
@@ -1,295 +0,0 @@
|
||||
#!/bin/zsh
|
||||
|
||||
cd ${0:a:h}
|
||||
|
||||
# iOS Social App Patch Setup Script
|
||||
# Usage: ./ios/setup.zsh [patch|reset]
|
||||
|
||||
# Cross-platform sed (macOS vs Linux)
|
||||
function sediment() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Arrays for patch management
|
||||
typeset -a FAILED_PATCHES
|
||||
|
||||
# Patch file lists for iOS
|
||||
typeset -a PATCH_FILES_IOS
|
||||
PATCH_FILES_IOS=(
|
||||
"001-social-app-ios-config.patch"
|
||||
"002-social-app-ios-lib.patch"
|
||||
"003-social-app-ios-view.patch"
|
||||
"004-social-app-ios-core.patch"
|
||||
"005-social-app-ios-screens.patch"
|
||||
"006-social-app-ios-shell.patch"
|
||||
"007-social-app-ios-misc.patch"
|
||||
"009-social-app-ios-license.patch"
|
||||
"010-social-app-ios-remove-contact-support.patch"
|
||||
"011-social-app-ios-splash-license-footer.patch"
|
||||
"013-social-app-ios-settings-remove-help.patch"
|
||||
"019-social-app-ios-entitlements-plugin.patch"
|
||||
"020-social-app-ios-bypass-age-assurance.patch"
|
||||
"021-social-app-ios-clean-feed.patch"
|
||||
"022-social-app-ios-bskyweb-support-pages.patch"
|
||||
"023-social-app-ios-disable-dm.patch"
|
||||
"024-social-app-ios-disable-external-services.patch"
|
||||
"025-social-app-ios-bskyweb-title.patch"
|
||||
"027-social-app-ios-remove-birthdate.patch"
|
||||
"028-social-app-ios-remove-discover-feeds.patch"
|
||||
"029-social-app-ios-remove-feeds-discover.patch"
|
||||
"030-social-app-ios-appinfo.patch"
|
||||
"032-social-app-ios-feed-loggedout.patch"
|
||||
"033-social-app-ios-hide-profile-tabs.patch"
|
||||
"036-social-app-ios-homeheader-loggedout.patch"
|
||||
"037-social-app-ios-disable-contacts-nux.patch"
|
||||
)
|
||||
|
||||
function ios-env() {
|
||||
d=${0:a:h:h}
|
||||
patching_dir=${0:a:h}/patching
|
||||
target_dir=$d/repos/social-app
|
||||
}
|
||||
|
||||
# Common patch function with status detection
|
||||
function apply-patch() {
|
||||
local patch_name=$1
|
||||
local target_dir=$2
|
||||
local patch_file=$3
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Patch: ${patch_name}"
|
||||
echo " Target: ${target_dir}"
|
||||
echo " File: ${patch_file}"
|
||||
|
||||
pushd ${target_dir} > /dev/null
|
||||
|
||||
# Check if patch can be applied (forward dry-run succeeds)
|
||||
if patch --dry-run -p1 < ${patch_file} > /dev/null 2>&1; then
|
||||
echo "🔧 Applying patch..."
|
||||
if patch -p1 < ${patch_file}; then
|
||||
echo "✅ Applied successfully"
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 0
|
||||
else
|
||||
echo "❌ Failed to apply"
|
||||
FAILED_PATCHES+=("${patch_name} (${patch_file})")
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "⚠️ Cannot apply - file may have been modified"
|
||||
echo " Please check manually"
|
||||
FAILED_PATCHES+=("${patch_name} (${patch_file}) - file modified")
|
||||
popd > /dev/null
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function show-failed-patches() {
|
||||
if [ ${#FAILED_PATCHES[@]} -eq 0 ]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ All patches applied successfully!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "⚠️ FAILED PATCHES SUMMARY"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "The following patches could not be applied:"
|
||||
echo ""
|
||||
for failed_patch in "${FAILED_PATCHES[@]}"; do
|
||||
echo " ❌ ${failed_patch}"
|
||||
done
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Helper function for applying patches
|
||||
function patch-apply() {
|
||||
local name=$1
|
||||
local patch_file=$2
|
||||
apply-patch "${name}" "$target_dir" "$patching_dir/${patch_file}"
|
||||
}
|
||||
|
||||
# Generate build number from timestamp (YYMMDDHHMMSS)
|
||||
function ios-generate-build-number() {
|
||||
local build_number=$(date +%y%m%d%H%M%S)
|
||||
local config_patch="$patching_dir/001-social-app-ios-config.patch"
|
||||
|
||||
if [ -f "$config_patch" ]; then
|
||||
# Replace placeholder with timestamp
|
||||
sediment "s/__BUILD_NUMBER__/${build_number}/" "$config_patch"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔢 Build number: ${build_number}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore placeholder after patching (for git cleanliness)
|
||||
function ios-restore-build-placeholder() {
|
||||
local config_patch="$patching_dir/001-social-app-ios-config.patch"
|
||||
|
||||
if [ -f "$config_patch" ]; then
|
||||
# Restore placeholder for next build
|
||||
sediment "s/buildNumber: '[0-9]*'/buildNumber: '__BUILD_NUMBER__'/" "$config_patch"
|
||||
fi
|
||||
}
|
||||
|
||||
# Auto-apply patches from list
|
||||
function ios-patch-apply-all() {
|
||||
for filename in "${PATCH_FILES_IOS[@]}"; do
|
||||
local title="${filename%.*}"
|
||||
patch-apply "$title" "$filename"
|
||||
done
|
||||
}
|
||||
|
||||
# Copy new files that aren't in patches
|
||||
function ios-copy-new-files() {
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📁 Copying new files..."
|
||||
|
||||
# Copy all assets from ios/assets/ to repos/social-app/assets/
|
||||
if [ -d "$d/ios/assets" ]; then
|
||||
cp -rf "$d/ios/assets/"* "$target_dir/assets/"
|
||||
echo "✅ Copied all assets (including logo.png, app-icons)"
|
||||
fi
|
||||
|
||||
# Copy License.tsx
|
||||
if [ -f "$patching_dir/License.tsx" ]; then
|
||||
mkdir -p "$target_dir/src/view/screens"
|
||||
cp "$patching_dir/License.tsx" "$target_dir/src/view/screens/License.tsx"
|
||||
echo "✅ Copied License.tsx"
|
||||
fi
|
||||
|
||||
# Copy AppInfo.tsx
|
||||
if [ -f "$patching_dir/AppInfo.tsx" ]; then
|
||||
mkdir -p "$target_dir/src/view/screens"
|
||||
cp "$patching_dir/AppInfo.tsx" "$target_dir/src/view/screens/AppInfo.tsx"
|
||||
echo "✅ Copied AppInfo.tsx"
|
||||
fi
|
||||
|
||||
# Copy pre-generated favicons for bskyweb
|
||||
local favicon_src="$d/ios/assets/favicons"
|
||||
local bskyweb_static="$target_dir/bskyweb/static"
|
||||
if [ -d "$favicon_src" ] && [ -d "$bskyweb_static" ]; then
|
||||
cp -f "$d/ios/assets/logo.png" "$bskyweb_static/app.png"
|
||||
cp -f "$favicon_src/favicon.png" "$bskyweb_static/favicon.png"
|
||||
cp -f "$favicon_src/favicon-16x16.png" "$bskyweb_static/favicon-16x16.png"
|
||||
cp -f "$favicon_src/favicon-32x32.png" "$bskyweb_static/favicon-32x32.png"
|
||||
cp -f "$favicon_src/apple-touch-icon.png" "$bskyweb_static/apple-touch-icon.png"
|
||||
echo "✅ Copied favicons to bskyweb/static"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function ios-setup-clone() {
|
||||
if [ ! -d $target_dir ]; then
|
||||
echo "Error: social-app repository not found at $target_dir"
|
||||
echo "Please run install.zsh first to clone repositories"
|
||||
return 1
|
||||
fi
|
||||
echo "Repository found: $target_dir"
|
||||
}
|
||||
|
||||
# Generate bskyweb templates from html/ source
|
||||
# html/ is the source of truth, bskyweb templates are generated
|
||||
function ios-generate-bskyweb-templates() {
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🌐 Generating bskyweb templates from html/..."
|
||||
|
||||
local html_src="$d/html/about/support"
|
||||
local templates="$target_dir/bskyweb/templates"
|
||||
local static_src="$d/html/static"
|
||||
local static_out="$target_dir/bskyweb/static"
|
||||
|
||||
# Check if html source exists
|
||||
if [ ! -d "$html_src" ]; then
|
||||
echo "⚠️ html/about/support not found, skipping template generation"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$templates"
|
||||
mkdir -p "$static_out"
|
||||
|
||||
# Convert html/ to bskyweb templates
|
||||
# Add {{ staticCDNHost }} prefix to /static/ paths
|
||||
for html_file in privacy.html license.html tos.html help.html app.html; do
|
||||
if [ -f "$html_src/$html_file" ]; then
|
||||
local template_name="about-${html_file}"
|
||||
sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \
|
||||
"$html_src/$html_file" > "$templates/$template_name"
|
||||
fi
|
||||
done
|
||||
|
||||
# Also generate about-app.html from index.html if exists
|
||||
if [ -f "$d/html/index.html" ]; then
|
||||
sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \
|
||||
"$d/html/index.html" > "$templates/about-app.html"
|
||||
fi
|
||||
|
||||
# Copy static assets
|
||||
if [ -d "$static_src" ]; then
|
||||
cp -f "$static_src/"* "$static_out/" 2>/dev/null
|
||||
fi
|
||||
|
||||
echo "✅ Generated bskyweb templates"
|
||||
echo " - about-privacy.html, about-tos.html, etc."
|
||||
echo ""
|
||||
}
|
||||
|
||||
function ios-setup-reset() {
|
||||
echo "Resetting social-app repository..."
|
||||
cd $target_dir
|
||||
git stash
|
||||
git checkout .
|
||||
echo "Reset complete"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
ios-env
|
||||
|
||||
case "$1" in
|
||||
patch)
|
||||
ios-setup-clone
|
||||
ios-generate-build-number
|
||||
ios-patch-apply-all
|
||||
ios-restore-build-placeholder
|
||||
ios-copy-new-files
|
||||
ios-generate-bskyweb-templates
|
||||
show-failed-patches
|
||||
exit
|
||||
;;
|
||||
reset)
|
||||
ios-setup-reset
|
||||
exit
|
||||
;;
|
||||
html)
|
||||
# Generate bskyweb templates only (requires patches to be applied first)
|
||||
ios-generate-bskyweb-templates
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
ios-setup-clone
|
||||
ios-generate-build-number
|
||||
ios-patch-apply-all
|
||||
ios-restore-build-placeholder
|
||||
ios-copy-new-files
|
||||
ios-generate-bskyweb-templates
|
||||
show-failed-patches
|
||||
;;
|
||||
esac
|
||||
BIN
item/card/0.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
item/card/0.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
item/card/1.png
Normal file
|
After Width: | Height: | Size: 544 KiB |
BIN
item/card/1.webp
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
item/card/10.png
Normal file
|
After Width: | Height: | Size: 933 KiB |
BIN
item/card/10.webp
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
item/card/11.png
Normal file
|
After Width: | Height: | Size: 688 KiB |
BIN
item/card/11.webp
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
item/card/12.png
Normal file
|
After Width: | Height: | Size: 516 KiB |
BIN
item/card/12.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |