From 23df4df6f969e41f5ea642087a257c49bab17e6c Mon Sep 17 00:00:00 2001 From: syui Date: Tue, 9 Dec 2025 18:36:16 +0900 Subject: [PATCH] fix social-app web icon --- .github/workflows/cf-pages.yml | 30 + .gitignore | 3 +- README.md | 16 - compose.yml | 1 + envs/feed | 2 +- envs/jetstream | 2 +- html/about/support/app.html | 135 ++++ html/about/support/help.html | 100 +++ html/about/support/license.html | 66 ++ html/about/support/privacy.html | 92 +++ html/about/support/tos.html | 84 +++ html/index.html | 135 ++++ html/static/app.png | Bin 0 -> 29204 bytes html/static/favicon.png | Bin 0 -> 1005 bytes install.zsh | 99 ++- ios/.keep | 0 ios/README.md | 91 +-- ios/assets/favicons/apple-touch-icon.png | Bin 0 -> 6032 bytes ios/assets/favicons/favicon-16x16.png | Bin 0 -> 574 bytes ios/assets/favicons/favicon-32x32.png | Bin 0 -> 1005 bytes ios/assets/favicons/favicon.png | Bin 0 -> 1005 bytes ios/{build.sh => build.zsh} | 86 ++- ios/patching/002-social-app-ios-lib.patch | 9 +- ios/patching/005-social-app-ios-screens.patch | 105 ++- ios/patching/006-social-app-ios-shell.patch | 709 +----------------- ios/patching/007-social-app-ios-misc.patch | 110 +-- ...-social-app-ios-settings-remove-help.patch | 31 +- ...social-app-ios-bskyweb-support-pages.patch | 517 +------------ ...026-social-app-ios-serverinput-label.patch | 15 - .../032-social-app-ios-feed-loggedout.patch | 71 ++ ...033-social-app-ios-hide-profile-tabs.patch | 23 + ...-social-app-ios-homeheader-loggedout.patch | 28 + ...-social-app-ios-disable-contacts-nux.patch | 13 + ios/patching/AppInfo.tsx | 310 ++++++++ ios/preview.zsh | 93 ++- ios/setup.zsh | 86 ++- patching/200-feed-generator-custom.patch | 263 ++++++- task.md | 4 - 38 files changed, 1764 insertions(+), 1565 deletions(-) create mode 100644 .github/workflows/cf-pages.yml create mode 100644 html/about/support/app.html create mode 100644 html/about/support/help.html create mode 100644 html/about/support/license.html create mode 100644 html/about/support/privacy.html create mode 100644 html/about/support/tos.html create mode 100644 html/index.html create mode 100644 html/static/app.png create mode 100644 html/static/favicon.png delete mode 100644 ios/.keep create mode 100644 ios/assets/favicons/apple-touch-icon.png create mode 100644 ios/assets/favicons/favicon-16x16.png create mode 100644 ios/assets/favicons/favicon-32x32.png create mode 100644 ios/assets/favicons/favicon.png rename ios/{build.sh => build.zsh} (55%) delete mode 100644 ios/patching/026-social-app-ios-serverinput-label.patch create mode 100644 ios/patching/032-social-app-ios-feed-loggedout.patch create mode 100644 ios/patching/033-social-app-ios-hide-profile-tabs.patch create mode 100644 ios/patching/036-social-app-ios-homeheader-loggedout.patch create mode 100644 ios/patching/037-social-app-ios-disable-contacts-nux.patch create mode 100644 ios/patching/AppInfo.tsx delete mode 100644 task.md 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 0000000000000000000000000000000000000000..f8f45fb6ea3d351d962359774c45911853552d6d GIT binary patch literal 29204 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 $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 0000000000000000000000000000000000000000..143348f6c93e6e660383af176832b86fda1861c0 GIT binary patch literal 6032 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 LS3j3^P6Y4FYmN4AKLknHMlZRRhHq zFu_$TEMP{kLE3(;l`8>KoCO|{#S9EAb3vG~#w2?o0|PUkr;B4q1n1i@Uw`315!=rf z{iB4sq8x%32sk!79dat)74nafyH`(P!U-;ql18RZm&Mj4C!2cpB3?DwE9j*(day7% zopw*|nbO8|x$IWo`*S4%QYZFEP0ITHcbgL5#*Q0SC7lkQ zjf=Du{`-qAxyeqX*|nQMzP-&LMP^n0@<@Xbi+f4f0PPW8}+CSI;} zMoKTgygcxXE1c^^s;l6|Tf5pHrYl~#u5Mrx)PCOhsXzqB_WYw4?(b(Tj}$YWf6CCV zj?>%zPE6>*T&L;U-L0(ryLQdK@I8ZjAD8Gw+1YxZcYm)_5mK_b$FpqNx!0<*@`H>Y z{n6ge^z4|=u@kTVR`0)XW5t8Bx4Szwd|DiGan{{=tqF6hK3jO0nwlPN_m?rh!fxVw zV0xtfq%|hh)!PK_Pu;NXS<~S_35$cGE)7a5AGp-^FY@4MpF3mr=LMTTn`be5?v=b~ zHSvjT49kkAL4B)N2TRCAxW~)8>Lyp;W(yU4n7a4j$H}EvuBjQEp3-?db+75?2^+pJ zG)~?kD;ro+?pkngz1+Ore~%w~XBsl^cX+oY z%ZrmH(f95=DmRz2$+@=r;He4IZZ7KQep>mavf<=e0nRTP8#w39zMrC7k+Q|le1)jR z(F0EzJ53MlTpy=XqwTCPp<#d5(t|3Kc29C&w!BuNDysi#==Ga*Jsq08Azm$)XI`{U zihgakYSpA)&n-lXUOkJBD|>0%@K5>!pEv&#d6#8NLIOM|9r@xD&ZNt7!)E$!(b>`* yXEHYb37l2!zbk6ihb6x0mf=bPK?`1R{b6M^mdxhg`0O7j+jzSAxvXY4FYmN4AKLknHMlZRRhHq zFu_$TEMP{kLE3(;l`8>KoCO|{#S9EAb3vG~#w2?o0|PUkr;B4q1n1i@Uw`315!=rf z{iB4sq8x%32sk!79dat)74nafyH`(P!U-;ql18RZm&Mj4C!2cpB3?DwE9j*(day7% zopw*|nbO8|x$IWo`*S4%QYZFEP0ITHcbgL5#*Q0SC7lkQ zjf=Du{`-qAxyeqX*|nQMzP-&LMP^n0@<@Xbi+f4f0PPW8}+CSI;} zMoKTgygcxXE1c^^s;l6|Tf5pHrYl~#u5Mrx)PCOhsXzqB_WYw4?(b(Tj}$YWf6CCV zj?>%zPE6>*T&L;U-L0(ryLQdK@I8ZjAD8Gw+1YxZcYm)_5mK_b$FpqNx!0<*@`H>Y z{n6ge^z4|=u@kTVR`0)XW5t8Bx4Szwd|DiGan{{=tqF6hK3jO0nwlPN_m?rh!fxVw zV0xtfq%|hh)!PK_Pu;NXS<~S_35$cGE)7a5AGp-^FY@4MpF3mr=LMTTn`be5?v=b~ zHSvjT49kkAL4B)N2TRCAxW~)8>Lyp;W(yU4n7a4j$H}EvuBjQEp3-?db+75?2^+pJ zG)~?kD;ro+?pkngz1+Ore~%w~XBsl^cX+oY z%ZrmH(f95=DmRz2$+@=r;He4IZZ7KQep>mavf<=e0nRTP8#w39zMrC7k+Q|le1)jR z(F0EzJ53MlTpy=XqwTCPp<#d5(t|3Kc29C&w!BuNDysi#==Ga*Jsq08Azm$)XI`{U zihgakYSpA)&n-lXUOkJBD|>0%@K5>!pEv&#d6#8NLIOM|9r@xD&ZNt7!)E$!(b>`* yXEHYb37l2!zbk6ihb6x0mf=bPK?`1R{b6M^mdxhg`0O7j+jzSAxvX "$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 ( -