()
+-
+- const onPressFindAccounts = React.useCallback(() => {
+- if (isWeb) {
+- navigation.navigate('Search', {})
+- } else {
+- navigation.navigate('SearchTab')
+- navigation.popToTop()
+- }
+- }, [navigation])
+-
+- const onPressDiscoverFeeds = React.useCallback(() => {
+- navigation.navigate('Feeds')
+- }, [navigation])
+
+ return (
+
+
+
+-
+- You've reached the end of your feed! Find some more accounts to
+- follow.
+-
+-
+-
+-
+- Find accounts to follow
+-
+-
+-
+-
+-
+- You can also discover new Custom Feeds to follow.
++ You've reached the end of your feed!
+
+-
+-
+- Discover new custom feeds
+-
+-
+-
+
+
+ )
+@@ -93,13 +37,4 @@ const styles = StyleSheet.create({
+ width: '100%',
+ maxWidth: 460,
+ },
+- emptyBtn: {
+- marginVertical: 20,
+- flexDirection: 'row',
+- alignItems: 'center',
+- justifyContent: 'space-between',
+- paddingVertical: 18,
+- paddingHorizontal: 24,
+- borderRadius: 30,
+- },
+ })
+diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
+index 4f25468c9..a72a10b80 100644
+--- a/src/view/com/posts/PostFeed.tsx
++++ b/src/view/com/posts/PostFeed.tsx
+@@ -766,7 +766,7 @@ let PostFeed = ({
+ } else if (row.type === 'feedShutdownMsg') {
+ return
+ } else if (row.type === 'interstitialFollows') {
+- return
++ return null
+ } else if (row.type === 'interstitialProgressGuide') {
+ return
+ } else if (row.type === 'ageAssuranceBanner') {
diff --git a/ios/patching/022-social-app-ios-bskyweb-support-pages.patch b/ios/patching/022-social-app-ios-bskyweb-support-pages.patch
new file mode 100644
index 0000000..a07a3ae
--- /dev/null
+++ b/ios/patching/022-social-app-ios-bskyweb-support-pages.patch
@@ -0,0 +1,329 @@
+diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
+index ec5261dee..c670cf75a 100644
+--- a/bskyweb/cmd/bskyweb/server.go
++++ b/bskyweb/cmd/bskyweb/server.go
+@@ -300,6 +300,10 @@ func serve(cctx *cli.Context) error {
+ e.GET("/support/tos", server.WebGeneric)
+ e.GET("/support/community-guidelines", server.WebGeneric)
+ e.GET("/support/copyright", server.WebGeneric)
++ // about/support pages (syu.is specific)
++ e.GET("/about/support/tos", server.WebAboutTOS)
++ e.GET("/about/support/privacy-policy", server.WebAboutPrivacy)
++ e.GET("/about/support/help", server.WebAboutHelp)
+ e.GET("/intent/compose", server.WebGeneric)
+ e.GET("/intent/verify-email", server.WebGeneric)
+ e.GET("/intent/age-assurance", server.WebGeneric)
+@@ -753,3 +757,21 @@ func (srv *Server) WebIpCC(c echo.Context) error {
+ }
+ return c.JSON(200, outResponse)
+ }
++
++// Handler for About TOS page (syu.is specific)
++func (srv *Server) WebAboutTOS(c echo.Context) error {
++ data := srv.NewTemplateContext()
++ return c.Render(http.StatusOK, "about-tos.html", data)
++}
++
++// Handler for About Privacy Policy page (syu.is specific)
++func (srv *Server) WebAboutPrivacy(c echo.Context) error {
++ data := srv.NewTemplateContext()
++ return c.Render(http.StatusOK, "about-privacy.html", data)
++}
++
++// Handler for About Help page (syu.is specific)
++func (srv *Server) WebAboutHelp(c echo.Context) error {
++ data := srv.NewTemplateContext()
++ return c.Render(http.StatusOK, "about-help.html", data)
++}
+diff --git a/bskyweb/templates/about-help.html b/bskyweb/templates/about-help.html
+new file mode 100644
+index 000000000..d37db25c5
+--- /dev/null
++++ b/bskyweb/templates/about-help.html
+@@ -0,0 +1,98 @@
++
++
++
++
++
++ Help - syu.is
++
++
++
++
++
++
++ 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
++
++
++ Related Links
++
++
++
++
++
+diff --git a/bskyweb/templates/about-privacy.html b/bskyweb/templates/about-privacy.html
+new file mode 100644
+index 000000000..14a1168ad
+--- /dev/null
++++ b/bskyweb/templates/about-privacy.html
+@@ -0,0 +1,92 @@
++
++
++
++
++
++ Privacy Policy - syu.is
++
++
++
++
++
++
++ 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:
++
++ Account Information: Email address, username, and profile information you provide
++ Content: Posts, messages, and other content you create on the platform
++ Usage Data: Information about how you interact with our service
++ Device Information: Browser type, operating system, and device identifiers
++
++
++ 3. How We Use Your Information
++ We use your information to:
++
++ Provide and maintain our service
++ Improve and personalize your experience
++ Communicate with you about the service
++ Ensure security and prevent abuse
++
++
++ 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:
++
++ Access your personal data
++ Request correction of your data
++ Request deletion of your account
++ Export your data
++
++
++ 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/bskyweb/templates/about-tos.html b/bskyweb/templates/about-tos.html
+new file mode 100644
+index 000000000..db5d82f5c
+--- /dev/null
++++ b/bskyweb/templates/about-tos.html
+@@ -0,0 +1,84 @@
++
++
++
++
++
++ Terms of Service - syu.is
++
++
++
++
++
++
++ 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:
++
++ Provide accurate information when creating an account
++ Keep your account credentials secure
++ Not use the service for illegal activities
++ Respect other users and their content
++ Comply with applicable laws and regulations
++
++
++ 4. Content Guidelines
++ Users are responsible for the content they post. Prohibited content includes:
++
++ Illegal content
++ Harassment or abuse
++ Spam or misleading information
++ Content that violates others' rights
++
++
++ 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/ios/patching/023-social-app-ios-disable-dm.patch b/ios/patching/023-social-app-ios-disable-dm.patch
new file mode 100644
index 0000000..2f2236e
--- /dev/null
+++ b/ios/patching/023-social-app-ios-disable-dm.patch
@@ -0,0 +1,70 @@
+diff --git a/src/state/messages/events/index.tsx b/src/state/messages/events/index.tsx
+index 2ff0784ae..dc314ecc5 100644
+--- a/src/state/messages/events/index.tsx
++++ b/src/state/messages/events/index.tsx
+@@ -10,13 +10,7 @@ const MessagesEventBusContext = React.createContext(
+ MessagesEventBusContext.displayName = 'MessagesEventBusContext'
+
+ export function useMessagesEventBus() {
+- const ctx = React.useContext(MessagesEventBusContext)
+- if (!ctx) {
+- throw new Error(
+- 'useMessagesEventBus must be used within a MessagesEventBusProvider',
+- )
+- }
+- return ctx
++ return React.useContext(MessagesEventBusContext)
+ }
+
+ export function MessagesEventBusProvider({
+@@ -24,18 +18,11 @@ export function MessagesEventBusProvider({
+ }: {
+ children: React.ReactNode
+ }) {
+- const {currentAccount} = useSession()
+-
+- if (!currentAccount) {
+- return (
+-
+- {children}
+-
+- )
+- }
+-
++ // DM functionality is disabled for syu.is
+ return (
+- {children}
++
++ {children}
++
+ )
+ }
+
+diff --git a/src/state/queries/messages/list-conversations.tsx b/src/state/queries/messages/list-conversations.tsx
+index c5457d1cb..5bc37bdce 100644
+--- a/src/state/queries/messages/list-conversations.tsx
++++ b/src/state/queries/messages/list-conversations.tsx
+@@ -74,17 +74,12 @@ export function useListConvos() {
+
+ const empty = {accepted: [], request: []}
+ export function ListConvosProvider({children}: {children: React.ReactNode}) {
+- const {hasSession} = useSession()
+-
+- if (!hasSession) {
+- return (
+-
+- {children}
+-
+- )
+- }
+-
+- return {children}
++ // DM functionality is disabled for syu.is - always return empty
++ return (
++
++ {children}
++
++ )
+ }
+
+ export function ListConvosProviderInner({
diff --git a/ios/patching/024-social-app-ios-disable-external-services.patch b/ios/patching/024-social-app-ios-disable-external-services.patch
new file mode 100644
index 0000000..66bd786
--- /dev/null
+++ b/ios/patching/024-social-app-ios-disable-external-services.patch
@@ -0,0 +1,15 @@
+diff --git a/src/env/common.ts b/src/env/common.ts
+--- a/src/env/common.ts
++++ b/src/env/common.ts
+@@ -107,9 +107,8 @@ export const GCP_PROJECT_ID: number =
+ /**
+ * URLs for the app config web worker. Can be a
+ * locally running server, see `env.example` for more.
++ * Disabled for self-hosted environment to avoid CORS errors
+ */
+ export const BAPP_CONFIG_DEV_URL = process.env.BAPP_CONFIG_DEV_URL
+ export const BAPP_CONFIG_PROD_URL = `https://ip.bsky.app`
+-export const BAPP_CONFIG_URL = IS_DEV
+- ? (BAPP_CONFIG_DEV_URL ?? BAPP_CONFIG_PROD_URL)
+- : BAPP_CONFIG_PROD_URL
++export const BAPP_CONFIG_URL = null
diff --git a/ios/patching/025-social-app-ios-bskyweb-title.patch b/ios/patching/025-social-app-ios-bskyweb-title.patch
new file mode 100644
index 0000000..9b674e6
--- /dev/null
+++ b/ios/patching/025-social-app-ios-bskyweb-title.patch
@@ -0,0 +1,91 @@
+diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
+--- a/bskyweb/templates/base.html
++++ b/bskyweb/templates/base.html
+@@ -7,9 +7,9 @@
+
+-
+-
+- {%- block head_title -%}Bluesky{%- endblock -%}
++
++
++ {%- block head_title -%}syu.is{%- endblock -%}
+
+
+
+@@ -121,7 +121,7 @@
+
+ JavaScript Required
+ This is a heavily interactive web application, and JavaScript is required. Simple HTML interfaces are possible, but that is not what this is.
+-
Learn more about Bluesky at bsky.social and atproto.com .
++
Learn more at syu.is and atproto.com .
+ {% block noscript_extra %}{% endblock %}
+
+ {% endblock -%}
+diff --git a/bskyweb/templates/home.html b/bskyweb/templates/home.html
+--- a/bskyweb/templates/home.html
++++ b/bskyweb/templates/home.html
+@@ -1,17 +1,17 @@
+ {% extends "base.html" %}
+
+-{% block head_title %}Bluesky{% endblock %}
++{% block head_title %}syu.is{% endblock %}
+
+ {% block html_head_extra -%}
+-
+-
++
++
+
+
+
+
+
+-
+-
+-
++
++
++
+
+
+diff --git a/bskyweb/templates/error.html b/bskyweb/templates/error.html
+--- a/bskyweb/templates/error.html
++++ b/bskyweb/templates/error.html
+@@ -1,6 +1,6 @@
+ {% extends "base.html" %}
+
+-{% block head_title %}Error {{ statusCode }} - Bluesky{% endblock %}
++{% block head_title %}Error {{ statusCode }} - syu.is{% endblock %}
+
+ {% block noscript_extra %}
+ {%- if statusCode == 404 %}
+diff --git a/bskyweb/templates/starterpack.html b/bskyweb/templates/starterpack.html
+--- a/bskyweb/templates/starterpack.html
++++ b/bskyweb/templates/starterpack.html
+@@ -17,8 +17,8 @@
+
+
+ {%- else -%}
+-
+-
++
++
+ {% endif -%}
+
+
+diff --git a/web/index.html b/web/index.html
+--- a/web/index.html
++++ b/web/index.html
+@@ -14,8 +14,8 @@
+
+-
+-
++
++
+ %WEB_TITLE%
+
+
diff --git a/ios/patching/026-social-app-ios-serverinput-label.patch b/ios/patching/026-social-app-ios-serverinput-label.patch
new file mode 100644
index 0000000..08a3264
--- /dev/null
+++ b/ios/patching/026-social-app-ios-serverinput-label.patch
@@ -0,0 +1,15 @@
+diff --git a/src/components/dialogs/ServerInput.tsx b/src/components/dialogs/ServerInput.tsx
+--- a/src/components/dialogs/ServerInput.tsx
++++ b/src/components/dialogs/ServerInput.tsx
+@@ -144,9 +144,9 @@
+
++ label={_(msg`syu.is`)}>
+
+- {_(msg`Bluesky`)}
++ {_(msg`syu.is`)}
+
+
+
+
+ License
+
+
+ This application is based on Bluesky Social App.
+
+
+
+ https://github.com/bluesky-social/social-app
+
+
+ MIT License
+
+
+ Copyright (c) 2022-2025 Bluesky PBC
+
+
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
+ 日本語訳(参考)
+
+
+ 本ソフトウェアおよび関連文書ファイル(以下「ソフトウェア」)のコピーを取得する
+ すべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、
+ ソフトウェアのコピーを使用、複製、変更、結合、公開、配布、サブライセンス、
+ および/または販売する権利、ならびにソフトウェアを提供する相手にそうした行為を
+ 許可する権利が含まれますが、これらに限定されません。
+
+
+
+ 上記の著作権表示および本許諾表示を、ソフトウェアのすべてのコピーまたは
+ 重要な部分に記載するものとします。
+
+
+
+ ソフトウェアは「現状のまま」で提供され、明示黙示を問わず、商品性、特定目的への
+ 適合性、および権利非侵害についての保証を含む、いかなる種類の保証もなされません。
+ いかなる場合においても、作者または著作権者は、契約行為、不法行為、またはそれ以外で
+ あろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用または
+ その他の扱いによって生じる一切の請求、損害、その他の義務について責任を負わないものとします。
+
+
+
+ Original License: https://github.com/bluesky-social/social-app/blob/main/LICENSE
+
+
+
+ )
+}
diff --git a/ios/patching/README.md b/ios/patching/README.md
new file mode 100644
index 0000000..a7c6ba3
--- /dev/null
+++ b/ios/patching/README.md
@@ -0,0 +1,62 @@
+# iOS Social App Patches
+
+このディレクトリには、iOS版social-appのカスタマイズパッチが含まれています。
+
+## パッチファイル一覧
+
+- `001-social-app-ios-config.patch` - app.config.js の設定変更(アプリ名、Bundle ID、アイコンパス、ドメイン等)
+- `002-social-app-ios-lib.patch` - lib/constants.ts, lib/statsig, lib/url-helpers の変更
+- `003-social-app-ios-view.patch` - Logo, Logotype, UserAvatar, Splash.tsx の UI 変更(Bluesky 蝶ロゴを logo.png に変更)
+- `004-social-app-ios-core.patch` - agent.ts, App.native.tsx, routes.ts のコア変更
+- `005-social-app-ios-screens.patch` - Settings, Home, Privacy, TOS 画面の変更
+- `006-social-app-ios-shell.patch` - Drawer, BottomBar, RightNav, ServerInput などシェル変更
+- `007-social-app-ios-misc.patch` - notifications, ageAssurance, PolicyUpdate などその他変更
+- `008-social-app-ios-policy-tos-error.patch` - プライバシーポリシー・利用規約をネイティブコンポーネントで表示(WebView から ScrollView + Text へ変更)
+- `009-social-app-ios-license.patch` - ライセンスページの追加(Drawer, Navigation, routes, types)
+- `010-social-app-ios-remove-contact-support.patch` - アカウント作成時の「Contact support」リンクを削除
+- `011-social-app-ios-splash-license-footer.patch` - スプラッシュ画面の「What's up?」削除、© syui フッター追加
+- `012-social-app-ios-settings-about-help.patch` - About 設定と routes.ts のリンクを内部ルートに変更(/support/tos, /support/privacy-policy, /support/license)
+- `013-social-app-ios-settings-remove-help.patch` - Settings から Help 項目を削除
+- `019-social-app-ios-entitlements-plugin.patch` - iOS entitlements プラグイン設定
+- `020-social-app-ios-bypass-age-assurance.patch` - 年齢確認を完全に無効化(access を Full に固定、chatDisabled と adultContentDisabled を false に固定)
+- `021-social-app-ios-clean-feed.patch` - Following フィードのシンプル化(DiscoverFallbackHeader の (i) アイコンと Discover リンク削除、SuggestedFollows インタースティシャル無効化、おすすめボタン削除)
+- `License.tsx` - ライセンス表示画面(新規ファイル)
+
+## 使用方法
+
+### パッチの適用
+
+```bash
+cd /Users/syui/ai/at/ios
+./setup.zsh patch
+```
+
+**注意**: setup.zsh が自動的に以下を実行します:
+- パッチファイルの適用
+- License.tsx のコピー
+- Xcode AppIcon を logo.png から 1024x1024 にリサイズして配置(GraphicsMagick または sips を使用)
+
+### リポジトリのリセット
+
+```bash
+cd /Users/syui/ai/at/ios
+./setup.zsh reset
+```
+
+### すべてのパッチを適用(デフォルト)
+
+```bash
+cd /Users/syui/ai/at/ios
+./setup.zsh
+```
+
+## パッチの更新方法
+
+repos/social-app で変更を加えた後:
+
+```bash
+cd /Users/syui/ai/at/repos/social-app
+git diff [ファイル名] > /Users/syui/ai/at/ios/patching/新しいパッチ.patch
+```
+
+その後、`setup.zsh` の `PATCH_FILES_IOS` 配列に新しいパッチファイル名を追加してください。
diff --git a/ios/preview.zsh b/ios/preview.zsh
new file mode 100755
index 0000000..070c575
--- /dev/null
+++ b/ios/preview.zsh
@@ -0,0 +1,82 @@
+#!/bin/zsh
+set -e
+d=${0:a:h}
+cd $d
+
+source $d/.env
+
+#xcrun simctl uninstall booted $BUNDLE_ID
+
+echo "Running iOS preview workflow..."
+cd "$REPO_DIR"
+
+# 0. Environment Setup (Fix Node Version)
+export NVM_DIR="$HOME/.nvm"
+[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
+echo "Checking Node version..."
+if command -v nvm >/dev/null; then
+ nvm use 22 || nvm use 20 || echo "Warning: Could not switch to Node 22/20. Current: $(node -v)"
+else
+ echo "nvm not found, using system node: $(node -v)"
+fi
+
+# 1. Install dependencies
+echo "1. Installing dependencies (yarn)..."
+yarn install
+
+# 1.5. Copy assets
+echo "1.5. Copying assets..."
+ASSETS_DIR="${0:a:h}/assets"
+if [ -d "$ASSETS_DIR" ]; then
+ cp -rf "$ASSETS_DIR/"* "$REPO_DIR/assets/"
+ echo "✅ Copied all assets (including logo.png, logo-1024.png)"
+else
+ echo "⚠️ Warning: $ASSETS_DIR not found"
+fi
+
+# 2. Prebuild (Generate ios directory)
+echo "2. Running Expo Prebuild..."
+# Clean old ios folder to remove old entitlements/AppClip targets
+rm -rf ios
+npx expo prebuild --platform ios --clean
+
+# 3. CocoaPods
+echo "3. Installing CocoaPods..."
+# Ensure PATH includes Homebrew ruby gems if needed
+export PATH="/opt/homebrew/lib/ruby/gems/3.4.0/bin:$PATH"
+cd ios
+pod install
+cd ..
+
+# 4. Signing (Manual Step)
+echo "4. Opening Xcode for 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
+
+open "$XCODE_PROJ"
+echo "========================================================"
+echo " [ACTION REQUIRED] "
+echo " Xcode opened ($XCODE_PROJ)."
+echo " 1. Go to 'Signing & Capabilities' tab."
+echo " 2. Select your Team."
+echo " 3. Verify 'App Clip' target is gone."
+echo " 4. Ensure no red errors exist."
+echo " Press ENTER here once you are done to continue building."
+echo "========================================================"
+read
+
+# 5. Run
+echo "5. Building and Running..."
+# If user wants specific device ID, uncomment below, otherwise let Expo ask/pick boot simulator
+
+case $1 in
+ d|devicei)
+ npx expo run:ios --device "$DEVICE_ID" --configuration Release
+ ;;
+ *)
+ npx expo run:ios
+ ;;
+esac
diff --git a/ios/setup.zsh b/ios/setup.zsh
new file mode 100755
index 0000000..8919a8d
--- /dev/null
+++ b/ios/setup.zsh
@@ -0,0 +1,223 @@
+#!/bin/zsh
+
+cd ${0:a:h}
+
+# iOS Social App Patch Setup Script
+# Usage: ./ios/setup.zsh [patch|reset]
+
+# Cross-platform sed (macOS vs Linux)
+function sediment() {
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ sed -i '' "$@"
+ else
+ sed -i "$@"
+ fi
+}
+
+# Arrays for patch management
+typeset -a FAILED_PATCHES
+
+# Patch file lists for iOS
+typeset -a PATCH_FILES_IOS
+PATCH_FILES_IOS=(
+ "001-social-app-ios-config.patch"
+ "002-social-app-ios-lib.patch"
+ "003-social-app-ios-view.patch"
+ "004-social-app-ios-core.patch"
+ "005-social-app-ios-screens.patch"
+ "006-social-app-ios-shell.patch"
+ "007-social-app-ios-misc.patch"
+ "009-social-app-ios-license.patch"
+ "010-social-app-ios-remove-contact-support.patch"
+ "011-social-app-ios-splash-license-footer.patch"
+ "013-social-app-ios-settings-remove-help.patch"
+ "019-social-app-ios-entitlements-plugin.patch"
+ "020-social-app-ios-bypass-age-assurance.patch"
+ "021-social-app-ios-clean-feed.patch"
+ "022-social-app-ios-bskyweb-support-pages.patch"
+ "023-social-app-ios-disable-dm.patch"
+ "024-social-app-ios-disable-external-services.patch"
+ "025-social-app-ios-bskyweb-title.patch"
+ "026-social-app-ios-serverinput-label.patch"
+)
+
+function ios-env() {
+ d=${0:a:h:h}
+ patching_dir=${0:a:h}/patching
+ target_dir=$d/repos/social-app
+}
+
+# Common patch function with status detection
+function apply-patch() {
+ local patch_name=$1
+ local target_dir=$2
+ local patch_file=$3
+
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "📝 Patch: ${patch_name}"
+ echo " Target: ${target_dir}"
+ echo " File: ${patch_file}"
+
+ pushd ${target_dir} > /dev/null
+
+ # Check if patch is already applied (reverse dry-run succeeds)
+ if patch -f --dry-run -p1 -R < ${patch_file} > /dev/null 2>&1; then
+ echo "✅ Already applied - skipping"
+ popd > /dev/null
+ echo ""
+ return 0
+ fi
+
+ # Check if patch can be applied (forward dry-run succeeds)
+ if patch --dry-run -p1 < ${patch_file} > /dev/null 2>&1; then
+ echo "🔧 Applying patch..."
+ if patch -p1 < ${patch_file}; then
+ echo "✅ Applied successfully"
+ popd > /dev/null
+ echo ""
+ return 0
+ else
+ echo "❌ Failed to apply"
+ FAILED_PATCHES+=("${patch_name} (${patch_file})")
+ popd > /dev/null
+ echo ""
+ return 1
+ fi
+ else
+ echo "⚠️ Cannot apply - file may have been modified"
+ echo " Please check manually"
+ FAILED_PATCHES+=("${patch_name} (${patch_file}) - file modified")
+ popd > /dev/null
+ echo ""
+ return 1
+ fi
+}
+
+function show-failed-patches() {
+ if [ ${#FAILED_PATCHES[@]} -eq 0 ]; then
+ echo ""
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "✅ All patches applied successfully!"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo ""
+ return 0
+ fi
+
+ echo ""
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "⚠️ FAILED PATCHES SUMMARY"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo ""
+ echo "The following patches could not be applied:"
+ echo ""
+ for failed_patch in "${FAILED_PATCHES[@]}"; do
+ echo " ❌ ${failed_patch}"
+ done
+ echo ""
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo ""
+}
+
+# Helper function for applying patches
+function patch-apply() {
+ local name=$1
+ local patch_file=$2
+ apply-patch "${name}" "$target_dir" "$patching_dir/${patch_file}"
+}
+
+# Generate build number from timestamp (YYMMDDHHMMSS)
+function ios-generate-build-number() {
+ local build_number=$(date +%y%m%d%H%M%S)
+ local config_patch="$patching_dir/001-social-app-ios-config.patch"
+
+ if [ -f "$config_patch" ]; then
+ # Replace placeholder with timestamp
+ sediment "s/__BUILD_NUMBER__/${build_number}/" "$config_patch"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "🔢 Build number: ${build_number}"
+ echo ""
+ fi
+}
+
+# Restore placeholder after patching (for git cleanliness)
+function ios-restore-build-placeholder() {
+ local config_patch="$patching_dir/001-social-app-ios-config.patch"
+
+ if [ -f "$config_patch" ]; then
+ # Restore placeholder for next build
+ sediment "s/buildNumber: '[0-9]*'/buildNumber: '__BUILD_NUMBER__'/" "$config_patch"
+ fi
+}
+
+# Auto-apply patches from list
+function ios-patch-apply-all() {
+ for filename in "${PATCH_FILES_IOS[@]}"; do
+ local title="${filename%.*}"
+ patch-apply "$title" "$filename"
+ done
+}
+
+# Copy new files that aren't in patches
+function ios-copy-new-files() {
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "📁 Copying new files..."
+
+ # Copy all assets from ios/assets/ to repos/social-app/assets/
+ if [ -d "$d/ios/assets" ]; then
+ cp -rf "$d/ios/assets/"* "$target_dir/assets/"
+ echo "✅ Copied all assets (including logo.png, app-icons)"
+ fi
+
+ # Copy License.tsx
+ if [ -f "$patching_dir/License.tsx" ]; then
+ mkdir -p "$target_dir/src/view/screens"
+ cp "$patching_dir/License.tsx" "$target_dir/src/view/screens/License.tsx"
+ echo "✅ Copied License.tsx"
+ fi
+
+ echo ""
+}
+
+function ios-setup-clone() {
+ if [ ! -d $target_dir ]; then
+ echo "Error: social-app repository not found at $target_dir"
+ echo "Please run install.zsh first to clone repositories"
+ return 1
+ fi
+ echo "Repository found: $target_dir"
+}
+
+function ios-setup-reset() {
+ echo "Resetting social-app repository..."
+ cd $target_dir
+ git stash
+ git checkout .
+ echo "Reset complete"
+}
+
+# Main execution
+ios-env
+
+case "$1" in
+ patch)
+ ios-setup-clone
+ ios-generate-build-number
+ ios-patch-apply-all
+ ios-restore-build-placeholder
+ ios-copy-new-files
+ show-failed-patches
+ exit
+ ;;
+ reset)
+ ios-setup-reset
+ exit
+ ;;
+ *)
+ ios-setup-clone
+ ios-generate-build-number
+ ios-patch-apply-all
+ ios-restore-build-placeholder
+ ios-copy-new-files
+ show-failed-patches
+ ;;
+esac
diff --git a/task.md b/task.md
new file mode 100644
index 0000000..3136801
--- /dev/null
+++ b/task.md
@@ -0,0 +1,4 @@
+## Rules & Constraints
+- Do not reformat existing code unless explicitly requested.
+- Maintain existing import styles (newlines, sorting).
+- Keep patches minimal (clean deltas).