diff --git a/.github/workflows/cf-pages.yml b/.github/workflows/cf-pages.yml new file mode 100644 index 0000000..85694ae --- /dev/null +++ b/.github/workflows/cf-pages.yml @@ -0,0 +1,30 @@ +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' diff --git a/.gitignore b/.gitignore index bd389f8..daca568 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ repos .claude deploy.yml claude.md -store.mobileprovision +embedded.mobileprovision .env +html.zip diff --git a/README.md b/README.md index 4137d1f..e11bdb2 100644 --- a/README.md +++ b/README.md @@ -85,20 +85,4 @@ $ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=${handle}&collection=a ./ios/build.zsh ``` -## social-app -- https://github.com/bluesky-social/social-app/blob/main/LICENSE - -```js -PrivacyPolicy: 'https://syu.is/about/support/privacy-policy', -TermsOfService: 'https://syu.is/about/support/tos', -License: 'https://syu.is/about/support/license', -``` - -```js -CommunityGuidelines: '/support/community-guidelines', -CopyrightPolicy: '/support/copyright', -``` - -- https://bsky.social/about/support/community-guidelines -- https://bsky.social/about/support/copyright diff --git a/compose.yml b/compose.yml index ca1a873..26c975e 100644 --- a/compose.yml +++ b/compose.yml @@ -86,6 +86,7 @@ services: depends_on: database: condition: service_healthy + #command: ["/bigsky", "--crawl-insecure-ws"] social-app: ports: diff --git a/envs/feed b/envs/feed index 60f82c3..1a2464e 100644 --- a/envs/feed +++ b/envs/feed @@ -3,5 +3,5 @@ FEEDGEN_LISTENHOST=0.0.0.0 FEEDGEN_SQLITE_LOCATION=/data/db.sqlite FEEDGEN_HOSTNAME=feed.syu.is FEEDGEN_PUBLISHER_DID=did:plc:6qyecktefllvenje24fcxnie -FEEDGEN_SUBSCRIPTION_ENDPOINT=ws://bgs:2470 FEEDGEN_SERVICE_DID=did:web:feed.syu.is +FEEDGEN_JETSTREAM_URL=ws://jetstream:6008/subscribe diff --git a/envs/jetstream b/envs/jetstream index 324bbd7..950a3c9 100644 --- a/envs/jetstream +++ b/envs/jetstream @@ -1,4 +1,4 @@ -JETSTREAM_WS_URL=wss://bgs.${host}/xrpc/com.atproto.sync.subscribeRepos +JETSTREAM_WS_URL=ws://bgs.${host}/xrpc/com.atproto.sync.subscribeRepos JETSTREAM_DATA_DIR=/data JETSTREAM_LISTEN_ADDR=:6008 JETSTREAM_METRICS_LISTEN_ADDR=:6009 diff --git a/html/about/support/app.html b/html/about/support/app.html new file mode 100644 index 0000000..241d905 --- /dev/null +++ b/html/about/support/app.html @@ -0,0 +1,135 @@ + + + + + + App Info - Aiat + + + + +
+ ← Back to syu.is +
+ +
+ Aiat +
Aiat
+
v1.111.0
+
+ +
+

Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.

+
+ +
+
App Information
+
+
+
Version
+
1.111.0
+
+
+
Category
+
Social
+
+
+
Supported OS
+
iOS 26.0+
+
+
+
Price
+
Free
+
+
+
+ +
+
Developer
+
syui
+ + +
+ +
+
Bitcoin
+
+ + 3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr + copy +
+
+ + + + + + diff --git a/html/about/support/help.html b/html/about/support/help.html new file mode 100644 index 0000000..72da246 --- /dev/null +++ b/html/about/support/help.html @@ -0,0 +1,100 @@ + + + + + + Help - syu.is + + + + +
+ ← Back to syu.is +

Help Center

+
+ +

About syu.is

+

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.

+ +

Frequently Asked Questions

+ +
+

What is the AT Protocol?

+

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.

+
+ +
+

How do I create an account?

+

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.

+
+ +
+

How do I reset my password?

+

You can reset your password through the login screen by selecting "Forgot Password" and following the instructions sent to your email.

+
+ +
+

How do I delete my account?

+

You can delete your account through Settings > Account. Please note that account deletion is permanent and cannot be undone.

+
+ +
+

How do I report abuse or inappropriate content?

+

You can report content by using the report function available on each post. Our moderation team will review reports and take appropriate action.

+
+ +

Contact

+
+

For additional support or questions:

+ +
+ +

Related Links

+ + + + + diff --git a/html/about/support/license.html b/html/about/support/license.html new file mode 100644 index 0000000..c61217a --- /dev/null +++ b/html/about/support/license.html @@ -0,0 +1,66 @@ + + + + + + License - syu.is + + + + +
+ ← Back to syu.is +

