ai/at
1
0

Compare commits

..

11 Commits

Author SHA1 Message Date
c99a14b7a6 fix install.zsh 2025-12-10 15:39:33 +09:00
d4473ca84f fix 2025-12-10 14:17:47 +09:00
15ba3fa3e8 fix pds-subscriberepos-no-auth patch 2025-12-10 13:40:27 +09:00
67f6691740 fix social-app ios patch rm create account 2025-12-10 12:54:38 +09:00
d345b8f62d add html 2025-12-09 23:03:59 +09:00
2a3f9060c5 fix social-app web icon 2025-12-09 19:08:36 +09:00
dea55e649a fix social-app ios patch 2025-12-09 17:48:50 +09:00
4ba39c15ad add social-app appinfo 2025-12-09 17:04:21 +09:00
25b6b39532 fix social-app ios patch 2025-12-09 13:54:43 +09:00
5b21fc6cac fix social-app ios host server 2025-12-08 16:18:51 +09:00
ea589afe41 add ios social-app 2025-12-08 15:12:20 +09:00
31 changed files with 1649 additions and 1252 deletions

View File

@@ -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'

2
.gitignore vendored
View File

@@ -2,6 +2,6 @@ repos
.claude .claude
deploy.yml deploy.yml
claude.md claude.md
embedded.mobileprovision store.mobileprovision
.env .env
html.zip html.zip

View File

@@ -86,7 +86,6 @@ services:
depends_on: depends_on:
database: database:
condition: service_healthy condition: service_healthy
#command: ["/bigsky", "--crawl-insecure-ws"]
social-app: social-app:
ports: ports:

View File

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

View File

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

View File

@@ -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">&larr; 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">&rarr;</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">&rarr;</span>
</div>
</div>
<div class="section">
<div class="section-title">Bitcoin</div>
<div class="bitcoin-row">
<span class="bitcoin-label">&#8383;</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">&copy; 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>

View File

@@ -89,12 +89,3 @@
<li><a href="/about/support/privacy-policy">Privacy Policy</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/license">License</a></li>
<li><a href="/about/support/app">App Info</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>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -60,7 +60,7 @@
<div class="footer"> <div class="footer">
<p>Last updated: 2025</p> <p>Last updated: 2025</p>
<p>&copy; syui</p> <p>&copy; syu.is</p>
</div> </div>
</body> </body>
</html> </html>

View File

@@ -81,12 +81,3 @@
<h2>8. Changes to This Policy</h2> <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> <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>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -74,11 +74,3 @@
<p>We may update these terms from time to time. Continued use of the service after changes constitutes acceptance of the new terms.</p> <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> <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>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -72,7 +72,7 @@
<div class="info-grid"> <div class="info-grid">
<div class="info-item"> <div class="info-item">
<div class="info-label">Version</div> <div class="info-label">Version</div>
<div class="info-value">1.111.2</div> <div class="info-value">1.111.0</div>
</div> </div>
<div class="info-item"> <div class="info-item">
<div class="info-label">Category</div> <div class="info-label">Category</div>
@@ -93,28 +93,28 @@
<div class="section-title">Developer</div> <div class="section-title">Developer</div>
<div class="developer-name">syui</div> <div class="developer-name">syui</div>
<div class="link-row"> <div class="link-row">
<span class="link-icon">Git</span> <span class="link-icon">GitHub</span>
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a> <a href="https://github.com/syui" class="link-value" target="_blank">github.com/syui</a>
<span class="link-arrow">&rarr;</span> <span class="link-arrow"></span>
</div> </div>
<div class="link-row"> <div class="link-row">
<span class="link-icon">ATProto</span> <span class="link-icon">ATProto</span>
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a> <a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
<span class="link-arrow">&rarr;</span> <span class="link-arrow"></span>
</div> </div>
</div> </div>
<div class="section"> <div class="section">
<div class="section-title">Bitcoin</div> <div class="section-title">Bitcoin</div>
<div class="bitcoin-row"> <div class="bitcoin-row">
<span class="bitcoin-label">&#8383;</span> <span class="bitcoin-label"></span>
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span> <span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
<span class="copy-btn" onclick="copyBTC()">copy</span> <span class="copy-btn" onclick="copyBTC()">copy</span>
</div> </div>
</div> </div>
<div class="footer"> <div class="footer">
<p class="copyright">&copy; syui</p> <p class="copyright">© syui</p>
</div> </div>
<script> <script>

View File

