From 023d41b2a07afbd519b0e2a2203876c500658fcb Mon Sep 17 00:00:00 2001 From: AokazeNozomi <272103650+AokazeNozomi@users.noreply.github.com> Date: Fri, 3 Apr 2026 23:57:58 -0500 Subject: [PATCH 1/2] iOS 17.0 Backport --- .github/workflows/deploy-pre-release.yml | 36 +- .github/workflows/deploy.yml | 57 ++-- AltStore.json | 20 +- EhPanda.xcodeproj/project.pbxproj | 67 ++-- .../xcshareddata/swiftpm/Package.resolved | 11 +- .../xcshareddata/xcschemes/EhPanda.xcscheme | 2 +- EhPanda/App/Generated/Strings.swift | 12 + EhPanda/App/Info.plist | 4 +- EhPanda/App/de.lproj/Localizable.strings | 3 + EhPanda/App/en.lproj/Localizable.strings | 3 + EhPanda/App/ja.lproj/Localizable.strings | 3 + EhPanda/App/ko.lproj/Localizable.strings | 3 + EhPanda/App/zh-Hans.lproj/Localizable.strings | 3 + .../App/zh-Hant-HK.lproj/Localizable.strings | 4 + .../App/zh-Hant-TW.lproj/Localizable.strings | 3 + EhPanda/App/zh-Hant.lproj/Localizable.strings | 2 + .../View/Detail/Archives/ArchivesView.swift | 69 ++-- .../Detail/Components/PostCommentView.swift | 17 +- EhPanda/View/Detail/DetailView.swift | 45 ++- .../View/Reading/Support/AdvancedList.swift | 55 +++- .../View/Reading/Support/ControlPanel.swift | 311 +++++++++++++----- EhPanda/View/Search/SearchRootView.swift | 136 +++++--- .../View/Search/Support/QuickSearchView.swift | 8 +- .../View/Setting/Components/AboutView.swift | 26 +- .../Components/LaboratorySettingView.swift | 53 ++- EhPanda/View/Setting/Login/LoginView.swift | 40 ++- .../View/Support/Components/AlertView.swift | 3 +- README.md | 4 +- READMEs/README.chs.md | 5 +- READMEs/README.cht.md | 4 +- READMEs/README.de.md | 4 +- READMEs/README.jpn.md | 5 +- READMEs/README.ko.md | 5 +- 33 files changed, 701 insertions(+), 322 deletions(-) diff --git a/.github/workflows/deploy-pre-release.yml b/.github/workflows/deploy-pre-release.yml index 571d459f..59dee4cb 100644 --- a/.github/workflows/deploy-pre-release.yml +++ b/.github/workflows/deploy-pre-release.yml @@ -3,26 +3,26 @@ on: workflow_dispatch: inputs: versionTag: - description: 'Version tag' + description: "Version tag" required: true type: string releaseTitle: - description: 'Release title' + description: "Release title" required: true type: string releaseDescription: - description: 'Release description' + description: "Release description" required: true type: string env: DEVELOPER_DIR: /Applications/Xcode_26.0.1.app - SCHEME_NAME: 'EhPanda' - ALTSTORE_JSON_PATH: './AltStore.json' - BUILDS_PATH: '/tmp/action-builds' - PAYLOAD_PATH: '/tmp/action-builds/Payload' - THIN_PAYLOAD_SCRIPT_PATH: './actions-tool/thin-payload.sh' - ARCHIVE_PATH: '/tmp/action-builds/EhPanda.xcarchive' - IPA_OUTPUT_PATH: '/tmp/action-builds/EhPanda.ipa' + SCHEME_NAME: "EhPanda" + ALTSTORE_JSON_PATH: "./AltStore.json" + BUILDS_PATH: "/tmp/action-builds" + PAYLOAD_PATH: "/tmp/action-builds/Payload" + THIN_PAYLOAD_SCRIPT_PATH: "./actions-tool/thin-payload.sh" + ARCHIVE_PATH: "/tmp/action-builds/EhPanda.xcarchive" + IPA_OUTPUT_PATH: "/tmp/action-builds/EhPanda.ipa" jobs: Deploy: @@ -34,7 +34,7 @@ jobs: - name: Modify git config run: | git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.email "272103650+github-actions[bot]@users.noreply.github.com" - name: Install iOS 26 Platform uses: nick-fields/retry@v3 with: @@ -89,10 +89,10 @@ jobs: - name: Release to GitHub uses: softprops/action-gh-release@v2 with: - prerelease: true - fail_on_unmatched_files: true - files: ${{ env.IPA_OUTPUT_PATH }} - token: ${{ secrets.GITHUB_TOKEN }} - name: ${{ inputs.releaseTitle }} - body: ${{ inputs.releaseDescription }} - tag_name: 'v${{ steps.bump-version.outputs.version }}' + prerelease: true + fail_on_unmatched_files: true + files: ${{ env.IPA_OUTPUT_PATH }} + token: ${{ secrets.GITHUB_TOKEN }} + name: ${{ inputs.releaseTitle }} + body: ${{ inputs.releaseDescription }} + tag_name: "v${{ steps.bump-version.outputs.version }}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 37e75998..3ac89edd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,31 +6,32 @@ on: types: [closed] env: DEVELOPER_DIR: /Applications/Xcode_26.0.1.app - APP_VERSION: '2.8.0' - SCHEME_NAME: 'EhPanda' - ALTSTORE_JSON_PATH: './AltStore.json' - BUILDS_PATH: '/tmp/action-builds' - PAYLOAD_PATH: '/tmp/action-builds/Payload' - FILTER_SWIFT_PATH: './actions-tool/ReleaseNotesFilter.swift' - FILTER_PATH: './actions-tool/ReleaseNotesFilter' - THIN_PAYLOAD_SCRIPT_PATH: './actions-tool/thin-payload.sh' - ARCHIVE_PATH: '/tmp/action-builds/EhPanda.xcarchive' - IPA_OUTPUT_PATH: '/tmp/action-builds/EhPanda.ipa' - + APP_VERSION: "2.9.0" + SCHEME_NAME: "EhPanda" + ALTSTORE_JSON_PATH: "./AltStore.json" + BUILDS_PATH: "/tmp/action-builds" + PAYLOAD_PATH: "/tmp/action-builds/Payload" + FILTER_SWIFT_PATH: "./actions-tool/ReleaseNotesFilter.swift" + FILTER_PATH: "./actions-tool/ReleaseNotesFilter" + THIN_PAYLOAD_SCRIPT_PATH: "./actions-tool/thin-payload.sh" + ARCHIVE_PATH: "/tmp/action-builds/EhPanda.xcarchive" + IPA_OUTPUT_PATH: "/tmp/action-builds/EhPanda.ipa" +permissions: write-all jobs: Deploy: runs-on: macos-26 if: | github.event.pull_request.merged == true && ( github.event.pull_request.user.login == 'aalberrty' || - github.event.pull_request.user.login == 'chihchy') + github.event.pull_request.user.login == 'chihchy' || + github.event.pull_request.user.login == 'AokazeNozomi') steps: - name: Checkout uses: actions/checkout@v4 - name: Modify git config run: | git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.email "272103650+github-actions[bot]@users.noreply.github.com" - name: Install iOS 26 Platform uses: nick-fields/retry@v3 with: @@ -89,12 +90,12 @@ jobs: - name: Release to GitHub uses: softprops/action-gh-release@v2 with: - fail_on_unmatched_files: true - files: ${{ env.IPA_OUTPUT_PATH }} - token: ${{ secrets.GITHUB_TOKEN }} - body: ${{ github.event.pull_request.body }} - name: ${{ github.event.pull_request.title }} - tag_name: 'v${{ steps.bump-version.outputs.version }}' + fail_on_unmatched_files: true + files: ${{ env.IPA_OUTPUT_PATH }} + token: ${{ secrets.GITHUB_TOKEN }} + body: ${{ github.event.pull_request.body }} + name: ${{ github.event.pull_request.title }} + tag_name: "v${{ steps.bump-version.outputs.version }}" - name: Commit bump version run: | git add . @@ -106,17 +107,19 @@ jobs: echo "`jq '.apps[0].versions[0].localizedDescription="${{ steps.retrieve-data.outputs.notes }}"' $ALTSTORE_JSON_PATH`" > $ALTSTORE_JSON_PATH echo "`jq '.apps[0].versions[0].date="${{ steps.retrieve-data.outputs.version_date }}"' $ALTSTORE_JSON_PATH`" > $ALTSTORE_JSON_PATH echo "`jq '.apps[0].versions[0].version="${{ steps.bump-version.outputs.version }}"' $ALTSTORE_JSON_PATH`" > $ALTSTORE_JSON_PATH - echo "`jq '.apps[0].versions[0].downloadURL="https://github.com/EhPanda-Team/EhPanda/releases/download/v${{ steps.bump-version.outputs.version }}/EhPanda.ipa"' $ALTSTORE_JSON_PATH`" > $ALTSTORE_JSON_PATH + echo "`jq '.apps[0].versions[0].minOSVersion="17.0"' $ALTSTORE_JSON_PATH`" > $ALTSTORE_JSON_PATH + echo "`jq '.apps[0].versions[0].downloadURL="https://github.com/AokazeNozomi/EhPanda17/releases/download/v${{ steps.bump-version.outputs.version }}/EhPanda.ipa"' $ALTSTORE_JSON_PATH`" > $ALTSTORE_JSON_PATH - name: Commit update AltStore.json run: | git add . git commit -m "Update AltStore.json" git push origin HEAD - - name: Post release notes - run: | - curl https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ - -d parse_mode=markdown -d chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }} \ - -d text='*v${{ steps.bump-version.outputs.version }} Release Notes:*%0A${{ github.event.pull_request.body }}' - curl ${{ secrets.DISCORD_WEBHOOK }} \ - -F 'payload_json={"content": "**v${{ steps.bump-version.outputs.version }} Release Notes:**\n${{ steps.retrieve-data.outputs.notes }}"}' +# - name: Post release notes +# run: | +# curl https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ +# -d parse_mode=markdown -d chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }} \ +# -d text='*v${{ steps.bump-version.outputs.version }} Release Notes:*%0A${{ github.event.pull_request.body }}' +# +# curl ${{ secrets.DISCORD_WEBHOOK }} \ +# -F 'payload_json={"content": "**v${{ steps.bump-version.outputs.version }} Release Notes:**\n${{ steps.retrieve-data.outputs.notes }}"}' diff --git a/AltStore.json b/AltStore.json index 23fd3931..4dee44a5 100644 --- a/AltStore.json +++ b/AltStore.json @@ -1,18 +1,18 @@ { "name": "EhPanda AltStore Source", - "identifier": "app.ehpanda.altstore", + "identifier": "app.ehpanda17.altstore", "subtitle": "Unleash your passion for E-Hentai with EhPanda.", "description": "EhPanda is an unofficial app for iOS and iPadOS that allows users to access and explore adult manga and doujinshi from E-Hentai.\nWith a user-friendly interface, powerful search features, and a vast collection of content, it offers a personalized library and an immersive reading experience.\nNote: EhPanda is not affiliated with or endorsed by E-Hentai.", "iconURL": "https://github.com/EhPanda-Team/ehpanda-website/raw/main/public/img/appicon/appicon-512.png", "website": "https://ehpanda.app", "tintColor": "289979", "featuredApps": [ - "app.ehpanda" + "app.ehpanda17" ], "apps": [ { "name": "EhPanda", - "bundleIdentifier": "app.ehpanda", + "bundleIdentifier": "app.ehpanda17", "developerName": "EhPanda Team", "subtitle": "Unleash your passion for E-Hentai with EhPanda.", "localizedDescription": "EhPanda is an unofficial app for iOS and iPadOS that allows users to access and explore adult manga and doujinshi from E-Hentai.\nWith a user-friendly interface, powerful search features, and a vast collection of content, it offers a personalized library and an immersive reading experience.\nNote: EhPanda is not affiliated with or endorsed by E-Hentai.", @@ -32,12 +32,12 @@ ], "versions": [ { - "version": "2.8.0", - "date": "2025-10-23T04:23:41", - "localizedDescription": "1. feat: Adapt to the new Liquid Glass design.\n3. fix: Gesture issue in the reading page.\n4. fix: Crash issue in eh setting page.\n5. fix: A minor issue in search page.", - "downloadURL": "https://github.com/EhPanda-Team/EhPanda/releases/download/v2.8.0/EhPanda.ipa", - "size": 7224545, - "minOSVersion": "16.0" + "version": "2.9.0", + "date": "2026-02-07T21:33:03", + "localizedDescription": "1. feat: Added back support for older iOS versions, going back to iOS 17.0", + "downloadURL": "https://github.com/AokazeNozomi/EhPanda17/releases/download/v2.9.0/EhPanda.ipa", + "size": 7285388, + "minOSVersion": "17.0" } ], "appPermissions": { @@ -60,7 +60,7 @@ "identifier": "ehpanda-now-available", "caption": "An unofficial E-Hentai App for iOS & iPadOS.", "tintColor": "289979", - "appID": "app.ehpanda", + "appID": "app.ehpanda17", "date": "2021-08-19", "notify": false } diff --git a/EhPanda.xcodeproj/project.pbxproj b/EhPanda.xcodeproj/project.pbxproj index 15c18f3b..a7841d13 100644 --- a/EhPanda.xcodeproj/project.pbxproj +++ b/EhPanda.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 7E1F0E7F2ED6ABE4005C6249 /* SwiftUIBackports in Frameworks */ = {isa = PBXBuildFile; productRef = 7E1F0E7E2ED6ABE4005C6249 /* SwiftUIBackports */; }; AB0929B6277F043D00F107CA /* AccountSettingReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB0929B5277F043D00F107CA /* AccountSettingReducer.swift */; }; AB0929BE2780032400F107CA /* EhSettingReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB0929BD2780032400F107CA /* EhSettingReducer.swift */; }; AB0929C027805A8200F107CA /* LoginReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB0929BF27805A8200F107CA /* LoginReducer.swift */; }; @@ -638,6 +639,7 @@ ABBB2636278FB888007B6149 /* SwiftUINavigation in Frameworks */, AB1FA94927C62BC80063EF55 /* CommonMark in Frameworks */, AB17573D27675B1E00FD64E2 /* Colorful in Frameworks */, + 7E1F0E7F2ED6ABE4005C6249 /* SwiftUIBackports in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1590,6 +1592,7 @@ AB2EB9A1280251F600011A8A /* AlertKit */, AB2EB9A42802521700011A8A /* DeprecatedAPI */, EAE63E2029E2A6330048C601 /* SwiftyBeaver */, + 7E1F0E7E2ED6ABE4005C6249 /* SwiftUIBackports */, ); productName = EhPanda; productReference = ABC3C7542593696C00E0C11B /* EhPanda.app */; @@ -1668,6 +1671,7 @@ AB2EB9A0280251F600011A8A /* XCRemoteSwiftPackageReference "AlertKit" */, AB2EB9A32802521700011A8A /* XCRemoteSwiftPackageReference "DeprecatedAPI" */, EAE63E1F29E2A6330048C601 /* XCRemoteSwiftPackageReference "SwiftyBeaver" */, + 7E1F0E7D2ED6A647005C6249 /* XCRemoteSwiftPackageReference "iOS-Backports" */, ); productRefGroup = ABC3C7552593696C00E0C11B /* Products */; projectDirPath = ""; @@ -2088,10 +2092,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 157; - DEVELOPMENT_TEAM = 9SKQ7QTZ74; + DEVELOPMENT_TEAM = 3H9YQKVXRL; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ShareExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = ShareExtension; @@ -2102,9 +2106,9 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda.shareExtension; + PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda17.shareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ShareExtension_Dev; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -2116,10 +2120,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 157; - DEVELOPMENT_TEAM = 9SKQ7QTZ74; + DEVELOPMENT_TEAM = 3H9YQKVXRL; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ShareExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = ShareExtension; @@ -2130,9 +2134,9 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda.shareExtension; + PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda17.shareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ShareExtension_Dev; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -2267,22 +2271,23 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = EhPanda/EhPanda.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 157; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = 9SKQ7QTZ74; + DEVELOPMENT_TEAM = 3H9YQKVXRL; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = EhPanda/App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 26.0; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda; + PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda17; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = App_Dev; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -2296,22 +2301,23 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = EhPanda/EhPanda.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 157; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = 9SKQ7QTZ74; + DEVELOPMENT_TEAM = 3H9YQKVXRL; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = EhPanda/App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 26.0; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda; + PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda17; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = App_Dev; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -2334,7 +2340,7 @@ "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda.tests; + PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda17.tests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; @@ -2361,7 +2367,7 @@ "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda.tests; + PRODUCT_BUNDLE_IDENTIFIER = app.ehpanda17.tests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; @@ -2414,6 +2420,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 7E1F0E7D2ED6A647005C6249 /* XCRemoteSwiftPackageReference "iOS-Backports" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/superwall/iOS-Backports"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.5; + }; + }; AB17573B27675B1E00FD64E2 /* XCRemoteSwiftPackageReference "Colorful" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Co2333/Colorful"; @@ -2545,6 +2559,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 7E1F0E7E2ED6ABE4005C6249 /* SwiftUIBackports */ = { + isa = XCSwiftPackageProductDependency; + package = 7E1F0E7D2ED6A647005C6249 /* XCRemoteSwiftPackageReference "iOS-Backports" */; + productName = SwiftUIBackports; + }; AB17573C27675B1E00FD64E2 /* Colorful */ = { isa = XCSwiftPackageProductDependency; package = AB17573B27675B1E00FD64E2 /* XCRemoteSwiftPackageReference "Colorful" */; diff --git a/EhPanda.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/EhPanda.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a94821f1..086452be 100644 --- a/EhPanda.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/EhPanda.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "d2c86e73cf55b52b5883b87c93943d803ad451433deeaa7ca1c32e53b38a2565", + "originHash" : "2734a7a245f2f57f43d7599b6b26a3458d67cd63564b382fdd24f15dac3800c3", "pins" : [ { "identity" : "alertkit", @@ -46,6 +46,15 @@ "version" : "1.0.1" } }, + { + "identity" : "ios-backports", + "kind" : "remoteSourceControl", + "location" : "https://github.com/superwall/iOS-Backports", + "state" : { + "revision" : "1549293848bd2467dce4be298e0ad6f90af748dc", + "version" : "1.0.5" + } + }, { "identity" : "kanna", "kind" : "remoteSourceControl", diff --git a/EhPanda.xcodeproj/xcshareddata/xcschemes/EhPanda.xcscheme b/EhPanda.xcodeproj/xcshareddata/xcschemes/EhPanda.xcscheme index e791012e..428e7a2e 100644 --- a/EhPanda.xcodeproj/xcshareddata/xcschemes/EhPanda.xcscheme +++ b/EhPanda.xcodeproj/xcshareddata/xcschemes/EhPanda.xcscheme @@ -1,6 +1,6 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.8.0 + 2.9.0 CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLName - app.ehpanda + app.ehpanda17 CFBundleURLSchemes ehpanda diff --git a/EhPanda/App/de.lproj/Localizable.strings b/EhPanda/App/de.lproj/Localizable.strings index b505d8d4..43db93b8 100644 --- a/EhPanda/App/de.lproj/Localizable.strings +++ b/EhPanda/App/de.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "Content"; "quick_search_view.title.name" = "Name"; "quick_search_view.placeholder.optional" = "Optional"; +"quick_search_view.toolbar_item.button.confirm" = "Confirm"; // MARK: SettingView "setting_view.title.setting" = "Einstellungen"; @@ -337,6 +338,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "Kommentar abgeben"; "post_comment_view.title.edit_comment" = "Kommentar bearbeiten"; +"post_comment_view.button.cancel" = "Abbrechen"; +"post_comment_view.button.post" = "Senden"; // MARK: PreviewsView "previews_view.title.previews" = "Vorschau"; diff --git a/EhPanda/App/en.lproj/Localizable.strings b/EhPanda/App/en.lproj/Localizable.strings index a3ca9bac..bc56f1d1 100644 --- a/EhPanda/App/en.lproj/Localizable.strings +++ b/EhPanda/App/en.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "Content"; "quick_search_view.title.name" = "Name"; "quick_search_view.placeholder.optional" = "Optional"; +"quick_search_view.toolbar_item.button.confirm" = "Confirm"; // MARK: SettingView "setting_view.title.setting" = "Setting"; @@ -337,6 +338,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "Post comment"; "post_comment_view.title.edit_comment" = "Edit comment"; +"post_comment_view.button.cancel" = "Cancel"; +"post_comment_view.button.post" = "Post"; // MARK: PreviewsView "previews_view.title.previews" = "Previews"; diff --git a/EhPanda/App/ja.lproj/Localizable.strings b/EhPanda/App/ja.lproj/Localizable.strings index fa68dba1..362be102 100644 --- a/EhPanda/App/ja.lproj/Localizable.strings +++ b/EhPanda/App/ja.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "内容"; "quick_search_view.title.name" = "名前"; "quick_search_view.placeholder.optional" = "任意"; +"quick_search_view.toolbar_item.button.confirm" = "確認"; // MARK: SettingView "setting_view.title.setting" = "設定"; @@ -337,6 +338,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "コメントを書く"; "post_comment_view.title.edit_comment" = "コメントを編集"; +"post_comment_view.button.cancel" = "キャンセル"; +"post_comment_view.button.post" = "投稿"; // MARK: PreviewsView "previews_view.title.previews" = "プレビュー"; diff --git a/EhPanda/App/ko.lproj/Localizable.strings b/EhPanda/App/ko.lproj/Localizable.strings index b9266898..fe48df2e 100644 --- a/EhPanda/App/ko.lproj/Localizable.strings +++ b/EhPanda/App/ko.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "Content"; "quick_search_view.title.name" = "Name"; "quick_search_view.placeholder.optional" = "Optional"; +"quick_search_view.toolbar_item.button.confirm" = "確認"; // MARK: SettingView "setting_view.title.setting" = "설정"; @@ -337,6 +338,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "평가 남기기"; "post_comment_view.title.edit_comment" = "평가 수정"; +"post_comment_view.button.cancel" = "キャンセル"; +"post_comment_view.button.post" = "投稿"; // MARK: PreviewsView "previews_view.title.previews" = "미리보기"; diff --git a/EhPanda/App/zh-Hans.lproj/Localizable.strings b/EhPanda/App/zh-Hans.lproj/Localizable.strings index 19a4d6b0..b6ecccdf 100644 --- a/EhPanda/App/zh-Hans.lproj/Localizable.strings +++ b/EhPanda/App/zh-Hans.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "内容"; "quick_search_view.title.name" = "名称"; "quick_search_view.placeholder.optional" = "可选"; +"quick_search_view.toolbar_item.button.confirm" = "确认"; // MARK: SettingView "setting_view.title.setting" = "设置"; @@ -337,6 +338,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "发布评论"; "post_comment_view.title.edit_comment" = "编辑评论"; +"post_comment_view.button.cancel" = "取消"; +"post_comment_view.button.post" = "发布"; // MARK: PreviewsView "previews_view.title.previews" = "预览"; diff --git a/EhPanda/App/zh-Hant-HK.lproj/Localizable.strings b/EhPanda/App/zh-Hant-HK.lproj/Localizable.strings index 948d5eb2..c0d19029 100644 --- a/EhPanda/App/zh-Hant-HK.lproj/Localizable.strings +++ b/EhPanda/App/zh-Hant-HK.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "搜尋內容"; "quick_search_view.title.name" = "名稱"; "quick_search_view.placeholder.optional" = "(可選)"; +"quick_search_view.toolbar_item.button.confirm" = "確定"; // MARK: SettingView "setting_view.title.setting" = "設定"; @@ -337,6 +338,9 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "發表留言"; "post_comment_view.title.edit_comment" = "編輯留言"; +"post_comment_view.button.cancel" = "取消"; +"post_comment_view.button.post" = "發表"; + // MARK: PreviewsView "previews_view.title.previews" = "預覽"; diff --git a/EhPanda/App/zh-Hant-TW.lproj/Localizable.strings b/EhPanda/App/zh-Hant-TW.lproj/Localizable.strings index 0c4f150d..b2964734 100644 --- a/EhPanda/App/zh-Hant-TW.lproj/Localizable.strings +++ b/EhPanda/App/zh-Hant-TW.lproj/Localizable.strings @@ -147,6 +147,7 @@ "quick_search_view.title.content" = "搜尋內容"; "quick_search_view.title.name" = "名稱"; "quick_search_view.placeholder.optional" = "(可選)"; +"quick_search_view.toolbar_item.button.confirm" = "确认"; // MARK: SettingView "setting_view.title.setting" = "設定"; @@ -337,6 +338,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "發表留言"; "post_comment_view.title.edit_comment" = "編輯留言"; +"post_comment_view.button.cancel" = "取消"; +"post_comment_view.button.post" = "发布"; // MARK: PreviewsView "previews_view.title.previews" = "預覽"; diff --git a/EhPanda/App/zh-Hant.lproj/Localizable.strings b/EhPanda/App/zh-Hant.lproj/Localizable.strings index ad77e167..714e7ce8 100644 --- a/EhPanda/App/zh-Hant.lproj/Localizable.strings +++ b/EhPanda/App/zh-Hant.lproj/Localizable.strings @@ -337,6 +337,8 @@ // MARK: PostCommentView "post_comment_view.title.post_comment" = "發表留言"; "post_comment_view.title.edit_comment" = "編輯留言"; +"post_comment_view.button.cancel" = "取消"; +"post_comment_view.button.post" = "发布"; // MARK: PreviewsView "previews_view.title.previews" = "預覽"; diff --git a/EhPanda/View/Detail/Archives/ArchivesView.swift b/EhPanda/View/Detail/Archives/ArchivesView.swift index 828e681b..ca29d36d 100644 --- a/EhPanda/View/Detail/Archives/ArchivesView.swift +++ b/EhPanda/View/Detail/Archives/ArchivesView.swift @@ -5,6 +5,7 @@ import SwiftUI import ComposableArchitecture +import SwiftUIBackports struct ArchivesView: View { @Bindable private var store: StoreOf @@ -163,7 +164,7 @@ private struct HathArchiveGrid: View { } var body: some View { - VStack(spacing: 10) { + let bodyStack: some View = VStack(spacing: 10) { Text(archive.resolution.value) .font(.title3.bold()) @@ -178,14 +179,18 @@ private struct HathArchiveGrid: View { } .lineLimit(1) } - .foregroundColor(foregroundColor) - .frame(width: width, height: height) - .contentShape(.rect) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(borderColor, lineWidth: 1) - ) - .glassEffect(.clear.interactive(), in: .rect(cornerRadius: 10)) + .foregroundColor(foregroundColor) + .frame(width: width, height: height) + .contentShape(.rect) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(borderColor, lineWidth: 1) + ) + if #available(iOS 26.0, *) { + bodyStack.glassEffect(.clear.interactive(), in: .rect(cornerRadius: 10)) + } else { + bodyStack + } } } @@ -214,23 +219,39 @@ private struct DownloadButton: View { } var body: some View { - Text(L10n.Localizable.ArchivesView.Button.downloadToHathClient) - .font(.headline) - .foregroundStyle(textColor) - .frame(maxWidth: .infinity) - .frame(height: 50) - .background(backgroundColor) - .animation(.default, value: backgroundColor) - .clipShape(.rect(cornerRadius: 30)) - .glassEffect(.regular.interactive()) - .padding(paddingInsets) - .onTapGesture(perform: { if !isDisabled { action() }}) + if #available(iOS 26.0, *) { + Text(L10n.Localizable.ArchivesView.Button.downloadToHathClient) + .font(.headline) + .foregroundStyle(textColor) + .frame(maxWidth: .infinity) + .frame(height: 50) + .background(backgroundColor) + .animation(.default, value: backgroundColor) + .clipShape(.rect(cornerRadius: 30)) + .backport.glassEffect(.regularInteractive) + .padding(paddingInsets) + .onTapGesture(perform: { if !isDisabled { action() }}) + .onLongPressGesture( + minimumDuration: 0, + maximumDistance: 50, + pressing: { isPressing = $0 }, + perform: {} + ) + } else { + HStack { + Spacer() + Text(L10n.Localizable.ArchivesView.Button.downloadToHathClient) + .font(.headline).foregroundColor(textColor) + Spacer() + } + .frame(height: 50).background(backgroundColor) + .cornerRadius(30).padding(paddingInsets) + .onTapGesture { if !isDisabled { action() }} .onLongPressGesture( - minimumDuration: 0, - maximumDistance: 50, - pressing: { isPressing = $0 }, - perform: {} + minimumDuration: 0, maximumDistance: 50, + pressing: { isPressing = $0 }, perform: {} ) + } } } diff --git a/EhPanda/View/Detail/Components/PostCommentView.swift b/EhPanda/View/Detail/Components/PostCommentView.swift index f99e998d..999d4f81 100644 --- a/EhPanda/View/Detail/Components/PostCommentView.swift +++ b/EhPanda/View/Detail/Components/PostCommentView.swift @@ -42,11 +42,24 @@ struct PostCommentView: View { } .toolbar { ToolbarItem(placement: .cancellationAction) { - Button(role: .close, action: cancelAction) + if #available(iOS 26.0, *) { + Button(role: .close, action: cancelAction) + } else { + Button(action: cancelAction) { + Text(L10n.Localizable.PostCommentView.Button.cancel) + } + } } ToolbarItem(placement: .confirmationAction) { - Button(role: .confirm, action: postAction) + if #available(iOS 26.0, *) { + Button(role: .confirm, action: postAction) + .disabled(content.isEmpty) + } else { + Button(action: postAction) { + Text(L10n.Localizable.PostCommentView.Button.post) + } .disabled(content.isEmpty) + } } } .navigationBarTitleDisplayMode(.inline) diff --git a/EhPanda/View/Detail/DetailView.swift b/EhPanda/View/Detail/DetailView.swift index 21d5d72f..3d78a00a 100644 --- a/EhPanda/View/Detail/DetailView.swift +++ b/EhPanda/View/Detail/DetailView.swift @@ -7,6 +7,7 @@ import SwiftUI import Kingfisher import ComposableArchitecture import CommonMark +import SwiftUIBackports struct DetailView: View { @Bindable private var store: StoreOf @@ -361,7 +362,7 @@ private struct HeaderSection: View { Spacer() - ZStack { + let buttonsStack = ZStack { Button(action: unfavorAction) { Image(systemSymbol: .heartFill) } @@ -380,8 +381,13 @@ private struct HeaderSection: View { } .imageScale(.large) .foregroundStyle(.tint) - .buttonStyle(.glass(.regular.interactive())) .disabled(!CookieUtil.didLogin) + if #available(iOS 26.0, *) { + buttonsStack + .buttonStyle(.glass(.regular.interactive())) + } else { + buttonsStack + } Button(action: navigateReadingAction) { Text(L10n.Localizable.DetailView.Button.read) @@ -389,7 +395,7 @@ private struct HeaderSection: View { .foregroundColor(.white).padding(.vertical, -2) .padding(.horizontal, 2).lineLimit(1) } - .buttonStyle(.glassProminent) + .backport.glassProminentButtonStyle() .buttonBorderShape(.capsule) } .minimumScaleFactor(0.5) @@ -874,19 +880,32 @@ private struct CommentButton: View { var body: some View { let shape = RoundedRectangle(cornerRadius: 15) - Button(action: action) { - HStack { - Image(systemSymbol: .squareAndPencil) + if #available(iOS 26.0, *) { + Button(action: action) { + HStack { + Image(systemSymbol: .squareAndPencil) + + Text(L10n.Localizable.DetailView.Button.postComment) + .bold() + } + .padding() + .frame(maxWidth: .infinity) + .background(backgroundColor) + .clipShape(shape) + } + .glassEffect(.clear.interactive(), in: shape) + } else { + Button(action: action) { + HStack { + Spacer() + Image(systemSymbol: .squareAndPencil) + Text(L10n.Localizable.DetailView.Button.postComment).bold() + Spacer() - Text(L10n.Localizable.DetailView.Button.postComment) - .bold() + } + .padding().background(backgroundColor).cornerRadius(15) } - .padding() - .frame(maxWidth: .infinity) - .background(backgroundColor) - .clipShape(shape) } - .glassEffect(.clear.interactive(), in: shape) } } diff --git a/EhPanda/View/Reading/Support/AdvancedList.swift b/EhPanda/View/Reading/Support/AdvancedList.swift index 22eb2bcf..70de4c9c 100644 --- a/EhPanda/View/Reading/Support/AdvancedList.swift +++ b/EhPanda/View/Reading/Support/AdvancedList.swift @@ -44,15 +44,11 @@ where PageView: View, Element: Equatable, ID: Hashable, G: Gesture { .onAppear(perform: { tryScrollTo(id: pagerModel.index + 1, proxy: proxy) }) } .scrollPosition(id: $scrollPositionID, anchor: .center) - .onScrollPhaseChange { _, newValue in - if newValue == .idle, let index = scrollPositionID { - performingChanges = true - pagerModel.update(.new(index: index - 1)) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - performingChanges = false - } - } - } + .modifier(ScrollPhaseChangeModifier( + scrollPositionID: $scrollPositionID, + performingChanges: $performingChanges, + pagerModel: pagerModel + )) .onChange(of: pagerModel.index) { _, newValue in tryScrollTo(id: newValue + 1, proxy: proxy) } @@ -65,3 +61,44 @@ where PageView: View, Element: Equatable, ID: Hashable, G: Gesture { } } } + +// MARK: ScrollPhaseChangeModifier +private struct ScrollPhaseChangeModifier: ViewModifier { + @Binding var scrollPositionID: Int? + @Binding var performingChanges: Bool + let pagerModel: Page + @State private var lastScrollPositionID: Int? + + func body(content: Content) -> some View { + if #available(iOS 18.0, *) { + content + .onScrollPhaseChange { _, newValue in + if newValue == .idle, let index = scrollPositionID { + performingChanges = true + pagerModel.update(.new(index: index - 1)) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + performingChanges = false + } + } + } + } else { + content + .onChange(of: scrollPositionID) { _, newValue in + if let index = newValue, index != lastScrollPositionID { + lastScrollPositionID = index + // Use a small delay to detect when scrolling has stopped + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + // Check if scroll position is still the same (scrolling stopped) + if scrollPositionID == index { + performingChanges = true + pagerModel.update(.new(index: index - 1)) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + performingChanges = false + } + } + } + } + } + } + } +} diff --git a/EhPanda/View/Reading/Support/ControlPanel.swift b/EhPanda/View/Reading/Support/ControlPanel.swift index f0fcdf0b..0a536d0e 100644 --- a/EhPanda/View/Reading/Support/ControlPanel.swift +++ b/EhPanda/View/Reading/Support/ControlPanel.swift @@ -5,6 +5,7 @@ import SwiftUI import Kingfisher +import SwiftUIBackports // MARK: ControlPanel struct ControlPanel: View { @@ -117,103 +118,201 @@ private struct UpperPanel: View { } var body: some View { - HStack { - HStack(spacing: 16) { - Button(action: dismissAction) { - Image(systemSymbol: .xmark) + if #available(iOS 26.0, *) { + HStack { + HStack(spacing: 16) { + Button(action: dismissAction) { + Image(systemSymbol: .xmark) + .font(.title2) + .frame(width: 44, height: 44) + } + .glassEffect(.regular.interactive()) + + Text(title) .font(.title2) - .frame(width: 44, height: 44) + .fontWeight(.bold) + .monospacedDigit() + .lineLimit(1) + .padding(.horizontal, 16) + .padding(.vertical, 8) + .glassEffect(.regular.interactive()) } - .glassEffect(.regular.interactive()) - Text(title) - .font(.title2) - .fontWeight(.bold) - .monospacedDigit() - .lineLimit(1) - .padding(.horizontal, 16) - .padding(.vertical, 8) - .glassEffect(.regular.interactive()) - } + Spacer() - Spacer() + HStack(spacing: 20) { + Button { + enablesLiveText.toggle() + } label: { + Image(systemSymbol: .viewfinderCircle) + .symbolVariant(enablesLiveText ? .fill : .none) + .font(.title2) + } - HStack(spacing: 20) { - Button { - enablesLiveText.toggle() - } label: { - Image(systemSymbol: .viewfinderCircle) - .symbolVariant(enablesLiveText ? .fill : .none) - .font(.title2) - } + if DeviceUtil.isLandscape && setting.readingDirection != .vertical { + Menu { + Button { + setting.enablesDualPageMode.toggle() - if DeviceUtil.isLandscape && setting.readingDirection != .vertical { - Menu { - Button { - setting.enablesDualPageMode.toggle() - } label: { - Text(L10n.Localizable.ReadingView.ToolbarItem.Title.dualPageMode) - if setting.enablesDualPageMode { - Image(systemSymbol: .checkmark) + + + } label: { + Text(L10n.Localizable.ReadingView.ToolbarItem.Title.dualPageMode) + if setting.enablesDualPageMode { + Image(systemSymbol: .checkmark) + } } - } - Button { - setting.exceptCover.toggle() + Button { + setting.exceptCover.toggle() + } label: { + Text(L10n.Localizable.ReadingView.ToolbarItem.Title.exceptTheCover) + if setting.exceptCover { + Image(systemSymbol: .checkmark) + + + + + + } + } + .disabled(!setting.enablesDualPageMode) } label: { - Text(L10n.Localizable.ReadingView.ToolbarItem.Title.exceptTheCover) - if setting.exceptCover { - Image(systemSymbol: .checkmark) + Image(systemSymbol: .rectangleSplit2x1) + .symbolVariant(setting.enablesDualPageMode ? .fill : .none) + .font(.title2) + } + } + + Menu { + Text(L10n.Localizable.ReadingView.ToolbarItem.Title.autoPlay).foregroundColor(.secondary) + ForEach(AutoPlayPolicy.allCases) { policy in + Button { + autoPlayPolicy = policy + } label: { + Text(policy.value) + if autoPlayPolicy == policy { + Image(systemSymbol: .checkmark) + } } } - .disabled(!setting.enablesDualPageMode) } label: { - Image(systemSymbol: .rectangleSplit2x1) - .symbolVariant(setting.enablesDualPageMode ? .fill : .none) + Image(systemSymbol: .timer) .font(.title2) } + .buttonStyle(.borderless) + + ToolbarFeaturesMenu { + Button(action: retryAllFailedImagesAction) { + Image(systemSymbol: .exclamationmarkArrowTriangle2Circlepath) + Text(L10n.Localizable.ReadingView.ToolbarItem.Button.retryAllFailedImages) + } + Button(action: reloadAllImagesAction) { + Image(systemSymbol: .arrowCounterclockwise) + Text(L10n.Localizable.ReadingView.ToolbarItem.Button.reloadAllImages) + } + Button(action: navigateSettingAction) { + Image(systemSymbol: .gear) + Text(L10n.Localizable.ReadingView.ToolbarItem.Button.readingSetting) + } + } + .buttonStyle(.borderless) + .font(.title2) } + .padding(.vertical, 12) + .padding(.horizontal, 20) + .glassEffect(.regular.interactive()) + } + .foregroundStyle(.primary) + .padding(.horizontal, 20) + } else { + ZStack { + HStack { + Button(action: dismissAction) { + Image(systemSymbol: .xmark) - Menu { - Text(L10n.Localizable.ReadingView.ToolbarItem.Title.autoPlay).foregroundColor(.secondary) - ForEach(AutoPlayPolicy.allCases) { policy in + + } + .font(.title2).padding(.leading, 20) + Spacer() + Slider(value: .constant(0)).opacity(0) + Spacer() + HStack(spacing: 20) { Button { - autoPlayPolicy = policy + enablesLiveText.toggle() } label: { - Text(policy.value) - if autoPlayPolicy == policy { - Image(systemSymbol: .checkmark) + Image(systemSymbol: .viewfinderCircle) + .symbolVariant(enablesLiveText ? .fill : .none) + } + if DeviceUtil.isLandscape && setting.readingDirection != .vertical { + Menu { + Button { + setting.enablesDualPageMode.toggle() + } label: { + Text(L10n.Localizable.ReadingView.ToolbarItem.Title.dualPageMode) + if setting.enablesDualPageMode { + Image(systemSymbol: .checkmark) + } + } + Button { + setting.exceptCover.toggle() + } label: { + Text(L10n.Localizable.ReadingView.ToolbarItem.Title.exceptTheCover) + if setting.exceptCover { + Image(systemSymbol: .checkmark) + } + } + .disabled(!setting.enablesDualPageMode) + } label: { + Image(systemSymbol: .rectangleSplit2x1) + .symbolVariant(setting.enablesDualPageMode ? .fill : .none) + + } } - } - } label: { - Image(systemSymbol: .timer) - .font(.title2) - } - .buttonStyle(.borderless) + Menu { + Text(L10n.Localizable.ReadingView.ToolbarItem.Title.autoPlay).foregroundColor(.secondary) + ForEach(AutoPlayPolicy.allCases) { policy in + Button { + autoPlayPolicy = policy + } label: { + Text(policy.value) + if autoPlayPolicy == policy { + Image(systemSymbol: .checkmark) + } + } + } + + } label: { + Image(systemSymbol: .timer) + + + } + ToolbarFeaturesMenu { + Button(action: retryAllFailedImagesAction) { + Image(systemSymbol: .exclamationmarkArrowTriangle2Circlepath) + Text(L10n.Localizable.ReadingView.ToolbarItem.Button.retryAllFailedImages) + } + Button(action: reloadAllImagesAction) { + Image(systemSymbol: .arrowCounterclockwise) + Text(L10n.Localizable.ReadingView.ToolbarItem.Button.reloadAllImages) + } + Button(action: navigateSettingAction) { + Image(systemSymbol: .gear) + Text(L10n.Localizable.ReadingView.ToolbarItem.Button.readingSetting) + } + } + .padding(.trailing, 20) - ToolbarFeaturesMenu { - Button(action: retryAllFailedImagesAction) { - Image(systemSymbol: .exclamationmarkArrowTriangle2Circlepath) - Text(L10n.Localizable.ReadingView.ToolbarItem.Button.retryAllFailedImages) - } - Button(action: reloadAllImagesAction) { - Image(systemSymbol: .arrowCounterclockwise) - Text(L10n.Localizable.ReadingView.ToolbarItem.Button.reloadAllImages) - } - Button(action: navigateSettingAction) { - Image(systemSymbol: .gear) - Text(L10n.Localizable.ReadingView.ToolbarItem.Button.readingSetting) } + + .font(.title2) } - .buttonStyle(.borderless) - .font(.title2) + Text(title).bold().lineLimit(1).padding() + + } - .padding(.vertical, 12) - .padding(.horizontal, 20) - .glassEffect(.regular.interactive()) + .background(.thinMaterial) } - .foregroundStyle(.primary) - .padding(.horizontal, 20) } } @@ -246,17 +345,30 @@ private struct LowerPanel: View { var body: some View { VStack(spacing: 30) { - Button(action: dismissAction) { - Image(systemSymbol: .xmark) + let dismissButton = Button(action: dismissAction) { + let image = Image(systemSymbol: .xmark) .foregroundColor(.primary) - .font(.title2) - .frame(width: 44, height: 44) + if #available(iOS 26.0, *) { + image + .font(.title2) + .frame(width: 44, height: 44) + } else { + image + .padding() + .background(.ultraThinMaterial) + .cornerRadius(.infinity) + } } - .glassEffect(.regular.interactive()) .gesture(dismissGesture) .opacity(showsSliderPreview ? 0 : 1) + if #available(iOS 26.0, *) { + dismissButton.glassEffect(.regular.interactive()) + } else { + dismissButton + } + - VStack(spacing: 0) { + let sliderPanel = VStack(spacing: 0) { SliderPreivew( showsSliderPreview: $showsSliderPreview, sliderValue: $sliderValue, @@ -272,17 +384,23 @@ private struct LowerPanel: View { .font(.caption) .padding() - Slider( + let slider = Slider( value: $sliderValue, in: range, onEditingChanged: { if !$0 { showsSliderPreview = false } } ) - .frame(width: DeviceUtil.windowW * 0.6) .rotationEffect(.init(degrees: isReversed ? 180 : 0)) - .simultaneousGesture( - LongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity) - .onChanged({ if $0 { showsSliderPreview = true } }) - ) + if #available(iOS 26.0, *) { + slider + .simultaneousGesture( + LongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity) + .onChanged({ if $0 { showsSliderPreview = true } }) + ) + .frame(width: DeviceUtil.windowW * 0.6) + } else { + slider + .padding(.horizontal).padding(.bottom) + } Text(isReversed ? "\(Int(range.lowerBound))" : "\(Int(range.upperBound))") .fontWeight(.medium) @@ -290,8 +408,13 @@ private struct LowerPanel: View { .padding() } } - .glassEffect(in: .rect(cornerRadius: 16)) - .padding(.horizontal, SliderPreivew.outerPadding) + if #available(iOS 26.0, *) { + sliderPanel + .glassEffect(in: .rect(cornerRadius: 16)) + .padding(.horizontal, SliderPreivew.outerPadding) + } else { + sliderPanel.background(.thinMaterial) + } } } } @@ -324,7 +447,7 @@ private struct SliderPreivew: View { } var body: some View { - HStack(spacing: previewSpacing) { + let previewStack = HStack(spacing: previewSpacing) { ForEach(previewsIndices, id: \.self) { index in let (url, modifier) = PreviewResolver.getPreviewConfigs(originalURL: previewURLs[index]) VStack { @@ -350,8 +473,13 @@ private struct SliderPreivew: View { } .opacity(showsSliderPreview ? 1 : 0) .padding(.vertical, verticalPadding) - .padding(.horizontal, horizontalPadding) .frame(height: showsSliderPreview ? previewHeight + verticalPadding * 2 : 0) + if #available(iOS 26.0, *) { + previewStack + .padding(.horizontal, horizontalPadding) + } else { + previewStack + } } } @@ -380,7 +508,10 @@ private extension SliderPreivew { var previewWidth: CGFloat { guard previewsCount > 0 else { return 0 } let count = CGFloat(previewsCount) - let spacing = (count + 1) * previewSpacing + horizontalPadding * 2 + Self.outerPadding * 2 + var spacing = (count + 1) * previewSpacing + if #available(iOS 26.0, *) { + spacing = spacing + horizontalPadding * 2 + Self.outerPadding * 2 + } return (DeviceUtil.windowW - spacing) / count } func checkIndex(_ index: Int) -> Bool { diff --git a/EhPanda/View/Search/SearchRootView.swift b/EhPanda/View/Search/SearchRootView.swift index e2cd9eb3..4964534c 100644 --- a/EhPanda/View/Search/SearchRootView.swift +++ b/EhPanda/View/Search/SearchRootView.swift @@ -26,44 +26,36 @@ struct SearchRootView: View { var body: some View { NavigationView { - let content = - ScrollView(showsIndicators: false) { - SuggestionsPanel( - historyKeywords: store.historyKeywords.reversed(), - historyGalleries: store.historyGalleries, - quickSearchWords: store.quickSearchWords, - navigateGalleryAction: { store.send(.setNavigation(.detail($0))) }, - navigateQuickSearchAction: { store.send(.setNavigation(.quickSearch())) }, - searchKeywordAction: { keyword in - store.send(.setKeyword(keyword)) - store.send(.setNavigation(.search)) - }, - removeKeywordAction: { store.send(.removeHistoryKeyword($0)) } - ) + if DeviceUtil.isPad { + baseContent + .sheet(item: $store.route.sending(\.setNavigation).detail, id: \.self) { gid in + detailSheetView(gid: gid) + } + } else { + if store.historyKeywords.isEmpty && store.historyGalleries.isEmpty { + if #available(iOS 26.0, *) { + baseContent.navigationSubtitle(Text(" ")) + } else { + baseContent.navigationTitle(Text(" ")) + } + } else { + baseContent + } } + } + } + + private var baseContent: some View { + scrollViewContent .sheet(item: $store.route.sending(\.setNavigation).filters) { _ in - FiltersView(store: store.scope(state: \.filtersState, action: \.filters)) - .autoBlur(radius: blurRadius).environment(\.inSheet, true) + filtersSheetView } .sheet(item: $store.route.sending(\.setNavigation).quickSearch) { _ in - QuickSearchView( - store: store.scope(state: \.quickSearchState, action: \.quickSearch) - ) { keyword in - store.send(.setNavigation(nil)) - store.send(.setKeyword(keyword)) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - store.send(.setNavigation(.search)) - } - } - .accentColor(setting.accentColor) - .autoBlur(radius: blurRadius) + quickSearchSheetView } .searchable(text: $store.keyword) .searchSuggestions { - TagSuggestionView( - keyword: $store.keyword, translations: tagTranslator.translations, - showsImages: setting.showsImagesInTags, isEnabled: setting.showsTagsSearchSuggestion - ) + searchSuggestionsView } .onSubmit(of: .search) { store.send(.setNavigation(.search)) @@ -75,32 +67,68 @@ struct SearchRootView: View { .background(navigationLinks) .toolbar(content: toolbar) .navigationTitle(L10n.Localizable.SearchView.Title.search) + } - if DeviceUtil.isPad { - content - .sheet(item: $store.route.sending(\.setNavigation).detail, id: \.self) { gid in - NavigationView { - DetailView( - store: store.scope(state: \.detailState.wrappedValue!, action: \.detail), - gid: gid, - user: user, - setting: $setting, - blurRadius: blurRadius, - tagTranslator: tagTranslator - ) - } - .autoBlur(radius: blurRadius).environment(\.inSheet, true).navigationViewStyle(.stack) - } - } else { - // Workaround: Prevent the title disappearing issue. - if store.historyKeywords.isEmpty && store.historyGalleries.isEmpty { - content - .navigationSubtitle(Text(" ")) - } else { - content - } + private var scrollViewContent: some View { + ScrollView(showsIndicators: false) { + SuggestionsPanel( + historyKeywords: store.historyKeywords.reversed(), + historyGalleries: store.historyGalleries, + quickSearchWords: store.quickSearchWords, + navigateGalleryAction: { store.send(.setNavigation(.detail($0))) }, + navigateQuickSearchAction: { store.send(.setNavigation(.quickSearch())) }, + searchKeywordAction: { keyword in + store.send(.setKeyword(keyword)) + store.send(.setNavigation(.search)) + }, + removeKeywordAction: { store.send(.removeHistoryKeyword($0)) } + ) + } + } + + private var filtersSheetView: some View { + FiltersView(store: store.scope(state: \.filtersState, action: \.filters)) + .autoBlur(radius: blurRadius) + .environment(\.inSheet, true) + } + + private var quickSearchSheetView: some View { + QuickSearchView( + store: store.scope(state: \.quickSearchState, action: \.quickSearch) + ) { keyword in + store.send(.setNavigation(nil)) + store.send(.setKeyword(keyword)) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + store.send(.setNavigation(.search)) } } + .accentColor(setting.accentColor) + .autoBlur(radius: blurRadius) + } + + private var searchSuggestionsView: some View { + TagSuggestionView( + keyword: $store.keyword, + translations: tagTranslator.translations, + showsImages: setting.showsImagesInTags, + isEnabled: setting.showsTagsSearchSuggestion + ) + } + + private func detailSheetView(gid: String) -> some View { + NavigationView { + DetailView( + store: store.scope(state: \.detailState.wrappedValue!, action: \.detail), + gid: gid, + user: user, + setting: $setting, + blurRadius: blurRadius, + tagTranslator: tagTranslator + ) + } + .autoBlur(radius: blurRadius) + .environment(\.inSheet, true) + .navigationViewStyle(.stack) } private func toolbar() -> some ToolbarContent { diff --git a/EhPanda/View/Search/Support/QuickSearchView.swift b/EhPanda/View/Search/Support/QuickSearchView.swift index 5c60c160..8e03621c 100644 --- a/EhPanda/View/Search/Support/QuickSearchView.swift +++ b/EhPanda/View/Search/Support/QuickSearchView.swift @@ -187,7 +187,13 @@ extension QuickSearchView { private func toolbar() -> some ToolbarContent { CustomToolbarItem { - Button(role: .confirm, action: confirmAction) + if #available(iOS 26.0, *) { + Button(role: .confirm, action: confirmAction) + } else { + Button(action: confirmAction) { + Text(L10n.Localizable.QuickSearchView.ToolbarItem.Button.confirm).bold() + } + } } } } diff --git a/EhPanda/View/Setting/Components/AboutView.swift b/EhPanda/View/Setting/Components/AboutView.swift index 9b6c91ec..530b6f02 100644 --- a/EhPanda/View/Setting/Components/AboutView.swift +++ b/EhPanda/View/Setting/Components/AboutView.swift @@ -14,6 +14,17 @@ struct AboutView: View { .joined(separator: " ") } + private var versionInfoView: some View { + VStack(alignment: .leading) { + Text(L10n.Constant.App.copyright) + Text(version) + } + .foregroundStyle(.gray) + .font(.caption2.bold()) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.vertical, 4) + } + var body: some View { Form { Section { @@ -44,15 +55,14 @@ struct AboutView: View { } .navigationTitle(L10n.Localizable.AboutView.Title.ehPanda) .toolbar { - ToolbarItem(placement: .largeSubtitle) { - VStack(alignment: .leading) { - Text(L10n.Constant.App.copyright) - Text(version) + if #available(iOS 26.0, *) { + ToolbarItem(placement: .largeSubtitle) { + versionInfoView + } + } else { + ToolbarItem(placement: .principal) { + versionInfoView } - .foregroundStyle(.gray) - .font(.caption2.bold()) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.vertical, 4) } } } diff --git a/EhPanda/View/Setting/Components/LaboratorySettingView.swift b/EhPanda/View/Setting/Components/LaboratorySettingView.swift index 15a75877..d69404f9 100644 --- a/EhPanda/View/Setting/Components/LaboratorySettingView.swift +++ b/EhPanda/View/Setting/Components/LaboratorySettingView.swift @@ -5,6 +5,7 @@ import SwiftUI import SFSafeSymbols +import SwiftUIBackports struct LaboratorySettingView: View { @Binding private var bypassesSNIFiltering: Bool @@ -52,24 +53,42 @@ struct LaboratoryCell: View { } var body: some View { - HStack { - Image(systemSymbol: symbol) - - Text(title) - .bold() + if #available(iOS 26.0, *) { + HStack { + Image(systemSymbol: symbol) + Text(title) + .bold() + } + .foregroundStyle(contentColor) + .font(.title2) + .frame(maxWidth: .infinity) + .contentShape(.rect) + .onTapGesture(perform: { isOn.toggle() }) + .minimumScaleFactor(0.75) + .padding(.vertical, 20) + .lineLimit(1) + .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 15)) + .animation(.default, value: isOn) + } else { + HStack { + Spacer() + Group { + Image(systemSymbol: symbol) + Text(title).bold() + } + .foregroundColor(contentColor) + .font(.title2) + Spacer() + } + .contentShape(.rect) + .onTapGesture(perform: { isOn.toggle() }) + .minimumScaleFactor(0.75) + .padding(.vertical, 20) + .background(bgColor) + .cornerRadius(15) + .lineLimit(1) + .animation(.default, value: isOn) } - .foregroundStyle(contentColor) - .font(.title2) - .frame(maxWidth: .infinity) - .contentShape(.rect) - .onTapGesture(perform: { isOn.toggle() }) - .minimumScaleFactor(0.75) - .padding(.vertical, 20) - .background(bgColor) - .cornerRadius(15) - .lineLimit(1) - .animation(.default, value: isOn) - .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 15)) } } diff --git a/EhPanda/View/Setting/Login/LoginView.swift b/EhPanda/View/Setting/Login/LoginView.swift index 7825edb9..0a9245d5 100644 --- a/EhPanda/View/Setting/Login/LoginView.swift +++ b/EhPanda/View/Setting/Login/LoginView.swift @@ -5,6 +5,7 @@ import SwiftUI import ComposableArchitecture +import SwiftUIBackports struct LoginView: View { @Bindable private var store: StoreOf @@ -47,24 +48,36 @@ struct LoginView: View { } .padding(.horizontal, proxy.size.width * 0.2) - Button { + let button = Button { store.send(.login) } label: { - Image(systemSymbol: .chevronForward) + let image = Image(systemSymbol: .chevronForward) .padding() - .clipShape(.circle) + if #available(iOS 26.0, *) { + image + .clipShape(.circle) + } else { + image + } } .overlay { ProgressView() .tint(nil) .opacity(store.loginState == .loading ? 1 : 0) } - .font(.title) .foregroundStyle(store.loginButtonColor) .disabled(store.loginButtonDisabled) - .glassEffect(.regular.interactive(), in: .circle) - .clipShape(.circle) .padding(.top, 30) + if #available(iOS 26.0, *) { + button + .font(.title) + .glassEffect(.regular.interactive(), in: .circle) + .clipShape(.circle) + } else { + button + .imageScale(.large) + .font(.largeTitle) + } } } } @@ -121,13 +134,17 @@ private struct LoginTextField: View { self.isPassword = isPassword } + private var backgroundColor: Color { + colorScheme == .light ? Color(.systemGray6) : Color(.systemGray5) + } + var body: some View { VStack(alignment: .leading, spacing: 8) { Text(description) .font(.caption) .foregroundStyle(.secondary) - Group { + let loginFields = Group { if isPassword { SecureField("", text: $text) } else { @@ -141,7 +158,14 @@ private struct LoginTextField: View { .disableAutocorrection(true) .keyboardType(isPassword ? .asciiCapable : .default) .padding(10) - .glassEffect(.regular.tint(Color(.systemGray5)), in: .rect(cornerRadius: 8)) + + if #available(iOS 26.0, *) { + loginFields + .glassEffect(.regular.tint(Color(.systemGray5)), in: .rect(cornerRadius: 8)) + } else { + loginFields + .background(backgroundColor.opacity(0.75).cornerRadius(8)) + } } } } diff --git a/EhPanda/View/Support/Components/AlertView.swift b/EhPanda/View/Support/Components/AlertView.swift index 06481449..d03ec19d 100644 --- a/EhPanda/View/Support/Components/AlertView.swift +++ b/EhPanda/View/Support/Components/AlertView.swift @@ -5,6 +5,7 @@ import SwiftUI import SFSafeSymbols +import SwiftUIBackports struct LoadingView: View { private let title: String @@ -126,7 +127,7 @@ struct AlertViewButton: View { .textCase(.uppercase) } .buttonBorderShape(.capsule) - .buttonStyle(.glass) + .backport.glassButtonStyle() } } diff --git a/README.md b/README.md index ca0249a8..2ccb9b20 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ GitHub Readme: [README.{lang}.md](/READMEs) https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/blob/main/src/main.js) ## Installation -1. Get the ipa file from [Releases](https://github.com/EhPanda-Team/EhPanda/releases). +1. Get the ipa file from [Releases](https://github.com/AokazeNozomi/EhPanda17/releases). 2. Use some software like [AltStore](https://altstore.io) to install the ipa file on your device. ## System Requirements -This app requires iOS / iPadOS 26.0 or later. +This app requires iOS / iPadOS 17.0 or later. ## Content & Copyright The content in this application is derived from E-Hentai, which is user-generated content. diff --git a/READMEs/README.chs.md b/READMEs/README.chs.md index 298fee04..db817f44 100644 --- a/READMEs/README.chs.md +++ b/READMEs/README.chs.md @@ -25,11 +25,11 @@ GitHub Readme: [README.{lang}.md](/READMEs) https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/blob/main/src/main.js) ## 安装步骤 -1. 在 [Releases](https://github.com/EhPanda-Team/EhPanda/releases) 取得 ipa 文件。 +1. 在 [Releases](https://github.com/AokazeNozomi/EhPanda17/releases) 取得 ipa 文件。 2. 使用 [AltStore](https://altstore.io) 这类软件将 ipa 文件安装到你的设备。 ## 系统要求 -请确保你的设备系统为 iOS / iPadOS 26.0 以上。 +请确保你的设备系统为 iOS / iPadOS 17.0 以上。 ## 内容及其著作权 本应用程序中的内容均来自 E-Hentai,而 E-Hentai 的内容均为用户生成内容。 @@ -43,3 +43,4 @@ https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/b ## 截图 https://ehpanda.app + diff --git a/READMEs/README.cht.md b/READMEs/README.cht.md index d9101b2d..329f0ec6 100644 --- a/READMEs/README.cht.md +++ b/READMEs/README.cht.md @@ -25,11 +25,11 @@ GitHub Readme: [README.{lang}.md](/READMEs) https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/blob/main/src/main.js) ## 安裝步驟 -1. 在 [Releases](https://github.com/EhPanda-Team/EhPanda/releases) 取得 ipa 文件。 +1. 在 [Releases](https://github.com/AokazeNozomi/EhPanda17/releases) 取得 ipa 文件。 2. 使用 [AltStore](https://altstore.io) 這類軟件將 ipa 文件安裝到你的裝置。 ## 系統需求 -須使用 iOS / iPadOS 26.0 或以上版本。 +須使用 iOS / iPadOS 17.0 或以上版本。 ## 內容及其著作權 本應用程式內的內容均來自 E-Hentai,而 E-Hentai 的內容均為用戶生成內容。 diff --git a/READMEs/README.de.md b/READMEs/README.de.md index e337f0e9..eeed1d7a 100644 --- a/READMEs/README.de.md +++ b/READMEs/README.de.md @@ -25,11 +25,11 @@ GitHub Readme: [README.{lang}.md](/READMEs) https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/blob/main/src/main.js) ## Installation -1. Lade die IPA-Datei hier herunter: [Releases](https://github.com/EhPanda-Team/EhPanda/releases). +1. Lade die IPA-Datei hier herunter: [Releases](https://github.com/AokazeNozomi/EhPanda17/releases). 2. Nutze eine Programm zur Installation von nicht im Appstore gelisteten Dateien wie z.B. [AltStore](https://altstore.io) um die IPA-Datei zu installieren. ## Systemanforderungen -Diese App erfordert iOS / iPadOS 26.0 oder neuer. +Diese App erfordert iOS / iPadOS 17.0 oder neuer. ## Inhalte & Copyright Der Inhalt der von dieser App verwaltet wird, wird von E-Hentai geladen. Hierbei handelt es sich um von anderen Nutzern generierten Inhalt. diff --git a/READMEs/README.jpn.md b/READMEs/README.jpn.md index 64d6a06a..12a8c3fb 100644 --- a/READMEs/README.jpn.md +++ b/READMEs/README.jpn.md @@ -25,11 +25,11 @@ GitHub Readme: [README.{lang}.md](/READMEs) https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/blob/main/src/main.js) ## インストール手順 -1. [Releases](https://github.com/EhPanda-Team/EhPanda/releases) から ipa ファイルを取得。 +1. [Releases](https://github.com/AokazeNozomi/EhPanda17/releases) から ipa ファイルを取得。 2. [AltStore](https://altstore.io) とかで ipa ファイルをデバイスにインストール。 ## 必要システム構成 -iOS・iPadOS 26.0 以上が必要です。 +iOS・iPadOS 17.0 以上が必要です。 ## コンテンツとその著作権 本アプリの内容はすべて E-Hentai 由来のもので、E-Hentai の内容もまたすべてユーザー生成コンテンツです。 @@ -43,3 +43,4 @@ iOS・iPadOS 26.0 以上が必要です。 ## スクリーンショット https://ehpanda.app + diff --git a/READMEs/README.ko.md b/READMEs/README.ko.md index 540177a5..19a70e4c 100644 --- a/READMEs/README.ko.md +++ b/READMEs/README.ko.md @@ -25,11 +25,11 @@ GitHub Readme: [README.{lang}.md](/READMEs) https://ehpanda.app: [main.js](https://github.com/EhPanda-Team/ehpanda-website/blob/main/src/main.js) ## 다운로드 -1. [Releases](https://github.com/EhPanda-Team/EhPanda/releases)에서 ipa 파일을 다운로드 받으세요. +1. [Releases](https://github.com/AokazeNozomi/EhPanda17/releases)에서 ipa 파일을 다운로드 받으세요. 2. [AltStore](https://altstore.io)를 사용해서 ipa 파일을 설치할 수 있습니다. ## 시스템 요구 사항 -iOS / iPadOS 버전이 26.0 이상인지 확인해주세요. +iOS / iPadOS 버전이 17.0 이상인지 확인해주세요. ## 컨텐츠와 저작권 이 앱의 내용은 E-Hentai을 통해 제공되고, E-Hentai의 모든 내용은 앱 이용자가 만듭니다. @@ -43,3 +43,4 @@ iOS / iPadOS 버전이 26.0 이상인지 확인해주세요. ## 화면캡쳐 https://ehpanda.app + From 00f2d5452a599574b529eabb30ed52a8812839d4 Mon Sep 17 00:00:00 2001 From: AokazeNozomi <272103650+AokazeNozomi@users.noreply.github.com> Date: Sat, 4 Apr 2026 00:00:08 -0500 Subject: [PATCH 2/2] Change to trigger deploy via PR --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ccb9b20..585352fe 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@

## 📢 Translations Wanted 📢 -Please submit a pull request if you want to help with translation. + +Please submit a pull request if you want to help with translation App Strings: [{lang}.lproj](/EhPanda/App)