License

+
+ +

Aiat (iOS/Android App)

+

This application is based on the Bluesky Social App, which is open source software.

+ +

Open Source Licenses

+

This app uses the following open source software:

+ +

Bluesky Social App

+

Licensed under the MIT License

+

https://github.com/bluesky-social/social-app

+ +

AT Protocol

+

Licensed under the MIT License / Apache 2.0

+

https://github.com/bluesky-social/atproto

+ +

Third Party Libraries

+

This application includes various third-party libraries, each with their own licenses. For a complete list, please see the application's source code repository.

+ + + + diff --git a/html/about/support/privacy.html b/html/about/support/privacy.html new file mode 100644 index 0000000..9ab6925 --- /dev/null +++ b/html/about/support/privacy.html @@ -0,0 +1,92 @@ + + + + + + Privacy Policy - syu.is + + + + +
+ ← Back to syu.is +

Privacy Policy

+
+ +

1. Introduction

+

This Privacy Policy explains how syu.is collects, uses, and protects your personal information when you use our service.

+ +

2. Information We Collect

+

We collect the following types of information:

+ + +

3. How We Use Your Information

+

We use your information to:

+ + +

4. Data Sharing

+

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.

+ +

5. Data Security

+

We implement appropriate security measures to protect your personal information. However, no method of transmission over the Internet is 100% secure.

+ +

6. Your Rights

+

You have the right to:

+ + +

7. Cookies

+

We use cookies and similar technologies to maintain your session and improve your experience.

+ +

8. Changes to This Policy

+

We may update this Privacy Policy from time to time. We will notify you of any significant changes.

+ +

9. Contact

+

For privacy-related questions, please visit our Help page.

+ + + + diff --git a/html/about/support/tos.html b/html/about/support/tos.html new file mode 100644 index 0000000..3784cb1 --- /dev/null +++ b/html/about/support/tos.html @@ -0,0 +1,84 @@ + + + + + + Terms of Service - syu.is + + + + +
+ ← Back to syu.is +

Terms of Service

+
+ +

1. Introduction

+

Welcome to syu.is. By using our service, you agree to these terms. Please read them carefully.

+ +

2. Service Description

+

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.

+ +

3. User Responsibilities

+

As a user of syu.is, you agree to:

+ + +

4. Content Guidelines

+

Users are responsible for the content they post. Prohibited content includes:

+ + +

5. Privacy

+

Your privacy is important to us. Please review our Privacy Policy to understand how we handle your data.

+ +

6. Disclaimer

+

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.

+ +

7. Changes to Terms

+

We may update these terms from time to time. Continued use of the service after changes constitutes acceptance of the new terms.

+ +

8. Contact

+

For questions about these terms, please visit our Help page.

+ + + + diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..a9f880a --- /dev/null +++ b/html/index.html @@ -0,0 +1,135 @@ + + + + + + App Info - Aiat + + + + +
+ ← Back to syu.is +
+ +
+ Aiat +
Aiat
+
v1.111.0
+
+ +
+

Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.