@@ -52,12 +52,6 @@ function at-repos-env() {
name=${host%%.*} name=${host%%.*}
domain=${host##*.} domain=${host##*.}
dport=5000 dport=5000
typeset -A PINNED_COMMITS
PINNED_COMMITS=(
[indigo]="d49b454196351c988ceb5ce1f5e21b689487b5ab"
[atproto]="104e6ed37b0589cc000109dc76316be35b2257e1"
)
} }
# Arrays for patch management # Arrays for patch management
@@ -126,25 +120,33 @@ function at-repos-pull() {
cd $d 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() { function at-repos-social-app-ios-patch() {
$d/ios/setup.zsh $d/ios/setup.zsh
} }
function at-repos-social-app-avatar-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 sediment "s/syu.is/${host}/g"
grep -R web.syu.is .|cut -d : -f 1|sort -u|xargs sediment "s/web.syu.is/web.${host}/g"
f=$dt/lib/constants.ts
sediment "s#export const BSKY_SERVICE = 'https://bsky.social'#export const BSKY_SERVICE = 'https://${host}'#g" $f
sediment "s#export const BSKY_SERVICE_DID = 'did:web:bsky.social'#export const BSKY_SERVICE_DID = 'did:web:${host}'#g" $f
sediment "s#export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'#export const PUBLIC_BSKY_SERVICE = 'https://bsky.${host}'#g" $f
sediment "s#export const PUBLIC_APPVIEW = 'https://api.bsky.app'#export const PUBLIC_APPVIEW = 'https://bsky.${host}'#g" $f
sediment "s#export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'#export const PUBLIC_APPVIEW_DID = 'did:web:bsky.${host}'#g" $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
sediment "s#/img/avatar/plain/#https://cdn.web.syu.is/img/avatar/plain/#g" $f
sediment "s#/img/avatar_thumbnail/plain/#https://bsky.${host}/img/avatar/plain/#g" $f
sediment "s#source={{uri: avatar}}#source={{ uri: hackModifyThumbnailPath(avatar, 1 > 0), }}#g" $f
curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/lib/strings/url-helpers.ts -o $dt/lib/strings/url-helpers.ts
sediment "s#https://go.web.syu.is/redirect?u=\${encodeURIComponent(url)}#\${url}#g" $dt/lib/strings/url-helpers.ts
grep -R $did_admin .|cut -d : -f 1|sort -u|xargs sediment "s/${did_admin}/${did}/g"
}
# Common patch function with status detection # Common patch function with status detection
function apply-patch() { function apply-patch() {
local patch_name=$1 local patch_name=$1
@@ -338,9 +340,6 @@ function at-repos-build-docker-atproto() {
for ((i=1; i<=${#services}; i++)); do for ((i=1; i<=${#services}; i++)); do
service=${services[$i]} service=${services[$i]}
docker compose build --no-cache $service docker compose build --no-cache $service
if [ "$service" = "ozone" ]; then
docker compose build --no-cache ${service}-web
fi
done done
else else
docker compose build --no-cache $1 docker compose build --no-cache $1
@@ -367,13 +366,13 @@ function at-repos-push-docker() {
docker tag at-${service}:latest localhost:${dport}/${service}:latest docker tag at-${service}:latest localhost:${dport}/${service}:latest
docker push localhost:${dport}/${service}:latest docker push localhost:${dport}/${service}:latest
if [ "$service" = "ozone" ]; then if [ "$service" = "ozone" ]; then
docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest docker tag at-"${service}"-web:latest localhost:"${dport}"/"${service}"-web:latest
docker push localhost:${dport}/${service}-web:latest docker push localhost:"${dport}"/"${service}"-web:latest
fi fi
done done
else else
docker tag at-${1}:latest localhost:${dport}/${1}:latest docker tag at-"${1}":latest localhost:"${dport}"/"${1}":latest
docker push localhost:${dport}/${1}:latest docker push localhost:"${dport}"/"${1}":latest
fi fi
} }
@@ -411,7 +410,6 @@ function at-repos-reset-bgs-db() {
echo "⚙️ Updating Slurp Config..." echo "⚙️ Updating Slurp Config..."
docker exec -i $dp psql -U postgres -d bgs -c "UPDATE slurp_configs SET new_subs_disabled = false, new_pds_per_day_limit = 1000 WHERE id = 1;" docker exec -i $dp psql -U postgres -d bgs -c "UPDATE slurp_configs SET new_subs_disabled = false, new_pds_per_day_limit = 1000 WHERE id = 1;"
# host=pds:3000
echo "🔗 Registering Trusted Domain..." echo "🔗 Registering Trusted Domain..."
# Retry loop for addTrustedDomain as BGS might still be warming up # Retry loop for addTrustedDomain as BGS might still be warming up
for i in {1..5}; do for i in {1..5}; do
@@ -430,7 +428,7 @@ function at-repos-reset-bgs-db() {
result=$(curl -s -X POST "https://bgs.${host}/admin/pds/requestCrawl" \ result=$(curl -s -X POST "https://bgs.${host}/admin/pds/requestCrawl" \
-H "Authorization: Bearer ${BGS_ADMIN_KEY}" \ -H "Authorization: Bearer ${BGS_ADMIN_KEY}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"hostname\":\"{$host}\"}" \ -d "{\"hostname\":\"${host}\"}" \
-w "%{http_code}" -o /dev/null) -w "%{http_code}" -o /dev/null)
if [ "$result" = "200" ]; then if [ "$result" = "200" ]; then
echo "✅ PDS crawl requested successfully" echo "✅ PDS crawl requested successfully"
@@ -570,8 +568,8 @@ case "`cat /etc/hostname`" in
*) *)
at-repos-clone at-repos-clone
at-repos-pull at-repos-pull
at-repos-checkout-pinned
at-repos-social-app-ios-patch at-repos-social-app-ios-patch
#at-repos-social-app-avatar-write
at-repos-patch-apply-all at-repos-patch-apply-all
at-repos-ozone-patch at-repos-ozone-patch
show-failed-patches show-failed-patches

0
ios/.keep Normal file
View File

View File

@@ -5,5 +5,94 @@ https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICE
2. "Bluesky"という名称を使用しないこと。アイコンの変更。リンクの変更 2. "Bluesky"という名称を使用しないこと。アイコンの変更。リンクの変更
3. selfhostでも動くこと。これはすでにpatchで実現しています。 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を表示しない

View File

@@ -5,21 +5,13 @@ SCRIPT_DIR=${0:a:h}
cd "$SCRIPT_DIR" cd "$SCRIPT_DIR"
source .env source .env
function sediment() {
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "$@"
else
sed -i "$@"
fi
}
# 絶対パスに変換 # 絶対パスに変換
REPO_DIR="$SCRIPT_DIR/../repos/social-app" REPO_DIR="$SCRIPT_DIR/../repos/social-app"
APP_NAME="Aiat" APP_NAME="Aiat"
WORKSPACE="$REPO_DIR/ios/${APP_NAME}.xcworkspace" WORKSPACE="$REPO_DIR/ios/${APP_NAME}.xcworkspace"
SCHEME="$APP_NAME" SCHEME="$APP_NAME"
BUILD_DIR="$REPO_DIR/build" BUILD_DIR="$REPO_DIR/build"
MOBILEPROVISION="$REPO_DIR/embedded.mobileprovision" MOBILEPROVISION="$SCRIPT_DIR/store.mobileprovision"
ASSETS_DIR="$SCRIPT_DIR/assets" ASSETS_DIR="$SCRIPT_DIR/assets"
echo "Running iOS preview workflow..." echo "Running iOS preview workflow..."
@@ -49,27 +41,6 @@ else
fi fi
function cleanup_build { 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) # 2. Prebuild (Generate ios directory)
echo "2. Running Expo Prebuild..." echo "2. Running Expo Prebuild..."
# Clean old ios folder to remove old entitlements/AppClip targets # Clean old ios folder to remove old entitlements/AppClip targets
@@ -84,50 +55,16 @@ function cleanup_build {
pod install pod install
cd .. cd ..
# 4. Signing (Automated) # 4. Signing (Manual Step)
echo "4. Configuring Xcode Signing..." echo "4. Opening Xcode for Signing..."
XCODE_PROJ="ios/${APP_NAME}.xcodeproj" XCODE_PROJ="ios/${APP_NAME}.xcodeproj"
# Fallback search if variable name logic differs
if [ ! -d "$XCODE_PROJ" ]; then if [ ! -d "$XCODE_PROJ" ]; then
XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1) XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1)
fi fi
PBXPROJ="$XCODE_PROJ/project.pbxproj"
# Set DEVELOPMENT_TEAM in pbxproj open "$XCODE_PROJ"
if [ -n "$DEVELOPMENT_TEAM" ]; then read
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 case $1 in
@@ -147,7 +84,6 @@ xcodebuild -workspace "$WORKSPACE" \
-configuration Release \ -configuration Release \
-archivePath "$BUILD_DIR/${APP_NAME}.xcarchive" \ -archivePath "$BUILD_DIR/${APP_NAME}.xcarchive" \
-allowProvisioningUpdates \ -allowProvisioningUpdates \
DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
archive 2>&1 | tee "$BUILD_DIR/build.log" archive 2>&1 | tee "$BUILD_DIR/build.log"
# アーカイブ成功確認 # アーカイブ成功確認

View File

@@ -81,7 +81,8 @@ index 231447b4f..a44b3da05 100644
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot' - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot'
+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app' + 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
export const VIDEO_FEED_URI = export const VIDEO_FEED_URI =
'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids' - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids'
+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
export const STAGING_VIDEO_FEED_URI = export const STAGING_VIDEO_FEED_URI =
'at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/thevids' 'at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/thevids'
export const VIDEO_FEED_URIS = [VIDEO_FEED_URI, STAGING_VIDEO_FEED_URI] export const VIDEO_FEED_URIS = [VIDEO_FEED_URI, STAGING_VIDEO_FEED_URI]
@@ -146,7 +147,7 @@ diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
index de1e92533..3d1566800 100644 index de1e92533..3d1566800 100644
--- a/src/state/queries/feed.ts --- a/src/state/queries/feed.ts
+++ b/src/state/queries/feed.ts +++ b/src/state/queries/feed.ts
@@ -201,14 +201,6 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) { @@ -201,14 +201,7 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
// for the ones we know need it // for the ones we know need it
// -prf // -prf
export const KNOWN_AUTHED_ONLY_FEEDS = [ export const KNOWN_AUTHED_ONLY_FEEDS = [
@@ -158,6 +159,7 @@ index de1e92533..3d1566800 100644
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky - '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:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz
- 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why - 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why
+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app', // app feed, by syu.is
] ]
type GetPopularFeedsOptions = {limit?: number; enabled?: boolean} type GetPopularFeedsOptions = {limit?: number; enabled?: boolean}
@@ -214,4 +216,3 @@ index 620382175..928480da2 100644
style={[a.text_md]}> style={[a.text_md]}>
Discover Discover
</InlineLinkText>{' '} </InlineLinkText>{' '}

View File

@@ -1,5 +1,5 @@
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 6b8257b91..48ba7909e 100644 index 6b8257b91..35202224b 100644
--- a/src/screens/Settings/AboutSettings.tsx --- a/src/screens/Settings/AboutSettings.tsx
+++ b/src/screens/Settings/AboutSettings.tsx +++ b/src/screens/Settings/AboutSettings.tsx
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) { @@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
@@ -21,25 +21,26 @@ index 6b8257b91..48ba7909e 100644
<SettingsList.ItemIcon icon={NewspaperIcon} /> <SettingsList.ItemIcon icon={NewspaperIcon} />
<SettingsList.ItemText> <SettingsList.ItemText>
diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx
index 77f219e55..53f5e0cc0 100644 index dd319a4c6..0e80f956a 100644
--- a/src/screens/Takendown.tsx --- a/src/screens/Takendown.tsx
+++ b/src/screens/Takendown.tsx +++ b/src/screens/Takendown.tsx
@@ -217,10 +217,10 @@ export function Takendown() { @@ -223,11 +223,11 @@ export function Takendown() {
<Trans> <Trans>
Your account was found to be in violation of the{' '} Your account was found to be in violation of the{' '}
<SimpleInlineLinkText <InlineLinkText
- label={_(msg`Bluesky Social Terms of Service`)} - label={_(msg`Bluesky Social Terms of Service`)}
- to="https://bsky.social/about/support/tos" - to="https://bsky.social/about/support/tos"
+ label={_(msg`syu.is Terms of Service`)} + label={_(msg`syu.is Terms of Service`)}
+ to="https://syu.is/about/support/tos" + to="https://syu.is/about/support/tos"
style={[a.text_md, a.leading_normal]}> style={[a.text_md, a.leading_normal]}
overridePresentation>
- Bluesky Social Terms of Service - Bluesky Social Terms of Service
+ syu.is Terms of Service + syu.is Terms of Service
</SimpleInlineLinkText> </InlineLinkText>
. You have been sent an email outlining the specific violation . You have been sent an email outlining the specific violation
and suspension period, if applicable. You can appeal this and suspension period, if applicable. You can appeal this
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index e058e2883..8daf41089 100644 index e058e2883..0f583c915 100644
--- a/src/view/screens/Home.tsx --- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx
@@ -1,23 +1,16 @@ @@ -1,23 +1,16 @@
@@ -81,42 +82,17 @@ index e058e2883..8daf41089 100644
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState' import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState' import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed' import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
@@ -39,97 +28,90 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned' @@ -39,97 +28,60 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
import * as Layout from '#/components/Layout' import * as Layout from '#/components/Layout'
import {useDemoMode} from '#/storage/hooks/demo-mode' 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 = [{
+
+const DEFAULT_PINNED_FEEDS: any[] = [{
+ feedDescriptor: 'following', + feedDescriptor: 'following',
+ displayName: 'Following', + displayName: 'Following',
+ id: 'following', + id: 'following',
+ uri: 'following',
+ type: 'feed', + type: 'feed',
+ savedFeed: undefined, + savedFeed: undefined,
+ pinned: true, + 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'> type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
@@ -129,12 +105,7 @@ index e058e2883..8daf41089 100644
+ const {data: pinnedFeedInfos} = usePinnedFeedsInfos() + const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
+ +
+ const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any + 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 = pinnedFeedInfos || DEFAULT_PINNED_FEEDS
+ const safePinnedFeedInfos = !currentAccount
+ ? DEFAULT_PINNED_FEEDS.filter(f => f.feedDescriptor !== 'following')
+ : (pinnedFeedInfos && pinnedFeedInfos.length > 0)
+ ? pinnedFeedInfos
+ : DEFAULT_PINNED_FEEDS
React.useEffect(() => { React.useEffect(() => {
if (isWeb && !currentAccount) { if (isWeb && !currentAccount) {
@@ -218,7 +189,8 @@ index e058e2883..8daf41089 100644
- const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex] - const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex]
+ const maybeSelectedFeed = allFeeds[selectedIndex] + const maybeSelectedFeed = allFeeds[selectedIndex]
const requestNotificationsPermission = useRequestNotificationsPermission() const requestNotificationsPermission = useRequestNotificationsPermission()
-
+
useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName) useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName)
useOTAUpdates() useOTAUpdates()
- -
@@ -236,7 +208,7 @@ index e058e2883..8daf41089 100644
if (selectedIndex !== lastPagerReportedIndexRef.current) { if (selectedIndex !== lastPagerReportedIndexRef.current) {
lastPagerReportedIndexRef.current = selectedIndex lastPagerReportedIndexRef.current = selectedIndex
pagerRef.current?.setPage(selectedIndex) pagerRef.current?.setPage(selectedIndex)
@@ -138,205 +120,43 @@ function HomeScreenReady({ @@ -138,205 +90,43 @@ function HomeScreenReady({
const {hasSession} = useSession() const {hasSession} = useSession()
const setMinimalShellMode = useSetMinimalShellMode() const setMinimalShellMode = useSetMinimalShellMode()
@@ -245,8 +217,7 @@ index e058e2883..8daf41089 100644
- setMinimalShellMode(false) - setMinimalShellMode(false)
- }, [setMinimalShellMode]), - }, [setMinimalShellMode]),
- ) - )
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode])) -
- useFocusEffect( - useFocusEffect(
- useNonReactiveCallback(() => { - useNonReactiveCallback(() => {
- if (maybeSelectedFeed) { - if (maybeSelectedFeed) {
@@ -264,13 +235,7 @@ index e058e2883..8daf41089 100644
- (index: number) => { - (index: number) => {
- setMinimalShellMode(false) - setMinimalShellMode(false)
- const maybeFeed = allFeeds[index] - 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 - // Mutate the ref before setting state to avoid the imperative syncing effect
- // above from starting a loop on Android when swiping back and forth. - // above from starting a loop on Android when swiping back and forth.
- lastPagerReportedIndexRef.current = index - lastPagerReportedIndexRef.current = index
@@ -300,6 +265,15 @@ index e058e2883..8daf41089 100644
- }, - },
- [setMinimalShellMode], - [setMinimalShellMode],
- ) - )
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode]))
+
+ const onPageSelected = React.useCallback((index) => {
+ setMinimalShellMode(false)
+ const maybeFeed = allFeeds[index]
+ lastPagerReportedIndexRef.current = index
+ setSelectedFeed(maybeFeed)
+ }, [setSelectedFeed, setMinimalShellMode, allFeeds])
+
+ const onPressSelected = React.useCallback(() => { emitSoftReset() }, []) + const onPressSelected = React.useCallback(() => { emitSoftReset() }, [])
+ const onPageScrollStateChanged = React.useCallback((state) => { + const onPageScrollStateChanged = React.useCallback((state) => {
+ 'worklet' + 'worklet'
@@ -307,10 +281,7 @@ index e058e2883..8daf41089 100644
+ }, [setMinimalShellMode]) + }, [setMinimalShellMode])
const [demoMode] = useDemoMode() 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( - const renderTabBar = React.useCallback(
- (props: RenderTabBarFnProps) => { - (props: RenderTabBarFnProps) => {
- if (demoMode) { - if (demoMode) {
@@ -341,16 +312,11 @@ index e058e2883..8daf41089 100644
- const renderFollowingEmptyState = React.useCallback(() => { - const renderFollowingEmptyState = React.useCallback(() => {
- return <FollowingEmptyState /> - return <FollowingEmptyState />
- }, []) - }, [])
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, []) -
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
- const renderCustomFeedEmptyState = React.useCallback(() => { - const renderCustomFeedEmptyState = React.useCallback(() => {
- return <CustomFeedEmptyState /> - return <CustomFeedEmptyState />
- }, []) - }, [])
+ const homeFeedParams = React.useMemo(() => ({ -
+ mergeFeedEnabled: false, mergeFeedSources: []
+ }), [preferences])
- const homeFeedParams = React.useMemo<FeedParams>(() => { - const homeFeedParams = React.useMemo<FeedParams>(() => {
- return { - return {
- mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled), - mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
@@ -402,6 +368,17 @@ index e058e2883..8daf41089 100644
- renderTabBar={renderTabBar}> - renderTabBar={renderTabBar}>
- {pinnedFeedInfos.length ? ( - {pinnedFeedInfos.length ? (
- pinnedFeedInfos.map((feedInfo, index) => { - pinnedFeedInfos.map((feedInfo, index) => {
+ const renderTabBar = React.useCallback((props) => {
+ return <HomeHeader key="FEEDS_TAB_BAR" {...props} testID="homeScreenFeedTabs" onPressSelected={onPressSelected} feeds={pinnedFeedInfos} />
+ }, [onPressSelected, pinnedFeedInfos])
+
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, [])
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
+
+ const homeFeedParams = React.useMemo(() => ({
+ mergeFeedEnabled: false, mergeFeedSources: []
+ }), [preferences])
+
+ return ( + return (
+ <Pager ref={pagerRef} testID="homeScreen" initialPage={selectedIndex} onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}> + <Pager ref={pagerRef} testID="homeScreen" initialPage={selectedIndex} onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}>
+ {pinnedFeedInfos.map((feedInfo, index) => { + {pinnedFeedInfos.map((feedInfo, index) => {
@@ -470,7 +447,7 @@ index e058e2883..8daf41089 100644
-}) -})
+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 diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
index a89eaadc4..1da393f03 100644 index a89eaadc4..228af4966 100644
--- a/src/view/screens/PrivacyPolicy.tsx --- a/src/view/screens/PrivacyPolicy.tsx
+++ b/src/view/screens/PrivacyPolicy.tsx +++ b/src/view/screens/PrivacyPolicy.tsx
@@ -1,52 +1,13 @@ @@ -1,52 +1,13 @@
@@ -532,7 +509,7 @@ index a89eaadc4..1da393f03 100644
) )
} }
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
index d843c713c..b81767bd5 100644 index d843c713c..c0b34c886 100644
--- a/src/view/screens/TermsOfService.tsx --- a/src/view/screens/TermsOfService.tsx
+++ b/src/view/screens/TermsOfService.tsx +++ b/src/view/screens/TermsOfService.tsx
@@ -1,50 +1,13 @@ @@ -1,50 +1,13 @@

View File

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

View File

@@ -1,35 +1,8 @@
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 6b0e184c0..42b609c9e 100644 index 1703036b5..42baa462c 100644
--- a/src/screens/Settings/Settings.tsx --- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx +++ b/src/screens/Settings/Settings.tsx
@@ -203,24 +203,8 @@ export function SettingsScreen({}: Props) { @@ -231,16 +231,6 @@ export function SettingsScreen({}: Props) {
<Trans>Notifications</Trans>
</SettingsList.ItemText>
</SettingsList.LinkItem>
- <SettingsList.LinkItem
- to="/settings/content-and-media"
- label={_(msg`Content and media`)}>
- <SettingsList.ItemIcon icon={WindowIcon} />
- <SettingsList.ItemText>
- <Trans>Content and media</Trans>
- </SettingsList.ItemText>
- </SettingsList.LinkItem>
- {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> <Trans>Languages</Trans>
</SettingsList.ItemText> </SettingsList.ItemText>
</SettingsList.LinkItem> </SettingsList.LinkItem>

View File

@@ -1,8 +1,8 @@
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index 790f211ee..ec05a8bcd 100644 index ec5261dee..c670cf75a 100644
--- a/bskyweb/cmd/bskyweb/server.go --- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go
@@ -317,6 +317,12 @@ func serve(cctx *cli.Context) error { @@ -302,6 +302,12 @@ func serve(cctx *cli.Context) error {
e.GET("/support/tos", server.WebGeneric) e.GET("/support/tos", server.WebGeneric)
e.GET("/support/community-guidelines", server.WebGeneric) e.GET("/support/community-guidelines", server.WebGeneric)
e.GET("/support/copyright", server.WebGeneric) e.GET("/support/copyright", server.WebGeneric)
@@ -15,9 +15,9 @@ index 790f211ee..ec05a8bcd 100644
e.GET("/intent/compose", server.WebGeneric) e.GET("/intent/compose", server.WebGeneric)
e.GET("/intent/verify-email", server.WebGeneric) e.GET("/intent/verify-email", server.WebGeneric)
e.GET("/intent/age-assurance", server.WebGeneric) e.GET("/intent/age-assurance", server.WebGeneric)
@@ -825,3 +831,33 @@ func (srv *Server) serveSitemapRequest(c echo.Context, url, sitemapType string) @@ -755,3 +761,33 @@ func (srv *Server) WebIpCC(c echo.Context) error {
}
return nil return c.JSON(200, outResponse)
} }
+ +
+// Handler for About TOS page (syu.is specific) +// Handler for About TOS page (syu.is specific)
@@ -49,3 +49,510 @@ index 790f211ee..ec05a8bcd 100644
+ data := srv.NewTemplateContext() + data := srv.NewTemplateContext()
+ return c.Render(http.StatusOK, "about-app.html", data) + return c.Render(http.StatusOK, "about-app.html", data)
+} +}
diff --git a/bskyweb/templates/about-app.html b/bskyweb/templates/about-app.html
new file mode 100644
index 000000000..000000002
--- /dev/null
+++ b/bskyweb/templates/about-app.html
@@ -0,0 +1,135 @@
+<!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="{{ staticCDNHost }}/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">&larr; Back to syu.is</a>
+ </div>
+
+ <div class="app-header">
+ <img src="{{ staticCDNHost }}/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">GitHub</span>
+ <a href="https://github.com/syui" class="link-value" target="_blank">github.com/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>
diff --git a/bskyweb/templates/about-license.html b/bskyweb/templates/about-license.html
new file mode 100644
index 000000000..000000003
--- /dev/null
+++ b/bskyweb/templates/about-license.html
@@ -0,0 +1,66 @@
+<!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="{{ staticCDNHost }}/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">&larr; 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>&copy; syu.is</p>
+ </div>
+</body>
+</html>
diff --git a/bskyweb/templates/about-help.html b/bskyweb/templates/about-help.html
new file mode 100644
index 000000000..d37db25c5
--- /dev/null
+++ b/bskyweb/templates/about-help.html
@@ -0,0 +1,91 @@
+<!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="{{ staticCDNHost }}/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">&larr; 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 &gt; 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>&copy; syu.is</p>
+ </div>
+</body>
+</html>
diff --git a/bskyweb/templates/about-privacy.html b/bskyweb/templates/about-privacy.html
new file mode 100644
index 000000000..14a1168ad
--- /dev/null
+++ b/bskyweb/templates/about-privacy.html
@@ -0,0 +1,83 @@
+<!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="{{ staticCDNHost }}/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">&larr; 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>&copy; syu.is</p>
+ </div>
+</body>
+</html>
diff --git a/bskyweb/templates/about-tos.html b/bskyweb/templates/about-tos.html
new file mode 100644
index 000000000..db5d82f5c
--- /dev/null
+++ b/bskyweb/templates/about-tos.html
@@ -0,0 +1,76 @@
+<!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="{{ staticCDNHost }}/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">&larr; 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>&copy; syu.is</p>
+ </div>
+</body>
+</html>

View File

@@ -0,0 +1,15 @@
diff --git a/src/components/dialogs/ServerInput.tsx b/src/components/dialogs/ServerInput.tsx
--- a/src/components/dialogs/ServerInput.tsx
+++ b/src/components/dialogs/ServerInput.tsx
@@ -144,9 +144,9 @@
<SegmentedControl.Item
testID="bskyServiceSelectBtn"
value={BSKY_SERVICE}
- label={_(msg`Bluesky`)}>
+ label={_(msg`syu.is`)}>
<SegmentedControl.ItemText>
- {_(msg`Bluesky`)}
+ {_(msg`syu.is`)}
</SegmentedControl.ItemText>
</SegmentedControl.Item>
<SegmentedControl.Item

View File

@@ -0,0 +1,51 @@
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
@@ -46,23 +46,6 @@ export const SplashScreen = ({
<View
testID="signinOrCreateAccount"
style={[a.px_xl, a.gap_md, a.pb_2xl]}>
- <Button
- testID="createAccountButton"
- onPress={() => {
- onPressCreateAccount()
- playHaptic('Light')
- }}
- label={_(msg`Create new account`)}
- accessibilityHint={_(
- msg`Opens flow to create a new Bluesky account`,
- )}
- size="large"
- variant="solid"
- color="primary">
- <ButtonText>
- <Trans>Create account</Trans>
- </ButtonText>
- </Button>
<Button
testID="signInButton"
onPress={() => {
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
@@ -97,20 +97,6 @@ export const SplashScreen = ({
<View
testID="signinOrCreateAccount"
style={[a.w_full, a.px_xl, a.gap_md, a.pb_2xl, {maxWidth: 320}]}>
- <Button
- testID="createAccountButton"
- onPress={onPressCreateAccount}
- label={_(msg`Create new account`)}
- accessibilityHint={_(
- msg`Opens flow to create a new Bluesky account`,
- )}
- size="large"
- variant="solid"
- color="primary">
- <ButtonText>
- <Trans>Create account</Trans>
- </ButtonText>
- </Button>
<Button
testID="signInButton"
onPress={onPressSignin}

View File

@@ -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}},

View File

@@ -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 = [

View File

@@ -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)

View File

@@ -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)
)
},
},

View File

@@ -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,
},
})

View File

@@ -5,14 +5,6 @@ cd $d
source $d/.env source $d/.env
function sediment() {
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "$@"
else
sed -i "$@"
fi
}
#xcrun simctl uninstall booted $BUNDLE_ID #xcrun simctl uninstall booted $BUNDLE_ID
echo "Running iOS preview workflow..." echo "Running iOS preview workflow..."
@@ -42,26 +34,6 @@ else
echo "⚠️ Warning: $ASSETS_DIR not found" echo "⚠️ Warning: $ASSETS_DIR not found"
fi 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) # 2. Prebuild (Generate ios directory)
echo "2. Running Expo Prebuild..." echo "2. Running Expo Prebuild..."
# Clean old ios folder to remove old entitlements/AppClip targets # Clean old ios folder to remove old entitlements/AppClip targets
@@ -76,62 +48,25 @@ cd ios
pod install pod install
cd .. cd ..
# 4. Signing (Automated) # 4. Signing (Manual Step)
echo "4. Configuring Xcode Signing..." echo "4. Opening Xcode for Signing..."
XCODE_PROJ="ios/${APP_NAME}.xcodeproj" XCODE_PROJ="ios/${APP_NAME}.xcodeproj"
# Fallback search if variable name logic differs
if [ ! -d "$XCODE_PROJ" ]; then if [ ! -d "$XCODE_PROJ" ]; then
XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1) XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1)
fi fi
PBXPROJ="$XCODE_PROJ/project.pbxproj"
# Set DEVELOPMENT_TEAM in pbxproj open "$XCODE_PROJ"
if [ -n "$DEVELOPMENT_TEAM" ]; then echo "========================================================"
echo " Setting DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM" echo " [ACTION REQUIRED] "
# Add DEVELOPMENT_TEAM to all build configurations echo " Xcode opened ($XCODE_PROJ)."
sediment "s/PRODUCT_BUNDLE_IDENTIFIER = /DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; PRODUCT_BUNDLE_IDENTIFIER = /g" "$PBXPROJ" echo " 1. Go to 'Signing & Capabilities' tab."
# Also set where it might already exist but be empty echo " 2. Select your Team."
sediment "s/DEVELOPMENT_TEAM = \"\";/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ" echo " 3. Verify 'App Clip' target is gone."
sediment "s/DEVELOPMENT_TEAM = ;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ" echo " 4. Ensure no red errors exist."
fi echo " Press ENTER here once you are done to continue building."
echo "========================================================"
# Create/Update entitlements file with App Group read
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 # 5. Run
echo "5. Building and Running..." echo "5. Building and Running..."

View File

@@ -38,14 +38,12 @@ PATCH_FILES_IOS=(
"023-social-app-ios-disable-dm.patch" "023-social-app-ios-disable-dm.patch"
"024-social-app-ios-disable-external-services.patch" "024-social-app-ios-disable-external-services.patch"
"025-social-app-ios-bskyweb-title.patch" "025-social-app-ios-bskyweb-title.patch"
"026-social-app-ios-serverinput-label.patch"
"027-social-app-ios-remove-birthdate.patch" "027-social-app-ios-remove-birthdate.patch"
"028-social-app-ios-remove-discover-feeds.patch" "028-social-app-ios-remove-discover-feeds.patch"
"029-social-app-ios-remove-feeds-discover.patch" "029-social-app-ios-remove-feeds-discover.patch"
"030-social-app-ios-appinfo.patch" "030-social-app-ios-appinfo.patch"
"032-social-app-ios-feed-loggedout.patch" "031-social-app-ios-hide-create-account.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() { function ios-env() {
@@ -174,13 +172,6 @@ function ios-copy-new-files() {
echo "✅ Copied License.tsx" echo "✅ Copied License.tsx"
fi 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 # Copy pre-generated favicons for bskyweb
local favicon_src="$d/ios/assets/favicons" local favicon_src="$d/ios/assets/favicons"
local bskyweb_static="$target_dir/bskyweb/static" local bskyweb_static="$target_dir/bskyweb/static"
@@ -205,50 +196,52 @@ function ios-setup-clone() {
echo "Repository found: $target_dir" echo "Repository found: $target_dir"
} }
# Generate bskyweb templates from html/ source # Generate static HTML for at.syui.ai (CF Pages)
# html/ is the source of truth, bskyweb templates are generated function ios-generate-static-html() {
function ios-generate-bskyweb-templates() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🌐 Generating bskyweb templates from html/..." echo "🌐 Generating static HTML for at.syui.ai..."
local html_src="$d/html/about/support"
local templates="$target_dir/bskyweb/templates" local templates="$target_dir/bskyweb/templates"
local static_src="$d/html/static" local html_out="$d/html/about/support"
local static_out="$target_dir/bskyweb/static" local static_out="$d/html/static"
# Check if html source exists # Check if templates exist
if [ ! -d "$html_src" ]; then if [ ! -d "$templates" ]; then
echo "⚠️ html/about/support not found, skipping template generation" echo "⚠️ Templates not found, skipping static HTML generation"
return 1 return 1
fi fi
# Create output directory # Create output directories
mkdir -p "$templates" mkdir -p "$html_out"
mkdir -p "$static_out" mkdir -p "$static_out"
# Convert html/ to bskyweb templates # Convert templates to static HTML
# Add {{ staticCDNHost }} prefix to /static/ paths # Replace {{ staticCDNHost }} with empty string (use relative paths)
for html_file in privacy.html license.html tos.html help.html app.html; do for template in about-privacy.html about-license.html about-tos.html about-help.html; do
if [ -f "$html_src/$html_file" ]; then if [ -f "$templates/$template" ]; then
local template_name="about-${html_file}" local output_name="${template#about-}" # Remove 'about-' prefix
sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \ sed 's/{{ staticCDNHost }}//g' "$templates/$template" > "$html_out/$output_name"
"$html_src/$html_file" > "$templates/$template_name"
fi fi
done done
# Also generate about-app.html from index.html if exists # Copy static assets from ios/assets
if [ -f "$d/html/index.html" ]; then local favicon_src="$d/ios/assets/favicons"
sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \ if [ -d "$favicon_src" ]; then
"$d/html/index.html" > "$templates/about-app.html" cp -f "$favicon_src/favicon.png" "$static_out/" 2>/dev/null
fi
if [ -f "$d/ios/assets/logo.png" ]; then
cp -f "$d/ios/assets/logo.png" "$static_out/app.png"
fi fi
# Copy static assets # Use about-app.html as index.html (root page)
if [ -d "$static_src" ]; then if [ -f "$templates/about-app.html" ]; then
cp -f "$static_src/"* "$static_out/" 2>/dev/null sed 's/{{ staticCDNHost }}//g' "$templates/about-app.html" > "$d/html/index.html"
fi fi
echo "✅ Generated bskyweb templates" echo "✅ Generated static HTML in $d/html/"
echo " - about-privacy.html, about-tos.html, etc." echo " - index.html (app info)"
echo " - about/support/privacy.html"
echo " - about/support/license.html"
echo "" echo ""
} }
@@ -270,7 +263,7 @@ case "$1" in
ios-patch-apply-all ios-patch-apply-all
ios-restore-build-placeholder ios-restore-build-placeholder
ios-copy-new-files ios-copy-new-files
ios-generate-bskyweb-templates ios-generate-static-html
show-failed-patches show-failed-patches
exit exit
;; ;;
@@ -279,8 +272,8 @@ case "$1" in
exit exit
;; ;;
html) html)
# Generate bskyweb templates only (requires patches to be applied first) # Generate static HTML only (requires patches to be applied first)
ios-generate-bskyweb-templates ios-generate-static-html
exit exit
;; ;;
*) *)
@@ -289,7 +282,7 @@ case "$1" in
ios-patch-apply-all ios-patch-apply-all
ios-restore-build-placeholder ios-restore-build-placeholder
ios-copy-new-files ios-copy-new-files
ios-generate-bskyweb-templates ios-generate-static-html
show-failed-patches show-failed-patches
;; ;;
esac esac

View File

@@ -22,31 +22,14 @@ index 0000000..993c83d
+ +
+EXPOSE 3000 +EXPOSE 3000
+CMD ["yarn", "start"] +CMD ["yarn", "start"]
diff --git a/package.json b/package.json
index 1431a9e..6a7c33c 100644
--- a/package.json
+++ b/package.json
@@ -23,9 +23,11 @@
"dotenv": "^16.0.3",
"express": "^4.18.2",
"kysely": "^0.27.4",
- "multiformats": "^9.9.0"
+ "multiformats": "^9.9.0",
+ "ws": "^8.14.2"
},
"devDependencies": {
+ "@types/ws": "^8.5.10",
"@types/better-sqlite3": "^7.6.11",
"@types/express": "^4.17.17",
"@types/node": "^20.1.2",
diff --git a/scripts/publish.ts b/scripts/publish.ts diff --git a/scripts/publish.ts b/scripts/publish.ts
new file mode 100644 new file mode 100644
index 0000000..044f1d9 index 0000000..966edcf
--- /dev/null --- /dev/null
+++ b/scripts/publish.ts +++ b/scripts/publish.ts
@@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
+import dotenv from 'dotenv' +import dotenv from 'dotenv'
+import { AtpAgent, BlobRef } from '@atproto/api' +import { AtpAgent, BlobRef, AppBskyFeedDefs } from '@atproto/api'
+import fs from 'fs/promises' +import fs from 'fs/promises'
+import { ids } from '../src/lexicon/lexicons' +import { ids } from '../src/lexicon/lexicons'
+ +
@@ -105,7 +88,7 @@ index 0000000..044f1d9
+ }, + },
+ }) + })
+ +
+ console.log('All done') + console.log('All done 🎉')
+} +}
+ +
+run() +run()
@@ -169,15 +152,12 @@ index b7ee48a..102cb93 100644
export default algos export default algos
diff --git a/src/index.ts b/src/index.ts diff --git a/src/index.ts b/src/index.ts
index 7128525..40d985c 100644 index c3bd006..1e7f0b5 100644
--- a/src/index.ts --- a/src/index.ts
+++ b/src/index.ts +++ b/src/index.ts
@@ -22,8 +22,10 @@ const run = async () => { @@ -24,6 +24,8 @@ const run = async () => {
})
await server.start()
console.log( console.log(
- `🤖 running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`, `🤖 running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`,
+ `running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`,
) )
+ console.log('Supported algos:', Object.keys(require('./algos').default)) + console.log('Supported algos:', Object.keys(require('./algos').default))
+ console.log('Publisher DID:', server.cfg.publisherDid) + console.log('Publisher DID:', server.cfg.publisherDid)
@@ -185,7 +165,7 @@ index 7128525..40d985c 100644
const maybeStr = (val?: string) => { const maybeStr = (val?: string) => {
diff --git a/src/methods/feed-generation.ts b/src/methods/feed-generation.ts diff --git a/src/methods/feed-generation.ts b/src/methods/feed-generation.ts
index 0f4989e..17be062 100644 index b887413..34c5148 100644
--- a/src/methods/feed-generation.ts --- a/src/methods/feed-generation.ts
+++ b/src/methods/feed-generation.ts +++ b/src/methods/feed-generation.ts
@@ -10,7 +10,7 @@ export default function (server: Server, ctx: AppContext) { @@ -10,7 +10,7 @@ export default function (server: Server, ctx: AppContext) {
@@ -197,233 +177,18 @@ index 0f4989e..17be062 100644
feedUri.collection !== 'app.bsky.feed.generator' || feedUri.collection !== 'app.bsky.feed.generator' ||
!algo !algo
) { ) {
diff --git a/src/server.ts b/src/server.ts
index c696749..9b9c382 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -6,7 +6,7 @@ import { createServer } from './lexicon'
import feedGeneration from './methods/feed-generation'
import describeGenerator from './methods/describe-generator'
import { createDb, Database, migrateToLatest } from './db'
-import { FirehoseSubscription } from './subscription'
+import { JetstreamSubscription } from './subscription'
import { AppContext, Config } from './config'
import wellKnown from './well-known'
@@ -14,25 +14,28 @@ export class FeedGenerator {
public app: express.Application
public server?: http.Server
public db: Database
- public firehose: FirehoseSubscription
+ public jetstream: JetstreamSubscription
public cfg: Config
constructor(
app: express.Application,
db: Database,
- firehose: FirehoseSubscription,
+ jetstream: JetstreamSubscription,
cfg: Config,
) {
this.app = app
this.db = db
- this.firehose = firehose
+ this.jetstream = jetstream
this.cfg = cfg
}
static create(cfg: Config) {
const app = express()
const db = createDb(cfg.sqliteLocation)
- const firehose = new FirehoseSubscription(db, cfg.subscriptionEndpoint)
+
+ // Use Jetstream URL from env or default to internal jetstream service
+ const jetstreamUrl = process.env.FEEDGEN_JETSTREAM_URL || 'ws://jetstream:6008/subscribe'
+ const jetstream = new JetstreamSubscription(db, jetstreamUrl, cfg.subscriptionReconnectDelay)
const didCache = new MemoryCache()
const didResolver = new DidResolver({
@@ -58,12 +61,12 @@ export class FeedGenerator {
app.use(server.xrpc.router)
app.use(wellKnown(ctx))
- return new FeedGenerator(app, db, firehose, cfg)
+ return new FeedGenerator(app, db, jetstream, cfg)
}
async start(): Promise<http.Server> {
await migrateToLatest(this.db)
- this.firehose.run(this.cfg.subscriptionReconnectDelay)
+ this.jetstream.run()
this.server = this.app.listen(this.cfg.port, this.cfg.listenhost)
await events.once(this.server, 'listening')
return this.server
diff --git a/src/subscription.ts b/src/subscription.ts diff --git a/src/subscription.ts b/src/subscription.ts
index 0422a03..7785982 100644 index 0422a03..d591ef9 100644
--- a/src/subscription.ts --- a/src/subscription.ts
+++ b/src/subscription.ts +++ b/src/subscription.ts
@@ -1,49 +1,126 @@ @@ -19,10 +19,6 @@ export class FirehoseSubscription extends FirehoseSubscriptionBase {
-import {
- OutputSchema as RepoEvent,
- isCommit,
-} from './lexicon/types/com/atproto/sync/subscribeRepos'
-import { FirehoseSubscriptionBase, getOpsByType } from './util/subscription'
-
-export class FirehoseSubscription extends FirehoseSubscriptionBase {
- async handleEvent(evt: RepoEvent) {
- if (!isCommit(evt)) return
-
- const ops = await getOpsByType(evt)
-
- // This logs the text of every post off the firehose.
- // Just for fun :)
- // Delete before actually using
- for (const post of ops.posts.creates) {
- console.log(post.record.text)
+import WebSocket from 'ws'
+import { Database } from './db'
+
+// Jetstream event types
+interface JetstreamEvent {
+ did: string
+ time_us: number
+ kind: 'commit' | 'identity' | 'account'
+ commit?: {
+ rev: string
+ operation: 'create' | 'update' | 'delete'
+ collection: string
+ rkey: string
+ record?: {
+ $type: string
+ text?: string
+ createdAt?: string
+ [key: string]: unknown
}
+ cid?: string
+ }
+}
- const postsToDelete = ops.posts.deletes.map((del) => del.uri) const postsToDelete = ops.posts.deletes.map((del) => del.uri)
- const postsToCreate = ops.posts.creates const postsToCreate = ops.posts.creates
- .filter((create) => { - .filter((create) => {
- // only alf-related posts - // only alf-related posts
- return create.record.text.toLowerCase().includes('alf') - return create.record.text.toLowerCase().includes('alf')
- }) - })
- .map((create) => { .map((create) => {
- // map alf-related posts to a db row // map alf-related posts to a db row
- return { return {
- uri: create.uri,
- cid: create.cid,
- indexedAt: new Date().toISOString(),
- }
- })
+export class JetstreamSubscription {
+ private ws: WebSocket | null = null
+ private cursor: number = 0
- if (postsToDelete.length > 0) {
- await this.db
- .deleteFrom('post')
- .where('uri', 'in', postsToDelete)
- .execute()
+ constructor(
+ public db: Database,
+ public jetstreamUrl: string,
+ public reconnectDelay: number = 3000
+ ) {}
+
+ async run() {
+ await this.loadCursor()
+ this.connect()
+ }
+
+ private connect() {
+ const url = new URL(this.jetstreamUrl)
+ url.searchParams.set('wantedCollections', 'app.bsky.feed.post')
+ if (this.cursor > 0) {
+ url.searchParams.set('cursor', this.cursor.toString())
}
- if (postsToCreate.length > 0) {
+
+ console.log(`Connecting to Jetstream: ${url.toString()}`)
+ this.ws = new WebSocket(url.toString())
+
+ this.ws.on('open', () => {
+ console.log('Connected to Jetstream')
+ })
+
+ this.ws.on('message', async (data: WebSocket.Data) => {
+ try {
+ const event: JetstreamEvent = JSON.parse(data.toString())
+ await this.handleEvent(event)
+ } catch (err) {
+ console.error('Failed to handle Jetstream message:', err)
+ }
+ })
+
+ this.ws.on('error', (err) => {
+ console.error('Jetstream WebSocket error:', err)
+ })
+
+ this.ws.on('close', () => {
+ console.log('Jetstream connection closed, reconnecting...')
+ setTimeout(() => this.connect(), this.reconnectDelay)
+ })
+ }
+
+ private async handleEvent(event: JetstreamEvent) {
+ if (event.kind !== 'commit' || !event.commit) return
+ if (event.commit.collection !== 'app.bsky.feed.post') return
+
+ const uri = `at://${event.did}/${event.commit.collection}/${event.commit.rkey}`
+
+ if (event.commit.operation === 'delete') {
+ await this.db.deleteFrom('post').where('uri', '=', uri).execute()
+ } else if (event.commit.operation === 'create' && event.commit.record) {
+ const text = event.commit.record.text || ''
+
+ // Filter: posts starting with / or @ai
+ if (!text.match(/^\/[a-z]/) && !text.match(/^@ai/)) {
+ return
+ }
+
+ console.log(`[post] ${event.did}: ${text.substring(0, 50)}...`)
+
await this.db
.insertInto('post')
- .values(postsToCreate)
+ .values({
+ uri: uri,
+ cid: event.commit.cid || '',
+ indexedAt: new Date().toISOString(),
+ })
.onConflict((oc) => oc.doNothing())
.execute()
}
+
+ // Update cursor periodically
+ this.cursor = event.time_us
+ if (event.time_us % 20 === 0) {
+ await this.saveCursor()
+ }
+ }
+
+ private async loadCursor() {
+ const res = await this.db
+ .selectFrom('sub_state')
+ .selectAll()
+ .where('service', '=', 'jetstream')
+ .executeTakeFirst()
+ if (res) {
+ this.cursor = res.cursor
+ }
+ }
+
+ private async saveCursor() {
+ await this.db
+ .insertInto('sub_state')
+ .values({ service: 'jetstream', cursor: this.cursor })
+ .onConflict((oc) => oc.column('service').doUpdateSet({ cursor: this.cursor }))
+ .execute()
}
}

View File

@@ -0,0 +1,133 @@
diff --git a/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts b/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts
index c2418d343..f81789df0 100644
--- a/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts
+++ b/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts
@@ -5,68 +5,71 @@ import { httpLogger } from '../../../../logger'
import { Outbox } from '../../../../sequencer/outbox'
export default function (server: Server, ctx: AppContext) {
- server.com.atproto.sync.subscribeRepos(async function* ({ params, signal }) {
- const { cursor } = params
- const outbox = new Outbox(ctx.sequencer, {
- maxBufferSize: ctx.cfg.subscription.maxBuffer,
- })
- httpLogger.info({ cursor }, 'request to com.atproto.sync.subscribeRepos')
+ server.com.atproto.sync.subscribeRepos({
+ auth: undefined,
+ handler: async function* ({ params, signal }) {
+ const { cursor } = params
+ const outbox = new Outbox(ctx.sequencer, {
+ maxBufferSize: ctx.cfg.subscription.maxBuffer,
+ })
+ httpLogger.info({ cursor }, 'request to com.atproto.sync.subscribeRepos')
- const backfillTime = new Date(
- Date.now() - ctx.cfg.subscription.repoBackfillLimitMs,
- ).toISOString()
- let outboxCursor: number | undefined = undefined
- if (cursor !== undefined) {
- const [next, curr] = await Promise.all([
- ctx.sequencer.next(cursor),
- ctx.sequencer.curr(),
- ])
- if (cursor > (curr ?? 0)) {
- throw new InvalidRequestError('Cursor in the future.', 'FutureCursor')
- } else if (next && next.sequencedAt < backfillTime) {
- // if cursor is before backfill time, find earliest cursor from backfill window
- yield {
- $type: '#info',
- name: 'OutdatedCursor',
- message: 'Requested cursor exceeded limit. Possibly missing events',
+ const backfillTime = new Date(
+ Date.now() - ctx.cfg.subscription.repoBackfillLimitMs,
+ ).toISOString()
+ let outboxCursor: number | undefined = undefined
+ if (cursor !== undefined) {
+ const [next, curr] = await Promise.all([
+ ctx.sequencer.next(cursor),
+ ctx.sequencer.curr(),
+ ])
+ if (cursor > (curr ?? 0)) {
+ throw new InvalidRequestError('Cursor in the future.', 'FutureCursor')
+ } else if (next && next.sequencedAt < backfillTime) {
+ // if cursor is before backfill time, find earliest cursor from backfill window
+ yield {
+ $type: '#info',
+ name: 'OutdatedCursor',
+ message: 'Requested cursor exceeded limit. Possibly missing events',
+ }
+ const startEvt = await ctx.sequencer.earliestAfterTime(backfillTime)
+ outboxCursor = startEvt?.seq ? startEvt.seq - 1 : undefined
+ } else {
+ outboxCursor = cursor
}
- const startEvt = await ctx.sequencer.earliestAfterTime(backfillTime)
- outboxCursor = startEvt?.seq ? startEvt.seq - 1 : undefined
- } else {
- outboxCursor = cursor
}
- }
- for await (const evt of outbox.events(outboxCursor, signal)) {
- if (evt.type === 'commit') {
- yield {
- $type: '#commit',
- seq: evt.seq,
- time: evt.time,
- ...evt.evt,
- }
- } else if (evt.type === 'sync') {
- yield {
- $type: '#sync',
- seq: evt.seq,
- time: evt.time,
- ...evt.evt,
- }
- } else if (evt.type === 'identity') {
- yield {
- $type: '#identity',
- seq: evt.seq,
- time: evt.time,
- ...evt.evt,
- }
- } else if (evt.type === 'account') {
- yield {
- $type: '#account',
- seq: evt.seq,
- time: evt.time,
- ...evt.evt,
+ for await (const evt of outbox.events(outboxCursor, signal)) {
+ if (evt.type === 'commit') {
+ yield {
+ $type: '#commit',
+ seq: evt.seq,
+ time: evt.time,
+ ...evt.evt,
+ }
+ } else if (evt.type === 'sync') {
+ yield {
+ $type: '#sync',
+ seq: evt.seq,
+ time: evt.time,
+ ...evt.evt,
+ }
+ } else if (evt.type === 'identity') {
+ yield {
+ $type: '#identity',
+ seq: evt.seq,
+ time: evt.time,
+ ...evt.evt,
+ }
+ } else if (evt.type === 'account') {
+ yield {
+ $type: '#account',
+ seq: evt.seq,
+ time: evt.time,
+ ...evt.evt,
+ }
}
}
- }
+ },
})
}