+
+ +
+
App Information
+
+
+
Version
+
1.111.2
+
+
+
Category
+
Social
+
+
+
Supported OS
+
iOS 26.0+
+
+
+
Price
+
Free
+
+
+
+ +
+
Developer
+
syui
+ + +
+ +
+
Bitcoin
+
+ + 3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr + copy +
+
+ + + + + + diff --git a/html/static/app.png b/html/static/app.png new file mode 100644 index 0000000..f8f45fb Binary files /dev/null and b/html/static/app.png differ diff --git a/html/static/favicon.png b/html/static/favicon.png new file mode 100644 index 0000000..2227ba3 Binary files /dev/null and b/html/static/favicon.png differ diff --git a/install.zsh b/install.zsh index 1845aac..d5afe8e 100755 --- a/install.zsh +++ b/install.zsh @@ -52,6 +52,12 @@ function at-repos-env() { name=${host%%.*} domain=${host##*.} dport=5000 + + typeset -A PINNED_COMMITS + PINNED_COMMITS=( + [indigo]="d49b454196351c988ceb5ce1f5e21b689487b5ab" + [atproto]="104e6ed37b0589cc000109dc76316be35b2257e1" + ) } # Arrays for patch management @@ -120,31 +126,23 @@ function at-repos-pull() { cd $d } -function at-repos-social-app-ios-patch() { - $d/ios/setup.zsh +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-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" +function at-repos-social-app-ios-patch() { + $d/ios/setup.zsh } # Common patch function with status detection @@ -340,6 +338,9 @@ function at-repos-build-docker-atproto() { for ((i=1; i<=${#services}; i++)); do service=${services[$i]} docker compose build --no-cache $service + if [ "$service" = "ozone" ]; then + docker compose build --no-cache ${service}-web + fi done else docker compose build --no-cache $1 @@ -361,12 +362,11 @@ function at-repos-push-reset() { } function at-repos-push-docker() { - if [ -z "$1" ];then - for ((i=1; i<=${#services}; i++)); do - service=${services[$i]} + if [ -z "$1" ] || [ "$1" = "push" ]; then + for service in "${services[@]}"; do docker tag at-${service}:latest localhost:${dport}/${service}:latest docker push localhost:${dport}/${service}:latest - if [ "$service" == "ozone" ];then + if [ "$service" = "ozone" ]; then docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest docker push localhost:${dport}/${service}-web:latest fi @@ -411,28 +411,55 @@ function at-repos-reset-bgs-db() { 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;" - echo "🔗 Registering Trusted Domain & Resetting Repos..." + # host=pds:3000 + echo "🔗 Registering Trusted Domain..." # Retry loop for addTrustedDomain as BGS might still be warming up for i in {1..5}; do if curl -f -X POST "https://bgs.${host}/admin/pds/addTrustedDomain?domain=${host}" -H "Authorization: Bearer ${BGS_ADMIN_KEY}"; then + echo "" echo "✅ Trusted domain registered" break fi - echo "Bot failed to contact BGS (attempt $i/5)... waiting 5s" + echo "Failed to contact BGS (attempt $i/5)... waiting 5s" sleep 5 done + echo "🔗 Requesting PDS Crawl..." + # Request BGS to crawl the PDS - this registers the PDS and starts subscription + for i in {1..5}; do + result=$(curl -s -X POST "https://bgs.${host}/admin/pds/requestCrawl" \ + -H "Authorization: Bearer ${BGS_ADMIN_KEY}" \ + -H "Content-Type: application/json" \ + -d "{\"hostname\":\"{$host}\"}" \ + -w "%{http_code}" -o /dev/null) + if [ "$result" = "200" ]; then + echo "✅ PDS crawl requested successfully" + break + fi + echo "Failed to request crawl (attempt $i/5, status: $result)... waiting 5s" + sleep 5 + done + + echo "⏳ Waiting 5s for BGS to connect to PDS..." + sleep 5 + + echo "🔄 Triggering repo sync for existing users..." for ((i=1; i<=${#handles}; i++)); do handle=${handles[$i]} - did=`curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" |jq -r .did` - if [ ! -z "$did" ] && [ "$did" != "null" ]; then - echo "Resetting repo: $handle ($did)" - curl -X POST "https://bgs.${host}/admin/repo/reset?did=${did}" \ - -H "Authorization: Bearer ${BGS_ADMIN_KEY}" + did=$(curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" | jq -r .did) + if [ -n "$did" ] && [ "$did" != "null" ]; then + echo " Syncing repo: $handle ($did)" + # Use takedown=false to trigger a resync without actually taking down + curl -s -X POST "https://bgs.${host}/admin/repo/takedown?did=${did}&takedown=false" \ + -H "Authorization: Bearer ${BGS_ADMIN_KEY}" || true else - echo "Skipping reset for $handle (DID not found)" + echo " Skipping $handle (DID not found)" fi done + + echo "" + echo "✅ BGS reset complete!" + echo " PDS should now be subscribed and syncing repos." } function at-repos-feed-generator-start-push() { @@ -543,8 +570,8 @@ case "`cat /etc/hostname`" in *) at-repos-clone at-repos-pull + at-repos-checkout-pinned at-repos-social-app-ios-patch - #at-repos-social-app-avatar-write at-repos-patch-apply-all at-repos-ozone-patch show-failed-patches diff --git a/ios/.keep b/ios/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/ios/README.md b/ios/README.md index dee5ce3..c08d5da 100644 --- a/ios/README.md +++ b/ios/README.md @@ -5,94 +5,5 @@ https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICE 2. "Bluesky"という名称を使用しないこと。アイコンの変更。リンクの変更 -3. selfhostでも動くこと。本来のsocial-appは動きませんので、これは不便なのでiosアプリに出品することにしました。なお、これはすでにpatchで実現しています。 - - -```sh -$ ./install.zsh pull -$ ./install.zsh patch -$ ./ios/setup.zsh -$ ./ios/preview.zsh -``` - -## App Storeへのアップロード - -```sh -# 1. 事前にXcodeでサイニング設定を完了させる -# 2. store.mobileprovision を repos/social-app/ に配置 -# 3. キーチェーンに AC_PASSWORD を登録 - -$ cd /Users/syui/ai/at/ios -$ ./build.sh -``` - -**必要な準備:** -- Apple Distribution証明書: `Apple Distribution: syutaro inagaki (WN6KD5ZT49)` -- App Store用Provisioning Profile: `store.mobileprovision` -- App-Specific Password: キーチェーンに `AC_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を表示しない +3. selfhostでも動くこと。これはすでにpatchで実現しています。 diff --git a/ios/assets/favicons/apple-touch-icon.png b/ios/assets/favicons/apple-touch-icon.png new file mode 100644 index 0000000..143348f Binary files /dev/null and b/ios/assets/favicons/apple-touch-icon.png differ diff --git a/ios/assets/favicons/favicon-16x16.png b/ios/assets/favicons/favicon-16x16.png new file mode 100644 index 0000000..07aa7a8 Binary files /dev/null and b/ios/assets/favicons/favicon-16x16.png differ diff --git a/ios/assets/favicons/favicon-32x32.png b/ios/assets/favicons/favicon-32x32.png new file mode 100644 index 0000000..2227ba3 Binary files /dev/null and b/ios/assets/favicons/favicon-32x32.png differ diff --git a/ios/assets/favicons/favicon.png b/ios/assets/favicons/favicon.png new file mode 100644 index 0000000..2227ba3 Binary files /dev/null and b/ios/assets/favicons/favicon.png differ diff --git a/ios/build.sh b/ios/build.zsh similarity index 55% rename from ios/build.sh rename to ios/build.zsh index 59fbf46..a379cff 100755 --- a/ios/build.sh +++ b/ios/build.zsh @@ -5,13 +5,21 @@ SCRIPT_DIR=${0:a:h} cd "$SCRIPT_DIR" source .env +function sediment() { + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + # 絶対パスに変換 REPO_DIR="$SCRIPT_DIR/../repos/social-app" APP_NAME="Aiat" WORKSPACE="$REPO_DIR/ios/${APP_NAME}.xcworkspace" SCHEME="$APP_NAME" BUILD_DIR="$REPO_DIR/build" -MOBILEPROVISION="$SCRIPT_DIR/store.mobileprovision" +MOBILEPROVISION="$REPO_DIR/embedded.mobileprovision" ASSETS_DIR="$SCRIPT_DIR/assets" echo "Running iOS preview workflow..." @@ -41,6 +49,27 @@ else fi function cleanup_build { + # 1.8. Update package.json version (prevent App Store version conflict) + echo "1.8. Updating package.json version..." + if [ -n "$APP_VERSION" ]; then + # Use node to update version in package.json (already in REPO_DIR) + node -e " + const fs = require('fs'); + const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8')); + pkg.version = '$APP_VERSION'; + fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n'); + " + echo " ✅ Set version to $APP_VERSION" + else + echo " ⚠️ APP_VERSION not set in .env" + fi + + # 1.9. Update buildNumber (CFBundleVersion) with current timestamp + echo "1.9. Updating buildNumber..." + local build_number=$(date +%y%m%d%H%M%S) + sediment "s/buildNumber: '[0-9]*'/buildNumber: '${build_number}'/" "./app.config.js" + echo " ✅ Set buildNumber to $build_number" + # 2. Prebuild (Generate ios directory) echo "2. Running Expo Prebuild..." # Clean old ios folder to remove old entitlements/AppClip targets @@ -55,16 +84,50 @@ function cleanup_build { pod install cd .. - # 4. Signing (Manual Step) - echo "4. Opening Xcode for Signing..." + # 4. Signing (Automated) + echo "4. Configuring Xcode Signing..." XCODE_PROJ="ios/${APP_NAME}.xcodeproj" - # Fallback search if variable name logic differs if [ ! -d "$XCODE_PROJ" ]; then XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1) fi + PBXPROJ="$XCODE_PROJ/project.pbxproj" - open "$XCODE_PROJ" - read + # Set DEVELOPMENT_TEAM in pbxproj + if [ -n "$DEVELOPMENT_TEAM" ]; then + echo " Setting DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM" + sediment "s/PRODUCT_BUNDLE_IDENTIFIER = /DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; PRODUCT_BUNDLE_IDENTIFIER = /g" "$PBXPROJ" + sediment "s/DEVELOPMENT_TEAM = \"\";/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ" + sediment "s/DEVELOPMENT_TEAM = ;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ" + fi + + # Create/Update entitlements file with App Group + ENTITLEMENTS_FILE="ios/${APP_NAME}/${APP_NAME}.entitlements" + if [ -n "$APP_GROUP" ]; then + echo " Setting APP_GROUP=$APP_GROUP" + cat > "$ENTITLEMENTS_FILE" << EOF + + + + + aps-environment + production + com.apple.security.application-groups + + ${APP_GROUP} + + + +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 @@ -80,11 +143,12 @@ mkdir -p "$BUILD_DIR" # アーカイブ(詳細ログ出力) xcodebuild -workspace "$WORKSPACE" \ - -scheme "$SCHEME" \ - -configuration Release \ - -archivePath "$BUILD_DIR/${APP_NAME}.xcarchive" \ - -allowProvisioningUpdates \ - archive 2>&1 | tee "$BUILD_DIR/build.log" + -scheme "$SCHEME" \ + -configuration Release \ + -archivePath "$BUILD_DIR/${APP_NAME}.xcarchive" \ + -allowProvisioningUpdates \ + DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \ + archive 2>&1 | tee "$BUILD_DIR/build.log" # アーカイブ成功確認 if [ ! -d "$BUILD_DIR/${APP_NAME}.xcarchive" ]; then diff --git a/ios/patching/002-social-app-ios-lib.patch b/ios/patching/002-social-app-ios-lib.patch index cfa2931..ee04f7b 100644 --- a/ios/patching/002-social-app-ios-lib.patch +++ b/ios/patching/002-social-app-ios-lib.patch @@ -81,8 +81,7 @@ index 231447b4f..a44b3da05 100644 - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot' + 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app' export const VIDEO_FEED_URI = -- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids' -+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app' + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids' export const STAGING_VIDEO_FEED_URI = 'at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/thevids' export const VIDEO_FEED_URIS = [VIDEO_FEED_URI, STAGING_VIDEO_FEED_URI] @@ -147,7 +146,7 @@ diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts index de1e92533..3d1566800 100644 --- a/src/state/queries/feed.ts +++ b/src/state/queries/feed.ts -@@ -201,14 +201,7 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) { +@@ -201,14 +201,6 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) { // for the ones we know need it // -prf export const KNOWN_AUTHED_ONLY_FEEDS = [ @@ -159,9 +158,8 @@ index de1e92533..3d1566800 100644 - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky - 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz - 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why -+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app', // app feed, by syu.is ] - + type GetPopularFeedsOptions = {limit?: number; enabled?: boolean} diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 0cf6ab546..399e592bc 100644 @@ -216,3 +214,4 @@ index 620382175..928480da2 100644 style={[a.text_md]}> Discover {' '} + diff --git a/ios/patching/005-social-app-ios-screens.patch b/ios/patching/005-social-app-ios-screens.patch index 1853137..492e263 100644 --- a/ios/patching/005-social-app-ios-screens.patch +++ b/ios/patching/005-social-app-ios-screens.patch @@ -1,5 +1,5 @@ diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx -index 6b8257b91..35202224b 100644 +index 6b8257b91..48ba7909e 100644 --- a/src/screens/Settings/AboutSettings.tsx +++ b/src/screens/Settings/AboutSettings.tsx @@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) { @@ -21,26 +21,25 @@ index 6b8257b91..35202224b 100644 diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx -index dd319a4c6..0e80f956a 100644 +index 77f219e55..53f5e0cc0 100644 --- a/src/screens/Takendown.tsx +++ b/src/screens/Takendown.tsx -@@ -223,11 +223,11 @@ export function Takendown() { +@@ -217,10 +217,10 @@ export function Takendown() { Your account was found to be in violation of the{' '} - + style={[a.text_md, a.leading_normal]}> - Bluesky Social Terms of Service + syu.is Terms of Service - + . You have been sent an email outlining the specific violation and suspension period, if applicable. You can appeal this diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx -index e058e2883..0f583c915 100644 +index e058e2883..8daf41089 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -1,23 +1,16 @@ @@ -82,17 +81,42 @@ index e058e2883..0f583c915 100644 import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState' import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState' import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed' -@@ -39,97 +28,60 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned' +@@ -39,97 +28,90 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned' import * as Layout from '#/components/Layout' import {useDemoMode} from '#/storage/hooks/demo-mode' -+const DEFAULT_PINNED_FEEDS = [{ ++const SYU_IS_FEED_URI = 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app' ++ ++const DEFAULT_PINNED_FEEDS: any[] = [{ + feedDescriptor: 'following', + displayName: 'Following', + id: 'following', ++ uri: 'following', + type: 'feed', + savedFeed: undefined, + pinned: true, ++ route: { href: '/', name: 'Home', params: {} }, ++ cid: '', ++ avatar: '', ++ creatorDid: '', ++ creatorHandle: '', ++}, { ++ feedDescriptor: `feedgen|${SYU_IS_FEED_URI}`, ++ displayName: 'Feeds', ++ id: SYU_IS_FEED_URI, ++ uri: SYU_IS_FEED_URI, ++ type: 'feed', ++ savedFeed: { ++ type: 'feed', ++ value: SYU_IS_FEED_URI, ++ pinned: true, ++ }, ++ pinned: true, ++ route: { href: '/', name: 'Home', params: {} }, ++ cid: '', ++ avatar: '', ++ creatorDid: '', ++ creatorHandle: '', +}] + type Props = NativeStackScreenProps @@ -105,7 +129,12 @@ index e058e2883..0f583c915 100644 + const {data: pinnedFeedInfos} = usePinnedFeedsInfos() + + const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any -+ const safePinnedFeedInfos = pinnedFeedInfos || DEFAULT_PINNED_FEEDS ++ // Use user's pinned feeds when logged in and available, otherwise use defaults ++ const safePinnedFeedInfos = !currentAccount ++ ? DEFAULT_PINNED_FEEDS.filter(f => f.feedDescriptor !== 'following') ++ : (pinnedFeedInfos && pinnedFeedInfos.length > 0) ++ ? pinnedFeedInfos ++ : DEFAULT_PINNED_FEEDS React.useEffect(() => { if (isWeb && !currentAccount) { @@ -189,8 +218,7 @@ index e058e2883..0f583c915 100644 - const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex] + const maybeSelectedFeed = allFeeds[selectedIndex] const requestNotificationsPermission = useRequestNotificationsPermission() -- -+ + useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName) useOTAUpdates() - @@ -208,7 +236,7 @@ index e058e2883..0f583c915 100644 if (selectedIndex !== lastPagerReportedIndexRef.current) { lastPagerReportedIndexRef.current = selectedIndex pagerRef.current?.setPage(selectedIndex) -@@ -138,205 +90,43 @@ function HomeScreenReady({ +@@ -138,205 +120,43 @@ function HomeScreenReady({ const {hasSession} = useSession() const setMinimalShellMode = useSetMinimalShellMode() @@ -217,7 +245,8 @@ index e058e2883..0f583c915 100644 - setMinimalShellMode(false) - }, [setMinimalShellMode]), - ) -- ++ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode])) + - useFocusEffect( - useNonReactiveCallback(() => { - if (maybeSelectedFeed) { @@ -235,7 +264,13 @@ index e058e2883..0f583c915 100644 - (index: number) => { - setMinimalShellMode(false) - const maybeFeed = allFeeds[index] -- ++ const onPageSelected = React.useCallback((index) => { ++ setMinimalShellMode(false) ++ const maybeFeed = allFeeds[index] ++ lastPagerReportedIndexRef.current = index ++ setSelectedFeed(maybeFeed) ++ }, [setSelectedFeed, setMinimalShellMode, allFeeds]) + - // Mutate the ref before setting state to avoid the imperative syncing effect - // above from starting a loop on Android when swiping back and forth. - lastPagerReportedIndexRef.current = index @@ -265,15 +300,6 @@ index e058e2883..0f583c915 100644 - }, - [setMinimalShellMode], - ) -+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode])) -+ -+ const onPageSelected = React.useCallback((index) => { -+ setMinimalShellMode(false) -+ const maybeFeed = allFeeds[index] -+ lastPagerReportedIndexRef.current = index -+ setSelectedFeed(maybeFeed) -+ }, [setSelectedFeed, setMinimalShellMode, allFeeds]) -+ + const onPressSelected = React.useCallback(() => { emitSoftReset() }, []) + const onPageScrollStateChanged = React.useCallback((state) => { + 'worklet' @@ -281,7 +307,10 @@ index e058e2883..0f583c915 100644 + }, [setMinimalShellMode]) const [demoMode] = useDemoMode() -- ++ const renderTabBar = React.useCallback((props) => { ++ return ++ }, [onPressSelected, pinnedFeedInfos]) + - const renderTabBar = React.useCallback( - (props: RenderTabBarFnProps) => { - if (demoMode) { @@ -312,11 +341,16 @@ index e058e2883..0f583c915 100644 - const renderFollowingEmptyState = React.useCallback(() => { - return - }, []) -- ++ const renderFollowingEmptyState = React.useCallback(() => , []) ++ const renderCustomFeedEmptyState = React.useCallback(() => , []) + - const renderCustomFeedEmptyState = React.useCallback(() => { - return - }, []) -- ++ const homeFeedParams = React.useMemo(() => ({ ++ mergeFeedEnabled: false, mergeFeedSources: [] ++ }), [preferences]) + - const homeFeedParams = React.useMemo(() => { - return { - mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled), @@ -368,17 +402,6 @@ index e058e2883..0f583c915 100644 - renderTabBar={renderTabBar}> - {pinnedFeedInfos.length ? ( - pinnedFeedInfos.map((feedInfo, index) => { -+ const renderTabBar = React.useCallback((props) => { -+ return -+ }, [onPressSelected, pinnedFeedInfos]) -+ -+ const renderFollowingEmptyState = React.useCallback(() => , []) -+ const renderCustomFeedEmptyState = React.useCallback(() => , []) -+ -+ const homeFeedParams = React.useMemo(() => ({ -+ mergeFeedEnabled: false, mergeFeedSources: [] -+ }), [preferences]) -+ + return ( + + {pinnedFeedInfos.map((feedInfo, index) => { @@ -447,7 +470,7 @@ index e058e2883..0f583c915 100644 -}) +const styles = StyleSheet.create({ loading: { height: '100%', alignContent: 'center', justifyContent: 'center', paddingBottom: 100 } }) diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx -index a89eaadc4..228af4966 100644 +index a89eaadc4..1da393f03 100644 --- a/src/view/screens/PrivacyPolicy.tsx +++ b/src/view/screens/PrivacyPolicy.tsx @@ -1,52 +1,13 @@ @@ -509,7 +532,7 @@ index a89eaadc4..228af4966 100644 ) } diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx -index d843c713c..c0b34c886 100644 +index d843c713c..b81767bd5 100644 --- a/src/view/screens/TermsOfService.tsx +++ b/src/view/screens/TermsOfService.tsx @@ -1,50 +1,13 @@ diff --git a/ios/patching/006-social-app-ios-shell.patch b/ios/patching/006-social-app-ios-shell.patch index d2154d2..399df6f 100644 --- a/ios/patching/006-social-app-ios-shell.patch +++ b/ios/patching/006-social-app-ios-shell.patch @@ -1,311 +1,8 @@ -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({ - - You must be at least 13 years old to use Bluesky. Read our{' '} - - Terms of Service - {' '} -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({ - - 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. - - - diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx -index ed2a6cfb7..1dc7f9227 100644 +index f76147ccf..36b4d7de1 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx -@@ -1,60 +1,50 @@ --import React, {type ComponentProps, type JSX} from 'react' --import {Linking, ScrollView, TouchableOpacity, View} from 'react-native' --import {useSafeAreaInsets} from 'react-native-safe-area-context' --import {msg, Plural, plural, Trans} from '@lingui/macro' --import {useLingui} from '@lingui/react' --import {StackActions, useNavigation} from '@react-navigation/native' -- --import {useActorStatus} from '#/lib/actor-status' --import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants' --import {type PressableScale} from '#/lib/custom-animations/PressableScale' --import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState' --import {getTabState, TabState} from '#/lib/routes/helpers' --import {type NavigationProp} from '#/lib/routes/types' --import {sanitizeHandle} from '#/lib/strings/handles' --import {colors} from '#/lib/styles' --import {isWeb} from '#/platform/detection' --import {emitSoftReset} from '#/state/events' --import {useKawaiiMode} from '#/state/preferences/kawaii' --import {useUnreadNotifications} from '#/state/queries/notifications/unread' --import {useProfileQuery} from '#/state/queries/profile' --import {type SessionAccount, useSession} from '#/state/session' --import {useSetDrawerOpen} from '#/state/shell' --import {formatCount} from '#/view/com/util/numeric/format' --import {UserAvatar} from '#/view/com/util/UserAvatar' --import {NavSignupCard} from '#/view/shell/NavSignupCard' --import {atoms as a, tokens, useTheme, web} from '#/alf' --import {Button, ButtonIcon, ButtonText} from '#/components/Button' --import {Divider} from '#/components/Divider' -+import React, { type ComponentProps, type JSX } from 'react' -+import { Linking, ScrollView, TouchableOpacity, View } from 'react-native' -+import { useSafeAreaInsets } from 'react-native-safe-area-context' -+import { msg, Plural, plural, Trans } from '@lingui/macro' -+import { useLingui } from '@lingui/react' -+import { StackActions, useNavigation } from '@react-navigation/native' -+ -+import { useActorStatus } from '#/lib/actor-status' -+import { FEEDBACK_FORM_URL, HELP_DESK_URL } from '#/lib/constants' -+import { type PressableScale } from '#/lib/custom-animations/PressableScale' -+import { useNavigationTabState } from '#/lib/hooks/useNavigationTabState' -+import { getTabState, TabState } from '#/lib/routes/helpers' -+import { type NavigationProp } from '#/lib/routes/types' -+import { sanitizeHandle } from '#/lib/strings/handles' -+import { colors } from '#/lib/styles' -+import { isWeb } from '#/platform/detection' -+import { emitSoftReset } from '#/state/events' -+import { useKawaiiMode } from '#/state/preferences/kawaii' -+import { useUnreadNotifications } from '#/state/queries/notifications/unread' -+import { useProfileQuery } from '#/state/queries/profile' -+import { type SessionAccount, useSession } from '#/state/session' -+import { useSetDrawerOpen } from '#/state/shell' -+import { formatCount } from '#/view/com/util/numeric/format' -+import { UserAvatar } from '#/view/com/util/UserAvatar' -+import { NavSignupCard } from '#/view/shell/NavSignupCard' -+import { atoms as a, tokens, useTheme, web } from '#/alf' -+import { Button } from '#/components/Button' -+import { Divider } from '#/components/Divider' - import { - Bell_Filled_Corner0_Rounded as BellFilled, - Bell_Stroke2_Corner0_Rounded as Bell, - } from '#/components/icons/Bell' --import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' --import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList' --import { -- Hashtag_Filled_Corner0_Rounded as HashtagFilled, -- Hashtag_Stroke2_Corner0_Rounded as Hashtag, --} from '#/components/icons/Hashtag' - import { - HomeOpen_Filled_Corner0_Rounded as HomeFilled, - HomeOpen_Stoke2_Corner0_Rounded as Home, - } from '#/components/icons/HomeOpen' --import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass' --import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2' --import { -- Message_Stroke2_Corner0_Rounded as Message, -- Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, --} from '#/components/icons/Message' --import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2' -+import { MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled } from '#/components/icons/MagnifyingGlass' -+import { MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass } from '#/components/icons/MagnifyingGlass2' -+import { SettingsGear2_Stroke2_Corner0_Rounded as Settings } from '#/components/icons/SettingsGear2' - import { - UserCircle_Filled_Corner0_Rounded as UserCircleFilled, - UserCircle_Stroke2_Corner0_Rounded as UserCircle, - } from '#/components/icons/UserCircle' --import {InlineLinkText} from '#/components/Link' --import {Text} from '#/components/Typography' --import {useSimpleVerificationState} from '#/components/verification' --import {VerificationCheck} from '#/components/verification/VerificationCheck' -+import { InlineLinkText } from '#/components/Link' -+import { Text } from '#/components/Typography' -+import { useSimpleVerificationState } from '#/components/verification' -+import { VerificationCheck } from '#/components/verification/VerificationCheck' - - const iconWidth = 26 - -@@ -65,11 +55,11 @@ let DrawerProfileCard = ({ - account: SessionAccount - onPressProfile: () => void - }): React.ReactNode => { -- const {_, i18n} = useLingui() -+ const { _, i18n } = useLingui() - const t = useTheme() -- const {data: profile} = useProfileQuery({did: account.did}) -- const verification = useSimpleVerificationState({profile}) -- const {isActive: live} = useActorStatus(profile) -+ const { data: profile } = useProfileQuery({ did: account.did }) -+ const verification = useSimpleVerificationState({ profile }) -+ const { isActive: live } = useActorStatus(profile) - - return ( - ): 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 ( - - -+ contentContainerStyle={[{ paddingTop: Math.max(insets.top + a.pt_xl.paddingTop, a.pt_xl.paddingTop) }]}> - - {hasSession && currentAccount ? ( - ): React.ReactNode => { - - - )} -- - - - -@@ -292,17 +208,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { +@@ -292,17 +292,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { <> @@ -314,7 +11,7 @@ index ed2a6cfb7..1dc7f9227 100644 isActive={isAtNotifications} onPress={onPressNotifications} /> -- + - - ): React.ReactNode => { - ) : ( - <> - -- - - - )} -@@ -322,69 +230,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { - - - -- -- - - ) - } - DrawerContent = React.memo(DrawerContent) --export {DrawerContent} -- --let DrawerFooter = ({ -- onPressFeedback, -- onPressHelp, --}: { -- onPressFeedback: () => void -- onPressHelp: () => void --}): React.ReactNode => { -- const {_} = useLingui() -- const insets = useSafeAreaInsets() -- return ( -- +@@ -357,17 +351,7 @@ let DrawerFooter = ({ + ), + }, + ]}> - -- -- -- ) --} --DrawerFooter = React.memo(DrawerFooter) -+export { DrawerContent } - - interface MenuItemProps extends ComponentProps { - icon: JSX.Element -@@ -400,7 +250,7 @@ let SearchMenuItem = ({ - isActive: boolean - onPress: () => void - }): React.ReactNode => { -- const {_} = useLingui() -+ const { _ } = useLingui() - const t = useTheme() - return ( - void - }): React.ReactNode => { -- const {_} = useLingui() -+ const { _ } = useLingui() - const t = useTheme() - return ( - void --}): React.ReactNode => { -- const {_} = useLingui() -- const t = useTheme() -- return ( -- -- ) : ( -- -- ) -- } -- 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 ( -- -- ) : ( -- -- ) -- } -- label={_(msg`Feeds`)} -- bold={isActive} -- onPress={onPress} -- /> -- ) --} --FeedsMenuItem = React.memo(FeedsMenuItem) -- --let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { -- const {_} = useLingui() -- const t = useTheme() -- -- return ( -- } -- 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 ( -- -- ) : ( -- -- ) -- } -- 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 ( - void}): React.ReactNode => { -- const {_} = useLingui() -+let SettingsMenuItem = ({ onPress }: { onPress: () => void }): React.ReactNode => { -+ const { _ } = useLingui() - const t = useTheme() - return ( - 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 ( -