diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index e3922c399..000000000
--- a/.eslintignore
+++ /dev/null
@@ -1,3 +0,0 @@
-src/plugins/types/filterTypes.ts
-src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx
-# These two files cause the @typescript-eslint/no-unused-vars rule to fail
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index a39a0051c..000000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,30 +0,0 @@
-module.exports = {
- root: true,
- extends: ['@react-native'],
- overrides: [
- {
- // Test files only
- plugins: ['jest'],
- files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
- extends: ['plugin:testing-library/react', 'plugin:jest/recommended'],
- },
- {
- files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
- rules: {
- 'no-shadow': 'off',
- 'no-undef': 'off',
- 'no-console': 'error',
- '@typescript-eslint/no-shadow': 'warn',
- 'react-hooks/exhaustive-deps': 'warn',
- 'curly': ['error', 'multi-line', 'consistent'],
- 'no-useless-return': 'error',
- 'block-scoped-var': 'error',
- 'no-var': 'error',
- 'prefer-const': 'error',
- 'no-dupe-else-if': 'error',
- 'no-duplicate-imports': 'error',
- '@react-native/no-deep-imports': 0,
- },
- },
- ],
-};
diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml
index 6bde773fa..a9008bbed 100644
--- a/.github/ISSUE_TEMPLATE/report_issue.yml
+++ b/.github/ISSUE_TEMPLATE/report_issue.yml
@@ -45,7 +45,7 @@ body:
label: LNReader version
description: You can find your LNReader version in **More → About**.
placeholder: |
- Example: "2.0.3"
+ Example: "2.1.2"
validations:
required: true
@@ -88,7 +88,7 @@ body:
required: true
- label: If this is an issue with a plugin, I should be opening an issue in the [plugins repository](https://github.com/lnreader/lnreader-plugins/issues/new/choose).
required: true
- - label: I have updated the app to version **[2.0.3](https://github.com/lnreader/lnreader/releases/latest)**.
+ - label: I have updated the app to version **[2.1.2](https://github.com/lnreader/lnreader/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true
diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml
index 888c2aa26..7047645b5 100644
--- a/.github/ISSUE_TEMPLATE/request_feature.yml
+++ b/.github/ISSUE_TEMPLATE/request_feature.yml
@@ -34,7 +34,7 @@ body:
required: true
- label: If this is an issue with a source, I should be opening an issue in the [sources repository](https://github.com/lnreader/lnreader-sources/issues/new/choose).
required: true
- - label: I have updated the app to version **[2.0.3](https://github.com/lnreader/lnreader/releases/latest)**.
+ - label: I have updated the app to version **[2.1.2](https://github.com/lnreader/lnreader/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index dd13ed7c4..4c9ae11ae 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,94 +1,208 @@
-name: Build
+name: Build Preview
+
on:
- push:
- branches:
- - master
+ schedule:
+ - cron: '30 0 * * *'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
+permissions:
+ actions: read
+ contents: read
+
+env:
+ CI: 'true'
+ EXPO_NO_TELEMETRY: '1'
+ NODE_ENV: 'production'
+
jobs:
+ check-preview:
+ name: Check Preview
+ runs-on: ubuntu-24.04
+ outputs:
+ should-build: ${{ steps.preview.outputs.should-build }}
+
+ steps:
+ - name: Check For Existing Preview
+ id: preview
+ uses: actions/github-script@v8
+ with:
+ script: |
+ if (context.eventName === 'workflow_dispatch') {
+ core.notice('Manual dispatch requested; building a new preview.')
+ core.setOutput('should-build', 'true')
+ return
+ }
+
+ const shortSha = context.sha.slice(0, 8)
+ const artifactName = `LNReader-${shortSha}`
+ const { data: { artifacts } } = await github.rest.actions.listArtifactsForRepo(
+ {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: artifactName,
+ per_page: 100,
+ },
+ )
+ const previewExists = artifacts.some(
+ artifact =>
+ !artifact.expired &&
+ artifact.workflow_run?.head_sha === context.sha,
+ )
+
+ core.notice(
+ previewExists
+ ? `A preview artifact already exists for ${shortSha}.`
+ : `No preview artifact exists for ${shortSha}; starting a build.`,
+ )
+ core.setOutput('should-build', String(!previewExists))
+
build-android:
- name: Build App
- runs-on: ubuntu-latest
+ name: APK
+ needs: check-preview
+ if: needs.check-preview.outputs.should-build == 'true'
+ runs-on: ubuntu-24.04
timeout-minutes: 60
+
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v7
with:
fetch-depth: 0
+ persist-credentials: false
+
+ - name: Set Up pnpm
+ uses: pnpm/action-setup@v6
- - name: Setup Node.js
- uses: actions/setup-node@v4
+ - name: Set Up Node.js
+ uses: actions/setup-node@v7
with:
- node-version: '20'
+ node-version: '22'
+ cache: pnpm
+ cache-dependency-path: pnpm-lock.yaml
- - name: Setup pnpm
- uses: pnpm/action-setup@v4
+ - name: Set Up Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: temurin
+ java-version: '17'
- - name: Get pnpm Store Directory
- shell: bash
+ - name: Install Dependencies
+ run: pnpm install --frozen-lockfile --prefer-offline
+
+ - name: Configure Release Environment
+ env:
+ MYANIMELIST_CLIENT_ID: ${{ vars.MYANIMELIST_CLIENT_ID }}
+ ANILIST_CLIENT_ID: ${{ vars.ANILIST_CLIENT_ID }}
run: |
- echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
+ pnpm generate:env:release \
+ --build-type "GitHub Action" \
+ --node-env "production"
- - name: Setup pnpm Cache
- uses: actions/cache@v4
- with:
- path: ${{ env.STORE_PATH }}
- key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- restore-keys: |
- ${{ runner.os }}-pnpm-store-
+ - name: Generate Native Android Project
+ run: |
+ pnpm exec expo prebuild \
+ --platform android \
+ --clean \
+ --no-install \
+ --non-interactive
- - name: Setup Java
- uses: actions/setup-java@v4
+ - name: Configure Gradle
+ uses: gradle/actions/setup-gradle@v6
with:
- distribution: 'zulu'
- java-version: '17'
+ cache-provider: enhanced
+ cache-read-only: >-
+ ${{ github.ref_name != github.event.repository.default_branch }}
+ cache-cleanup: on-success
+ add-job-summary: always
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
- with:
- cache-read-only: false
+ - name: Generate Preview Metadata
+ run: |
+ short_sha="${GITHUB_SHA::8}"
+ base_version_name="$(node --print "require('./app.json').expo.version")"
+ base_version_code="$(node --print "require('./app.json').expo.android.versionCode")"
- - name: Install Dependencies
- run: pnpm install --frozen-lockfile
+ echo "PRERELEASE_VERSION_CODE=$((base_version_code + GITHUB_RUN_NUMBER))" >> "$GITHUB_ENV"
+ echo "PRERELEASE_VERSION_NAME=${base_version_name}-pre.r${GITHUB_RUN_NUMBER}.${short_sha}" >> "$GITHUB_ENV"
+ echo "PRERELEASE_ARTIFACT_NAME=LNReader-${short_sha}" >> "$GITHUB_ENV"
+ echo "PRERELEASE_APK_NAME=LNReader-r${GITHUB_RUN_NUMBER}-${short_sha}.apk" >> "$GITHUB_ENV"
- - name: Create Environment File
- run: |
- cat > .env << EOF
- MYANIMELIST_CLIENT_ID=${{ vars.MYANIMELIST_CLIENT_ID }}
- ANILIST_CLIENT_ID=${{ vars.ANILIST_CLIENT_ID }}
- GIT_HASH=$(git rev-parse --short HEAD)
- RELEASE_DATE=$(date --utc +'%d/%m/%y %I:%M %p %Z')
- BUILD_TYPE=Github Action
- EOF
-
- - name: Make Gradlew Executable
- run: cd android && chmod +x ./gradlew
-
- - name: Set Environment Variables
+ - name: Build Preview APK
+ working-directory: android
run: |
- set -x
- echo "COMMIT_COUNT=$(git rev-list --count HEAD)" >> $GITHUB_ENV
- echo "COMMIT_ID=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
+ mkdir -p app/build/intermediates/sourcemaps/react/preRelease
- - name: Build Android Release
+ ./gradlew :app:assemblePreRelease \
+ --build-cache \
+ --parallel \
+ -PpreReleaseVersionCode="$PRERELEASE_VERSION_CODE" \
+ -PpreReleaseVersionName="$PRERELEASE_VERSION_NAME" \
+ --stacktrace
+
+ mv \
+ app/build/outputs/apk/preRelease/app-preRelease.apk \
+ "app/build/outputs/apk/preRelease/$PRERELEASE_APK_NAME"
+
+ - name: Upload Preview Sourcemap
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.PRERELEASE_ARTIFACT_NAME }}-sourcemap
+ path: android/app/build/generated/sourcemaps/react/preRelease/index.android.bundle.map
+ if-no-files-found: error
+ retention-days: 14
+ compression-level: 0
+ - name: Upload Preview APK
+ id: upload-nightly
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.PRERELEASE_ARTIFACT_NAME }}
+ path: android/app/build/outputs/apk/preRelease/${{ env.PRERELEASE_APK_NAME }}
+ if-no-files-found: error
+ retention-days: 14
+ compression-level: 0
+
+ - name: Check Discord Notification
+ id: discord
env:
- COMMIT_COUNT: ${{ env.COMMIT_COUNT }}
- COMMIT_ID: ${{ env.COMMIT_ID }}
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }}
run: |
- sed -i 's/lnreader/lnreader-r${{ env.COMMIT_COUNT }}(${{ env.COMMIT_ID }})/g' android/app/src/main/res/values/strings.xml
- cd android && ./gradlew assembleRelease -PcustomAppId=com.rajarsheechatterjee.LNReader.commit_${{ env.COMMIT_ID }} --build-cache
- mv app/build/outputs/apk/release/app-release.apk app/build/outputs/apk/release/LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}.apk
+ if [ -n "$DISCORD_WEBHOOK_URL" ]; then
+ echo "configured=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "configured=false" >> "$GITHUB_OUTPUT"
+ fi
- - name: Upload Release Artifact
+ - name: Notify Discord
+ if: steps.discord.outputs.configured == 'true'
env:
- COMMIT_COUNT: ${{ env.COMMIT_COUNT }}
- COMMIT_ID: ${{ env.COMMIT_ID }}
- uses: actions/upload-artifact@v4
- with:
- name: LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}
- path: android/app/build/outputs/apk/release/LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}.apk
- retention-days: 30
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }}
+ ARTIFACT_URL: ${{ steps.upload-nightly.outputs.artifact-url }}
+ run: |
+ short_sha="${GITHUB_SHA::8}"
+ message="[${GITHUB_REPOSITORY}] New nightly build published: \`${short_sha}\`"
+
+ payload=$(jq -n \
+ --arg title "$message" \
+ --arg url "$ARTIFACT_URL" \
+ '{
+ username: "GitHub",
+ avatar_url: "https://github.com/github.png",
+ embeds: [{
+ author: {
+ name: "github-actions[bot]",
+ icon_url: "https://github.com/github.png"
+ },
+ title: $title,
+ url: $url,
+ color: 3092790
+ }]
+ }')
+
+ curl --fail-with-body \
+ --request POST \
+ --header "Content-Type: application/json" \
+ --data "$payload" \
+ "$DISCORD_WEBHOOK_URL"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 90290ee3d..21188e863 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -23,7 +23,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
+ node-version: '24'
- name: Setup pnpm
uses: pnpm/action-setup@v4
@@ -44,5 +44,8 @@ jobs:
- name: Install Dependencies
run: pnpm install --frozen-lockfile
+ - name: Generate Environment Variables
+ run: pnpm generate:env:debug
+
- name: Run ESLint
run: pnpm run lint
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 000000000..42c314c8a
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,232 @@
+name: Release Android
+run-name: Draft ${{ inputs.bump }} Android release
+
+on:
+ workflow_dispatch:
+ inputs:
+ bump:
+ description: Version component to increment
+ required: true
+ default: Patch
+ type: choice
+ options:
+ - Patch
+ - Minor
+ - Major
+
+concurrency:
+ group: release-android
+ cancel-in-progress: false
+
+permissions:
+ contents: write
+
+env:
+ CI: 'true'
+ EXPO_NO_TELEMETRY: '1'
+ NODE_ENV: 'production'
+ RELEASE_BUMP: ${{ inputs.bump }}
+
+jobs:
+ release-android:
+ name: Build Draft Release
+ if: >-
+ github.ref == 'refs/heads/master' &&
+ github.actor_id == vars.RELEASE_ACTOR_ID &&
+ github.triggering_actor == vars.RELEASE_ACTOR_LOGIN
+ environment: release
+ runs-on: ubuntu-24.04
+ timeout-minutes: 90
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v7
+ with:
+ fetch-depth: 0
+ persist-credentials: true
+
+ - name: Set Up pnpm
+ uses: pnpm/action-setup@v6
+
+ - name: Set Up Node.js
+ uses: actions/setup-node@v7
+ with:
+ node-version: '22'
+ cache: pnpm
+ cache-dependency-path: pnpm-lock.yaml
+
+ - name: Set Up Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: temurin
+ java-version: '17'
+
+ - name: Install Dependencies
+ run: pnpm install --frozen-lockfile --prefer-offline
+
+ - name: Resolve Release Version
+ id: version
+ run: |
+ RELEASE_VERSION="$(
+ node scripts/resolve-release-version.cjs "$RELEASE_BUMP"
+ )"
+
+ echo "Releasing $RELEASE_BUMP version: $RELEASE_VERSION"
+ echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_ENV"
+ echo "version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
+
+ - name: Validate Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ if ! [[ "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo "Version must use stable semver, for example 2.0.4"
+ exit 1
+ fi
+
+ if git rev-parse --verify --quiet "refs/tags/v$RELEASE_VERSION"; then
+ echo "Tag v$RELEASE_VERSION already exists"
+ exit 1
+ fi
+
+ if gh release view "v$RELEASE_VERSION" >/dev/null 2>&1; then
+ echo "Release v$RELEASE_VERSION already exists"
+ exit 1
+ fi
+
+ - name: Prepare Release Commit
+ run: |
+ pnpm release:prepare "$RELEASE_VERSION"
+
+ if git diff --quiet -- \
+ package.json \
+ app.json \
+ .github/ISSUE_TEMPLATE/report_issue.yml \
+ .github/ISSUE_TEMPLATE/request_feature.yml; then
+ CURRENT_VERSION="$(node -p "require('./package.json').version")"
+ if [ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]; then
+ echo "No release changes were generated for $RELEASE_VERSION"
+ exit 1
+ fi
+ echo "Release version $RELEASE_VERSION is already committed; resuming"
+ else
+ git diff --check
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git add \
+ package.json \
+ app.json \
+ .github/ISSUE_TEMPLATE/report_issue.yml \
+ .github/ISSUE_TEMPLATE/request_feature.yml
+ git commit -m "chore: Release v$RELEASE_VERSION"
+ fi
+
+ git fetch origin master
+ REMOTE_VERSION="$(
+ node -e "
+ const { execFileSync } = require('child_process');
+ const contents = execFileSync(
+ 'git',
+ ['show', 'origin/master:package.json'],
+ { encoding: 'utf8' },
+ );
+ process.stdout.write(JSON.parse(contents).version);
+ "
+ )"
+
+ if [ "$REMOTE_VERSION" = "$RELEASE_VERSION" ]; then
+ echo "Release commit is already on master; resuming from it"
+ git checkout --detach origin/master
+ echo "RELEASE_ALREADY_PUSHED=true" >> "$GITHUB_ENV"
+ else
+ echo "RELEASE_ALREADY_PUSHED=false" >> "$GITHUB_ENV"
+ fi
+
+ echo "RELEASE_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
+
+ - name: Configure Release Environment
+ env:
+ MYANIMELIST_CLIENT_ID: ${{ vars.MYANIMELIST_CLIENT_ID }}
+ ANILIST_CLIENT_ID: ${{ vars.ANILIST_CLIENT_ID }}
+ run: |
+ pnpm generate:env:release \
+ --build-type "Release" \
+ --node-env "production" \
+ --myanimelist-client-id "$MYANIMELIST_CLIENT_ID" \
+ --anilist-client-id "$ANILIST_CLIENT_ID"
+
+ - name: Generate Native Android Project
+ run: |
+ pnpm exec expo prebuild \
+ --platform android \
+ --clean \
+ --no-install
+
+ - name: Configure Gradle
+ uses: gradle/actions/setup-gradle@v6
+ with:
+ cache-provider: enhanced
+ cache-cleanup: on-success
+ add-job-summary: always
+
+ - name: Build Release APKs
+ working-directory: android
+ run: |
+ mkdir -p app/build/intermediates/sourcemaps/react/release
+
+ ./gradlew :app:assembleRelease \
+ --build-cache \
+ --parallel \
+ -PreleaseAbiSplits=true \
+ --stacktrace
+
+ - name: Package Release APKs
+ run: pnpm release:package "$RELEASE_VERSION"
+
+ - name: Upload Sourcemaps
+ run: |
+ cp android/app/build/generated/sourcemaps/react/release/index.android.bundle.map \
+ "release-artifacts/LNReader-v${RELEASE_VERSION}-sourcemap.map"
+
+ - name: Verify APK Signatures
+ run: |
+ APKSIGNER="$(find "$ANDROID_HOME/build-tools" -type f -name apksigner |
+ sort -V |
+ tail -1)"
+
+ if [ -z "$APKSIGNER" ]; then
+ echo "Android apksigner was not found"
+ exit 1
+ fi
+
+ for APK in release-artifacts/*.apk; do
+ "$APKSIGNER" verify "$APK"
+ done
+
+ - name: Upload Release APKs
+ uses: actions/upload-artifact@v4
+ with:
+ name: LNReader-v${{ steps.version.outputs.version }}
+ path: release-artifacts/
+ if-no-files-found: error
+ retention-days: 30
+ compression-level: 0
+
+ - name: Push Release Commit
+ run: |
+ if [ "$RELEASE_ALREADY_PUSHED" = "true" ]; then
+ echo "Release commit is already on master"
+ else
+ git push origin HEAD:master
+ fi
+
+ - name: Create Draft GitHub Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ gh release create "v$RELEASE_VERSION" \
+ release-artifacts/* \
+ --target "$RELEASE_SHA" \
+ --title "LNReader v$RELEASE_VERSION" \
+ --generate-notes \
+ --draft
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 65d3c4b4a..94bcae4ea 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -1,4 +1,4 @@
-name: Testing
+name: Tests
on:
push:
branches:
@@ -12,28 +12,28 @@ concurrency:
cancel-in-progress: true
jobs:
- build:
- name: Test
+ test:
+ name: Jest
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- - name: Checkout Repository
+ - name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
+ node-version: '24'
- name: Setup pnpm
uses: pnpm/action-setup@v4
- - name: Get pnpm Store Directory
+ - name: Get pnpm Store Path
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- - name: Setup pnpm Cache
+ - name: Cache pnpm
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
@@ -44,5 +44,8 @@ jobs:
- name: Install Dependencies
run: pnpm install --frozen-lockfile
+ - name: Generate Environment
+ run: pnpm generate:env:debug
+
- name: Run Tests
run: pnpm run test
diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml
index ae3e49bd0..9131b2b5b 100644
--- a/.github/workflows/types.yml
+++ b/.github/workflows/types.yml
@@ -1,36 +1,39 @@
-# Temporarily Disabled
name: Type Check
on:
- workflow_dispatch:
- # push:
- # branches:
- # - master
- # pull_request:
- # branches:
- # - master
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
jobs:
- build:
- name: Type Checking
+ type-check:
+ name: TypeScript
runs-on: ubuntu-latest
+ timeout-minutes: 10
steps:
- - name: Checkout Repository
+ - name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
+ node-version: '24'
- name: Setup pnpm
uses: pnpm/action-setup@v4
- - name: Get pnpm Store Directory
+ - name: Get pnpm Store Path
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- - name: Setup pnpm Cache
+ - name: Cache pnpm
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
@@ -41,5 +44,8 @@ jobs:
- name: Install Dependencies
run: pnpm install --frozen-lockfile
+ - name: Generate Environment
+ run: pnpm generate:env:debug
+
- name: Check Types
run: pnpm run type-check
diff --git a/.gitignore b/.gitignore
index b58ccbf0a..92b5b38c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,8 @@ npm-debug.*
*.mobileprovision
*.orig.*
web-build/
-.env
+.*env
+.env.local
reader_playground/index.html
# macOS
.DS_Store
@@ -89,8 +90,27 @@ flake.lock
# pnpm
.pnpm-store
+src/generated/**/*
+
.cursor/
.agents/
.claude/
.jj/
.sisyphus/
+
+# Rock
+.rock/
+
+# TypeScript
+*.tsbuildinfo
+
+*/**/org.eclipse.buildship.core.prefs
+*/**/org.eclipse.jdt.core.prefs
+*/**/.classpath
+*/**/.project
+android/
+release-artifacts/
+!modules/**/android/
+!plugins/android/
+ios/
+!modules/**/ios/
diff --git a/.husky/pre-push b/.husky/pre-push
new file mode 100755
index 000000000..2ca6b9dae
--- /dev/null
+++ b/.husky/pre-push
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+pnpm run type-check
diff --git a/App.tsx b/App.tsx
index 0fcfb58fe..df4f9a1b1 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,15 +1,15 @@
import 'react-native-url-polyfill/auto';
import { enableFreeze } from 'react-native-screens';
-
-enableFreeze(true);
-
-import React, { Suspense, useEffect } from 'react';
+import { PropsWithChildren, Suspense, useEffect, useMemo } from 'react';
import { StatusBar, StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
-import LottieSplashScreen from 'react-native-lottie-splash-screen';
+import * as SplashScreen from 'expo-splash-screen';
import { SafeAreaProvider } from 'react-native-safe-area-context';
-import { Provider as PaperProvider } from 'react-native-paper';
-import * as Notifications from 'expo-notifications';
+import {
+ MD3DarkTheme,
+ MD3LightTheme,
+ Provider as PaperProvider,
+} from 'react-native-paper';
import AppErrorBoundary, {
ErrorFallback,
@@ -18,46 +18,93 @@ import AppErrorBoundary, {
import Main from './src/navigators/Main';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { useInitDatabase } from '@database/db';
+import { useInitializeAppServices } from '@hooks/common/useInitializeAppServices';
+import { opSqliteAdapter } from './src/rozenite/opSqliteAdapter';
+import { useRozeniteSqlitePlugin } from '@rozenite/sqlite-plugin';
+import { ThemeProvider, useTheme } from '@hooks/persisted/useTheme';
+
+enableFreeze(true);
+const sqliteAdapters = __DEV__ && opSqliteAdapter ? [opSqliteAdapter] : [];
+
+/**
+ * The Android window background is resolved from the system light/dark setting,
+ * so it is bright white whenever the system is light – regardless of the theme
+ * picked inside the app. Nothing between it and the navigators paints a
+ * background of its own, so every frame in which no screen is drawn (mounting a
+ * nested navigator, freezing the screen being left) shows through as a flash.
+ * Painting the root view keeps the window covered at all times.
+ */
+const ThemedRootView = ({ children }: PropsWithChildren) => {
+ const theme = useTheme();
+
+ return (
+
+ {children}
+
+ );
+};
+
+const ThemedPaperProvider = ({ children }: PropsWithChildren) => {
+ const theme = useTheme();
+ const paperTheme = useMemo(() => {
+ const baseTheme = theme.isDark ? MD3DarkTheme : MD3LightTheme;
-Notifications.setNotificationHandler({
- handleNotification: async () => {
return {
- shouldPlaySound: false,
- shouldSetBadge: false,
- shouldShowBanner: true,
- shouldShowList: true,
+ ...baseTheme,
+ colors: {
+ ...baseTheme.colors,
+ ...theme,
+ },
};
- },
-});
+ }, [theme]);
+ return {children};
+};
const App = () => {
- const state = useInitDatabase();
+ useRozeniteSqlitePlugin({ adapters: sqliteAdapters });
+ const { success: databaseReady, error: databaseError } = useInitDatabase();
+ const { ready: servicesReady, error: servicesError } =
+ useInitializeAppServices(Boolean(databaseReady));
useEffect(() => {
- if (state.success || state.error) {
- LottieSplashScreen.hide();
+ if ((databaseReady && servicesReady) || databaseError || servicesError) {
+ SplashScreen.hideAsync();
}
- }, [state.success, state.error]);
+ }, [databaseReady, databaseError, servicesReady, servicesError]);
+
+ const initializationError = databaseError || servicesError;
+
+ if (initializationError) {
+ return (
+
+ null} />
+
+ );
+ }
- if (state.error) {
- return null} />;
+ if (!databaseReady || !servicesReady) {
+ return null;
}
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/CONTRIBUTING-NIX.md b/CONTRIBUTING-NIX.md
index 8d6d55843..b0973dc32 100644
--- a/CONTRIBUTING-NIX.md
+++ b/CONTRIBUTING-NIX.md
@@ -6,7 +6,7 @@ This document outlines how to set up your React Native development environment u
This was tested on an Arch Linux system with AMD CPU and GPU. There are likely problems with NVIDIA GPUs.
-Compatibility with Microsoft WSL2 is not guaranteed, but should work withoud major issues, except for the emulator.
+Compatibility with Microsoft WSL2 is not guaranteed, but should work without major issues, except for the emulator.
## Prerequisites
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5b4dfd384..1fe7b41ed 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,14 +2,14 @@
Contributions are welcome and are greatly appreciated!
-## Setup your environment with nix
+## Setup Your Environment with Nix
If you are on a Linux system, you can install the nix package manager and use the nix flakes to set up your development environment.
-See [CONTRIBUTING-NIX.md](CONTRIBUTING-NIX.md)
+See [CONTRIBUTING-NIX.md](CONTRIBUTING-NIX.md).
-## Setting up your environment
+## Setting Up Your Environment
-After forking to your own github org or account, do the following steps to get started:
+After forking to your own GitHub organization or account, take the following steps to get started:
```bash
# prerequisites
@@ -35,7 +35,7 @@ pnpm run build:release:android
### Developing on Android
-You will need an Android device or emulator connected to your computer as well as an IDE of your choice. (eg: vscode)
+You will need an Android device or emulator connected to your computer as well as an IDE of your choice (e.g., VS Code).
```bash
# prerequisites
@@ -57,7 +57,7 @@ pnpm run dev:android
This codebase's linting rules are enforced using [ESLint](http://eslint.org/).
It is recommended that you install an eslint plugin for your editor of choice when working on this
-codebase, however you can always check to see if the source code is compliant by running:
+codebase, however, you can always check to see if the source code is compliant by running:
```bash
pnpm run lint
diff --git a/DEBUG_PRODUCTION.md b/DEBUG_PRODUCTION.md
new file mode 100644
index 000000000..25e1f3442
--- /dev/null
+++ b/DEBUG_PRODUCTION.md
@@ -0,0 +1,73 @@
+# Debugging Production Builds
+
+Symbolicate production stack traces from LNReader using uploaded sourcemaps.
+
+## Prerequisites
+
+- **Node.js** (v18 or later)
+- The **sourcemap** matching the crashing APK version
+- A **stack trace** from either `adb logcat` (device attached) or pasted from a Discord / GitHub issue report
+
+## Getting the Sourcemap
+
+### Release builds
+
+Sourcemaps are uploaded as GitHub release assets. Download the one matching the crashing version:
+
+```
+LNReader-v{version}-sourcemap.map
+```
+
+Example for `v2.5.0`:
+
+1. Go to the [releases page](https://github.com/lnreader/lnreader/releases)
+2. Find release `v2.5.0`
+3. Under **Assets**, download `LNReader-v2.5.0-sourcemap.map`
+
+### Preview / nightly builds
+
+Sourcemaps are uploaded as a separate artifact alongside the preview APK, named `LNReader-{sha}-sourcemap`.
+
+1. Go to the [Actions tab](https://github.com/lnreader/lnreader/actions) → **Build Preview**
+2. Find the workflow run matching the build hash
+3. Under **Artifacts**, download the one with the `-sourcemap` suffix
+
+## Symbolicating a Stack Trace
+
+Save the raw stack trace to a file (from `adb logcat`, or paste from Discord/GitHub into `trace.txt`), then run:
+
+```bash
+npx metro-symbolicate < trace.txt
+```
+
+Or pipe it directly:
+
+```bash
+adb logcat -d | npx metro-symbolicate
+```
+
+## Example
+
+Before (Hermes-obfuscated):
+
+```
+com.lnreader E Error: Unexpected token
+ at 93795 (address at index.js:1:729193)
+ at 28471 (address at index.js:1:219482)
+ at 10384 (address at index.js:1:81729)
+```
+
+After symbolication:
+
+```
+com.lnreader E Error: Unexpected token
+ at handleResponse (src/api/plugins.ts:120:12)
+ at fetchPlugin (src/hooks/usePlugins.ts:45:8)
+ at getPluginList (src/screens/browse/BrowseScreen.tsx:89:20)
+```
+
+## Troubleshooting
+
+- **Version mismatch**: the sourcemap must match the exact APK build. A mismatched version produces wrong file names and line numbers.
+- **No sourcemap artifact**: verify the workflow that built the APK ran successfully and that the "Upload Sourcemaps" / "Upload Preview Sourcemap" step completed without errors.
+- **Hermes bytecode**: Hermes compiles JS to bytecode. The sourcemap is generated during this step and maps bytecode offsets back to source. `metro-symbolicate` handles this transparently.
diff --git a/Gemfile b/Gemfile
index 20d5e24af..51515233e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -13,4 +13,5 @@ gem 'concurrent-ruby', '< 1.3.4'
gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
-gem 'mutex_m'
\ No newline at end of file
+gem 'mutex_m'
+gem 'nkf'
diff --git a/README.md b/README.md
index 06d4941f7..fe142f4ae 100644
--- a/README.md
+++ b/README.md
@@ -35,8 +35,8 @@
-
-
+
+
diff --git a/TESTING.md b/TESTING.md
index 21ae843b7..e6ecea276 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -9,13 +9,13 @@ This guide explains how to write tests in this React Native project using Jest a
The project has global mocks configured in Jest. These are automatically applied:
-- `__mocks__/` - Global mocks for native modules (react-native-mmkv, react-navigation, all database queries, etc.)
+- `test/mocks/` - Global mocks for native modules (react-native-mmkv, react-navigation, all database queries, etc.)
- `src/hooks/__mocks__/index.ts` - Hook-specific mocks (showToast, getString, parseChapterNumber, etc.)
- `src/hooks/__tests__/mocks.ts` - Extended mocks for persisted hooks
### Using @test-utils
-There's a custom render wrapper at `__tests-modules__/test-utils.tsx` with:
+There is a custom render wrapper at `test/test-utils.tsx` with:
- `render` - wraps with GestureHandlerRootView, SafeAreaProvider, PaperProvider, etc.
- `renderNovel` - includes NovelContextProvider
@@ -40,7 +40,7 @@ jest.mock('@hooks/persisted/usePlugins');
### 2. Mock Functions Not Working
-If `mockReturnValue` throws "not a function", create mock functions at module level:
+If `mockReturnValue` throws "not a function", create mock functions at the module level:
```typescript
// CORRECT: Module-level mock functions
diff --git a/__mocks__/nativeModules.js b/__mocks__/nativeModules.js
deleted file mode 100644
index fa3c82cdf..000000000
--- a/__mocks__/nativeModules.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// require('react-native-gesture-handler/jestSetup');
-// require('react-native-reanimated').setUpTests();
-
-jest.mock('@specs/NativeFile', () => ({
- __esModule: true,
- default: {
- writeFile: jest.fn(),
- readFile: jest.fn(() => ''),
- copyFile: jest.fn(),
- moveFile: jest.fn(),
- exists: jest.fn(() => true),
- mkdir: jest.fn(),
- unlink: jest.fn(),
- readDir: jest.fn(() => []),
- downloadFile: jest.fn().mockResolvedValue(),
- getConstants: jest.fn(() => ({
- ExternalDirectoryPath: '/mock/external',
- ExternalCachesDirectoryPath: '/mock/caches',
- })),
- },
-}));
-
-jest.mock('@specs/NativeEpub', () => ({
- __esModule: true,
- default: {
- parseNovelAndChapters: jest.fn(() => ({
- name: 'Mock Novel',
- cover: null,
- summary: null,
- author: null,
- artist: null,
- chapters: [],
- cssPaths: [],
- imagePaths: [],
- })),
- },
-}));
-
-jest.mock('@specs/NativeTTSMediaControl', () => ({
- __esModule: true,
- default: {
- showMediaNotification: jest.fn(),
- updatePlaybackState: jest.fn(),
- updateProgress: jest.fn(),
- dismiss: jest.fn(),
- addListener: jest.fn(),
- removeListeners: jest.fn(),
- },
-}));
-
-jest.mock('@specs/NativeVolumeButtonListener', () => ({
- __esModule: true,
- default: {
- addListener: jest.fn(),
- removeListeners: jest.fn(),
- },
-}));
-
-jest.mock('@specs/NativeZipArchive', () => ({
- __esModule: true,
- default: {
- zip: jest.fn().mockResolvedValue(),
- unzip: jest.fn().mockResolvedValue(),
- remoteUnzip: jest.fn().mockResolvedValue(),
- remoteZip: jest.fn().mockResolvedValue(''),
- },
-}));
diff --git a/__mocks__/react-native-nitro-modules.js b/__mocks__/react-native-nitro-modules.js
deleted file mode 100644
index f6a21b70a..000000000
--- a/__mocks__/react-native-nitro-modules.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// Mock for react-native-nitro-modules in Jest environment
-jest.mock('react-native-nitro-modules', () => ({
- __esModule: true,
- default: {
- createHybridObject: jest.fn(() => {
- // Return a mock object that won't be used since MMKV has its own mock
- return {};
- }),
- },
-}));
diff --git a/android/app/build.gradle b/android/app/build.gradle
deleted file mode 100644
index 691301293..000000000
--- a/android/app/build.gradle
+++ /dev/null
@@ -1,137 +0,0 @@
-apply plugin: "com.android.application"
-apply plugin: "com.facebook.react"
-apply plugin: 'org.jetbrains.kotlin.android'
-
-/**
- * This is the configuration block to customize your React Native Android app.
- * By default you don't need to apply any configuration, just uncomment the lines you need.
- */
-react {
- /* Folders */
- // The root of your project, i.e. where "package.json" lives. Default is '../..'
- // root = file("../../")
- // The folder where the react-native NPM package is. Default is ../../node_modules/react-native
- // reactNativeDir = file("../../node_modules/react-native")
- // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
- // codegenDir = file("../../node_modules/@react-native/codegen")
- // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
- // cliFile = file("../../node_modules/react-native/cli.js")
-
- /* Variants */
- // The list of variants to that are debuggable. For those we're going to
- // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
- // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
- // debuggableVariants = ["liteDebug", "prodDebug"]
-
- /* Bundling */
- // A list containing the node command and its flags. Default is just 'node'.
- // nodeExecutableAndArgs = ["node"]
- //
- // The command to run when bundling. By default is 'bundle'
- // bundleCommand = "ram-bundle"
- //
- // The path to the CLI configuration file. Default is empty.
- // bundleConfig = file(../rn-cli.config.js)
- //
- // The name of the generated asset file containing your JS bundle
- // bundleAssetName = "MyApplication.android.bundle"
- //
- // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
- // entryFile = file("../js/index.android.js")
- //
- // A list of extra flags to pass to the 'bundle' commands.
- // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
- // extraPackagerArgs = []
-
- /* Hermes Commands */
- // The hermes compiler command to run. By default it is 'hermesc'
- // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
- //
- // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
- // hermesFlags = ["-O", "-output-source-map"]
-
- /* Autolinking */
- autolinkLibrariesWithApp()
-}
-
-/**
- * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
- */
-def enableProguardInReleaseBuilds = false
-
-/**
- * The preferred build flavor of JavaScriptCore (JSC).
- *
- * For example, to use the international variant, you can use:
- * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
- *
- * The international variant includes ICU i18n library and necessary data
- * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
- * give correct results when using with locales other than en-US. Note that
- * this variant is about 6MiB larger per architecture than default.
- */
-def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
-
-ext {
- versionMajor = 2
- versionMinor = 0
- versionPatch = 3
-}
-
-android {
- ndkVersion rootProject.ext.ndkVersion
- buildToolsVersion rootProject.ext.buildToolsVersion
- compileSdk rootProject.ext.compileSdkVersion
- namespace "com.rajarsheechatterjee.LNReader"
- defaultConfig {
- applicationId project.hasProperty('customAppId') ? project.getProperty('customAppId') : 'com.rajarsheechatterjee.LNReader'
- minSdkVersion rootProject.ext.minSdkVersion
- targetSdkVersion rootProject.ext.targetSdkVersion
- // Generated version code. Supports versions up to 1024.1024.2048
- versionCode ((((versionMajor << 10) | versionMinor) << 11) | versionPatch)
- versionName "$versionMajor.$versionMinor.$versionPatch"
- }
- signingConfigs {
- debug {
- storeFile file('debug.keystore')
- storePassword 'android'
- keyAlias 'androiddebugkey'
- keyPassword 'android'
- }
- }
- buildTypes {
- debug {
- signingConfig signingConfigs.debug
- applicationIdSuffix 'debug'
- versionNameSuffix '-debug'
- }
- release {
- // Caution! In production, you need to generate your own keystore file.
- // see https://reactnative.dev/docs/signed-apk-android.
- signingConfig signingConfigs.debug
- minifyEnabled enableProguardInReleaseBuilds
- proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
- }
- }
- kotlinOptions {
- jvmTarget = '17'
- }
- externalNativeBuild {
- cmake {
- path "src/main/jni/CMakeLists.txt"
- }
- }
-}
-
-dependencies {
- // The version of react-native is set by the React Native Gradle Plugin
- implementation("com.facebook.react:react-android")
- implementation 'androidx.core:core-ktx:1.15.0'
- implementation 'androidx.media:media:1.7.0'
-
- if (hermesEnabled.toBoolean()) {
- implementation("com.facebook.react:hermes-android")
- } else {
- implementation jscFlavor
- }
-}
\ No newline at end of file
diff --git a/android/app/debug.keystore b/android/app/debug.keystore
deleted file mode 100644
index 364e105ed..000000000
Binary files a/android/app/debug.keystore and /dev/null differ
diff --git a/android/app/google-services.json b/android/app/google-services.json
deleted file mode 100644
index 4e0ee2c73..000000000
--- a/android/app/google-services.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "project_info": {
- "project_number": "523872485654",
- "project_id": "lnreader-backup",
- "storage_bucket": "lnreader-backup.appspot.com"
- },
- "client": [
- {
- "client_info": {
- "mobilesdk_app_id": "1:523872485654:android:169cd062458db962ce1037",
- "android_client_info": {
- "package_name": "com.rajarsheechatterjee.LNReader"
- }
- },
- "oauth_client": [
- {
- "client_id": "523872485654-14jtut8orr7dbrk2chea279d7k8889sr.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "com.rajarsheechatterjee.LNReader",
- "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
- }
- },
- {
- "client_id": "523872485654-liarmq8nl0g5an2cki3bpg9jc0d8a21j.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyBH_j-0Jyo9aFJ_KV8Nr3te2hs_L_ZYhrE"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "523872485654-liarmq8nl0g5an2cki3bpg9jc0d8a21j.apps.googleusercontent.com",
- "client_type": 3
- }
- ]
- }
- }
- }
- ],
- "configuration_version": "1"
-}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
deleted file mode 100644
index c83ce9df1..000000000
--- a/android/app/proguard-rules.pro
+++ /dev/null
@@ -1,14 +0,0 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
--keep class com.swmansion.reanimated.** { *; }
--keep class com.facebook.react.turbomodule.** { *; }
--keep class com.facebook.hermes.unicode.** { *; }
--keep class com.facebook.jni.** { *; }
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
deleted file mode 100644
index a40bc8153..000000000
--- a/android/app/src/debug/AndroidManifest.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
diff --git a/android/app/src/debug/res/values/string.xml b/android/app/src/debug/res/values/string.xml
deleted file mode 100644
index 6dec539d1..000000000
--- a/android/app/src/debug/res/values/string.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- LNReader debug
-
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 2d8e7b563..000000000
--- a/android/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/app/src/main/assets/css/tts.css b/android/app/src/main/assets/css/tts.css
deleted file mode 100644
index 91244534b..000000000
--- a/android/app/src/main/assets/css/tts.css
+++ /dev/null
@@ -1,48 +0,0 @@
-#TTS-Controller {
- position: fixed;
- top: 50%;
- left: 20px;
- opacity: 0.5;
-}
-
-#TTS-Controller button {
- background: color-mix(
- in srgb,
- var(--readerSettings-theme) 85%,
- var(--readerSettings-textColor) 15%
- );
- outline: none;
- border-width: 1px;
- border-style: solid;
- border-color: color-mix(
- in srgb,
- var(--readerSettings-textColor) 30%,
- var(--readerSettings-theme) 70%
- );
- display: flex;
- justify-items: center;
- align-items: center;
- padding: 4px;
- border-radius: 100%;
- transition: 0.5s;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
-}
-
-#TTS-Controller.active {
- opacity: 1;
-}
-
-#TTS-Controller.active button {
- padding: 16px;
-}
-
-#TTS-Controller svg {
- fill: var(--readerSettings-textColor);
- width: 20px;
- height: 20px;
-}
-
-#TTS-Controller.active svg {
- width: 24px;
- height: 24px;
-}
diff --git a/android/app/src/main/assets/js/icons.js b/android/app/src/main/assets/js/icons.js
deleted file mode 100644
index 54ea56035..000000000
--- a/android/app/src/main/assets/js/icons.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const volumnIcon =
- '';
-const pauseIcon =
- '';
-const resumeIcon =
- '';
diff --git a/android/app/src/main/assets/js/index.js b/android/app/src/main/assets/js/index.js
deleted file mode 100644
index f4175aa0a..000000000
--- a/android/app/src/main/assets/js/index.js
+++ /dev/null
@@ -1,351 +0,0 @@
-const { div, p, img, button } = van.tags;
-
-const ChapterEnding = () => {
- return () =>
- reader.generalSettings.val.pageReader
- ? div()
- : div(div({ class: 'info-text' }, reader.strings.finished), () =>
- reader.nextChapter
- ? button(
- {
- class: 'next-button',
- onclick: e => {
- e.stopPropagation();
- reader.post({ type: 'next' });
- },
- },
- reader.strings.nextChapter,
- )
- : div({ class: 'info-text' }, reader.strings.noNextChapter),
- );
-};
-
-const Scrollbar = () => {
- const horizontal = van.derive(
- () => !reader.generalSettings.val.verticalSeekbar,
- );
- let lock = false;
- const percentage = van.state(0);
- const update = ratio => {
- if (ratio === undefined) {
- ratio = (window.scrollY + reader.layoutHeight) / reader.chapterHeight;
- }
- if (ratio > 1) {
- ratio = 1;
- }
- if (reader.generalSettings.val.pageReader) {
- pageReader.movePage(
- parseInt(pageReader.totalPages.val * Math.min(0.99, ratio)),
- );
- return;
- }
- percentage.val = parseInt(ratio * 100);
- if (lock) {
- window.scrollTo({
- top: reader.chapterHeight * ratio - reader.layoutHeight,
- behavior: 'instant',
- });
- }
- };
- window.addEventListener(
- 'scroll',
- () => !lock && !reader.generalSettings.val.pageReader && update(),
- );
- return div(
- { id: 'ScrollBar' },
- div(
- { class: 'scrollbar-item scrollbar-text', id: 'scrollbar-percentage' },
- () =>
- reader.generalSettings.val.pageReader
- ? pageReader.page.val + 1
- : percentage.val,
- ),
- div(
- { class: 'scrollbar-item', id: 'scrollbar-slider' },
- div(
- { id: 'scrollbar-track' },
- div(
- {
- id: 'scrollbar-progress',
- style: () => {
- const percentageValue = reader.generalSettings.val.pageReader
- ? ((pageReader.page.val + 1) / pageReader.totalPages.val) * 100
- : percentage.val;
- return horizontal.val
- ? `width: ${percentageValue}%; height: 100%;`
- : `height: ${percentageValue}%; width: 100%;`;
- },
- },
- div(
- {
- id: 'scrollbar-thumb-wrapper',
- ontouchstart: () => {
- lock = true;
- },
- ontouchend: () => {
- lock = false;
- },
- ontouchmove: function (e) {
- const slider = this.parentElement.parentElement.parentElement;
- const sliderHeight = horizontal.val
- ? slider.clientWidth
- : slider.clientHeight;
- const sliderOffsetY = horizontal.val
- ? slider.getBoundingClientRect().left
- : slider.getBoundingClientRect().top;
- const ratio =
- ((horizontal.val
- ? e.changedTouches[0].clientX
- : e.changedTouches[0].clientY) -
- sliderOffsetY) /
- sliderHeight;
- update(ratio < 0 ? 0 : ratio);
- },
- },
- div({ id: 'scrollbar-thumb' }),
- ),
- ),
- ),
- ),
- div(
- {
- class: 'scrollbar-item scrollbar-text',
- id: 'scrollbar-percentage-max',
- },
- () =>
- reader.generalSettings.val.pageReader ? pageReader.totalPages.val : 100,
- ),
- );
-};
-
-const ToolWrapper = () => {
- const horizontal = van.derive(
- () => !reader.generalSettings.val.verticalSeekbar,
- );
- return div(
- {
- id: 'ToolWrapper',
- class: () =>
- `${reader.hidden.val ? 'hidden' : ''} ${
- horizontal.val ? 'horizontal' : ''
- }`,
- },
- Scrollbar(),
- );
-};
-
-const ImageModal = ({ src }) => {
- return div(
- {
- id: 'Image-Modal',
- class: () => (src.val ? 'show' : ''),
- onclick: e => {
- if (e.target.id !== 'Image-Modal-img') {
- e.stopPropagation();
- src.val = '';
- }
- },
- },
- img({
- id: 'Image-Modal-img',
- src: src,
- alt: () => (src.val ? `Cant not render image from ${src.val}` : ''),
- }),
- );
-};
-
-const ModalWrapper = () => {
- const imgSrc = van.state('');
- const showImage = src => {
- imgSrc.val = src;
- reader.viewport.setAttribute(
- 'content',
- 'width=device-width, initial-scale=1.0, maximum-scale=10',
- );
- };
- const hideImage = () => {
- imgSrc.val = '';
- reader.viewport.setAttribute(
- 'content',
- 'width=device-width, initial-scale=1.0, maximum-scale=1.0',
- );
- };
-
- document.addEventListener('contextmenu', e => {
- if (e.target instanceof HTMLImageElement) {
- if (!imgSrc.val) {
- showImage(e.target.src);
- } else {
- hideImage();
- }
- }
- });
- return div(ImageModal({ src: imgSrc }));
-};
-
-const Footer = () => {
- const percentage = van.state(0);
- const time = van.state(
- new Date().toLocaleTimeString(undefined, {
- hour: '2-digit',
- minute: '2-digit',
- hour12: false,
- }),
- );
- window.addEventListener('scroll', () => {
- let ratio = (window.scrollY + reader.layoutHeight) / reader.chapterHeight;
- if (ratio > 1) {
- ratio = 1;
- }
- percentage.val = parseInt(ratio * 100);
- });
- setInterval(() => {
- time.val = new Date().toLocaleTimeString(undefined, {
- hour: '2-digit',
- minute: '2-digit',
- hour12: false,
- });
- }, 10000);
- return div(
- {
- id: 'reader-footer-wrapper',
- class: () =>
- reader.generalSettings.val.showBatteryAndTime ||
- reader.generalSettings.val.showScrollPercentage
- ? ''
- : 'd-none',
- },
- div(
- { id: 'reader-footer' },
-
- div(
- {
- id: 'reader-battery',
- class: () =>
- `reader-footer-item ${
- reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden'
- }`,
- },
- () => Math.ceil(reader.batteryLevel.val * 100) + '%',
- ),
- div(
- {
- id: 'reader-percentage',
- class: () =>
- `reader-footer-item ${
- reader.generalSettings.val.showScrollPercentage ? '' : 'hidden'
- }`,
- },
- () =>
- reader.generalSettings.val.pageReader
- ? `${pageReader.page.val + 1}/${pageReader.totalPages.val}`
- : percentage.val + '%',
- ),
- div(
- {
- id: 'reader-time',
- class: () =>
- `reader-footer-item ${
- reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden'
- }`,
- },
- time,
- ),
- ),
- );
-};
-
-const TTSController = () => {
- let controllerElement = null;
- let hoverElement = null;
- let clientX = null;
- let clientY = null;
- return div(
- {
- id: 'TTS-Controller',
- class: () => `${reader.generalSettings.val.TTSEnable ? '' : 'hidden'}`,
- style: () =>
- reader.generalSettings.val.TTSEnable
- ? 'pointer-events: auto;'
- : 'pointer-events: none; display: none !important; opacity: 0; transition: none;',
- ontouchstart: () => {
- if (!controllerElement) {
- controllerElement = document.getElementById('TTS-Controller');
- }
- controllerElement.classList.add('active');
- controllerElement.style.transition = '';
- },
- ontouchmove: e => {
- e.preventDefault();
- e.stopPropagation();
- clientX = e.changedTouches[0].clientX;
- clientY = e.changedTouches[0].clientY;
- controllerElement.style.left = `${clientX}px`;
- controllerElement.style.top = `${clientY}px`;
- const hoverElements = document.elementsFromPoint(clientX, clientY);
- const newHoverElement = hoverElements.reverse().find(e => {
- if (e.id.includes('scrollbar')) {
- return false;
- }
- return tts.readable(e);
- });
- hoverElement?.classList.remove('highlight');
- if (newHoverElement) {
- newHoverElement.classList.add('highlight');
- hoverElement = newHoverElement;
- } else {
- hoverElement = null;
- }
- },
- ontouchend: () => {
- controllerElement.style.transition = '1s';
- controllerElement.classList.remove('active');
- controllerElement.style.left = '20px';
- if (clientX && clientY) {
- let top = clientY < 120 ? 120 : clientY;
- if (top + 120 > reader.layoutHeight) {
- top = reader.layoutHeight - 120;
- }
- controllerElement.style.top = `${top}px`;
- // Check if TTS is still enabled before starting
- if (hoverElement && reader.generalSettings.val.TTSEnable) {
- tts.start(hoverElement);
- controllerElement.firstElementChild.innerHTML = pauseIcon;
- }
- }
- clientX = null;
- clientY = null;
- },
- onclick: e => {
- e.stopPropagation();
- // Don't allow interaction if TTS is disabled
- if (!reader.generalSettings.val.TTSEnable) {
- return;
- }
- if (tts.reading) {
- tts.pause();
- controllerElement.firstElementChild.innerHTML = resumeIcon;
- } else if (tts.started) {
- tts.resume();
- controllerElement.firstElementChild.innerHTML = pauseIcon;
- } else {
- tts.start();
- controllerElement.firstElementChild.innerHTML = pauseIcon;
- }
- },
- },
- button({ innerHTML: volumnIcon }),
- );
-};
-
-const ReaderUI = () => {
- return div(
- ToolWrapper(),
- TTSController(),
- ModalWrapper(),
- Footer(),
- ChapterEnding(),
- );
-};
-
-van.add(document.getElementById('reader-ui'), ReaderUI());
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainActivity.kt b/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainActivity.kt
deleted file mode 100644
index c23dfe6c9..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainActivity.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.rajarsheechatterjee.LNReader
-
-import android.graphics.Color
-import android.os.Build
-import android.os.Bundle
-import android.view.KeyEvent
-import android.view.View
-import android.view.WindowManager
-import com.facebook.react.ReactActivity
-import com.facebook.react.ReactActivityDelegate
-import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
-import com.facebook.react.defaults.DefaultReactActivityDelegate
-import com.rajarsheechatterjee.NativeVolumeButtonListener.NativeVolumeButtonListener
-import expo.modules.ReactActivityDelegateWrapper
-import org.devio.rn.splashscreen.SplashScreen
-
-class MainActivity : ReactActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- val layoutParams = WindowManager.LayoutParams()
- layoutParams.layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
- window.attributes = layoutParams
- }
- window.statusBarColor = Color.TRANSPARENT
- window.navigationBarColor = Color.TRANSPARENT
- @Suppress("DEPRECATION")
- window.decorView.systemUiVisibility =
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- super.onCreate(null)
- SplashScreen.show(this, R.style.SplashScreenTheme, R.id.lottie)
- SplashScreen.setAnimationFinished(true)
- }
-
- override fun dispatchKeyEvent(event: KeyEvent): Boolean {
- if (NativeVolumeButtonListener.isActive) {
- val action = event.action
- return when (event.keyCode) {
- KeyEvent.KEYCODE_VOLUME_UP -> {
- if (action == KeyEvent.ACTION_DOWN) {
- NativeVolumeButtonListener.sendEvent(true)
- }
- true
- }
-
- KeyEvent.KEYCODE_VOLUME_DOWN -> {
- if (action == KeyEvent.ACTION_DOWN) {
- NativeVolumeButtonListener.sendEvent(false)
- }
- true
- }
-
- else -> super.dispatchKeyEvent(event)
- }
- }
- return super.dispatchKeyEvent(event)
- }
-
- /**
- * Returns the name of the main component registered from JavaScript.
- * This is used to schedule rendering of the component.
- */
- override fun getMainComponentName(): String = "main"
-
- override fun createReactActivityDelegate(): ReactActivityDelegate {
- return ReactActivityDelegateWrapper(
- this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, DefaultReactActivityDelegate(
- this,
- mainComponentName, // If you opted-in for the New Architecture, we enable the Fabric Renderer.
- fabricEnabled
- )
- )
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.kt b/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.kt
deleted file mode 100644
index 856a31726..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.rajarsheechatterjee.LNReader
-import expo.modules.ExpoReactHostFactory
-
-import android.app.Application
-import android.content.res.Configuration
-import com.facebook.react.PackageList
-import com.facebook.react.ReactApplication
-import com.facebook.react.ReactHost
-import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
-import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
-import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
-import com.facebook.react.soloader.OpenSourceMergedSoMapping
-import com.facebook.soloader.SoLoader
-import com.rajarsheechatterjee.NativeFile.NativePackage
-import com.rajarsheechatterjee.NativeVolumeButtonListener.NativeVolumeButtonListenerPackage
-import com.rajarsheechatterjee.NativeTTSMediaControl.NativeTTSMediaControlPackage
-import com.rajarsheechatterjee.NativeZipArchive.NativeZipArchivePackage
-import expo.modules.ApplicationLifecycleDispatcher
-
-class MainApplication : Application(), ReactApplication {
- override val reactHost: ReactHost by lazy {
- ExpoReactHostFactory.getDefaultReactHost(
- context = applicationContext,
- packageList =
- PackageList(this).packages.apply {
- add(NativePackage())
- add(NativeTTSMediaControlPackage())
- add(NativeVolumeButtonListenerPackage())
- add(NativeZipArchivePackage())
- },
- )
- }
-
- override fun onCreate() {
- super.onCreate()
- loadReactNative(this)
- ApplicationLifecycleDispatcher.onApplicationCreate(this)
- }
-
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
- ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativeFile.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativeFile.kt
deleted file mode 100644
index a65a56505..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativeFile.kt
+++ /dev/null
@@ -1,248 +0,0 @@
-package com.rajarsheechatterjee.NativeFile
-
-import android.net.Uri
-import android.os.Build
-import com.facebook.react.bridge.Promise
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.bridge.ReadableMap
-import com.facebook.react.bridge.WritableArray
-import com.facebook.react.bridge.WritableMap
-import com.facebook.react.bridge.WritableNativeArray
-import com.facebook.react.bridge.WritableNativeMap
-import com.facebook.react.modules.network.CookieJarContainer
-import com.facebook.react.modules.network.ForwardingCookieHandler
-import com.facebook.react.modules.network.OkHttpClientProvider
-import com.lnreader.spec.NativeFileSpec
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import okhttp3.Call
-import okhttp3.Callback
-import okhttp3.Headers
-import okhttp3.JavaNetCookieJar
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-import okhttp3.Response
-import java.io.File
-import java.io.FileOutputStream
-import java.io.FileWriter
-import java.io.IOException
-import java.io.InputStream
-import java.io.OutputStream
-import java.io.PushbackInputStream
-import java.util.zip.GZIPInputStream
-
-
-class NativeFile(context: ReactApplicationContext) :
- NativeFileSpec(context) {
- private val BUFFER_SIZE = 4096
- private val okHttpClient = OkHttpClientProvider.createClient()
- private val coroutineScope = CoroutineScope(Dispatchers.IO)
-
- init {
- val cookieContainer = okHttpClient.cookieJar as CookieJarContainer
- val cookieHandler = ForwardingCookieHandler(reactApplicationContext)
- cookieContainer.setCookieJar(JavaNetCookieJar(cookieHandler))
- }
-
- private fun getFileUri(filepath: String): Uri {
- var uri = Uri.parse(filepath)
- if (uri.scheme == null) {
- // No prefix, assuming that provided path is absolute path to file
- val file = File(filepath)
- if (file.isDirectory) {
- throw Exception("Invalid file, folder found!")
- }
- uri = Uri.parse("file://$filepath")
- }
- return uri
- }
-
- private fun getInputStream(filepath: String): InputStream {
- val uri = getFileUri(filepath)
- return reactApplicationContext.contentResolver.openInputStream(uri)
- ?: throw Exception("ENOENT: could not open an input stream for '$filepath'")
- }
-
- private val writeAccessByAPILevel: String
- get() = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) "w" else "rwt"
-
- private fun getOutputStream(filepath: String): OutputStream {
- val uri = getFileUri(filepath)
- return reactApplicationContext.contentResolver.openOutputStream(uri, writeAccessByAPILevel)
- ?: throw Exception("ENOENT: could not open an output stream for '$filepath'")
- }
-
- override fun writeFile(path: String, content: String) {
- try {
- val fw = FileWriter(path)
- fw.write(content)
- fw.close()
- } catch (e: IOException) {
- throw Exception("Failed to write file '$path': ${e.message}")
- }
- }
-
- override fun readFile(path: String): String {
- val file = File(path)
- if (!file.exists()) {
- throw Exception("File not found: '$path'")
- }
- return file.bufferedReader().readText()
- }
-
- override fun copyFile(filepath: String, destPath: String) {
- copyFileContent(filepath, destPath)
- }
-
- override fun moveFile(filepath: String, destPath: String) {
- val inFile = File(filepath)
- copyFileContent(filepath, destPath, { inFile.delete() })
- }
-
- private fun copyFileContent(
- filepath: String,
- destPath: String,
- onDone: (() -> Unit)? = null,
- ) {
- try {
- val inputStream = getInputStream(filepath)
- try {
- val outputStream = getOutputStream(destPath)
- try {
- val buffer = ByteArray(BUFFER_SIZE)
- var length: Int
- while (inputStream.read(buffer).also { length = it } > 0) {
- outputStream.write(buffer, 0, length)
- }
- } finally {
- outputStream.close()
- }
- } finally {
- inputStream.close()
- }
- if (onDone != null) {
- onDone()
- }
- } catch (e: IOException) {
- throw Exception("Failed to copy file from '$filepath' to '$destPath': ${e.message}")
- }
- }
-
- override fun exists(filepath: String) = File(filepath).exists()
-
- override fun mkdir(filepath: String) {
- val file = File(filepath)
- if (!file.exists()) {
- val created = file.mkdirs()
- if (!created) throw Exception("Directory could not be created")
- }
- }
-
- private fun deleteRecursive(fileOrDirectory: File) {
- if (fileOrDirectory.isDirectory) {
- for (child in fileOrDirectory.listFiles()!!) {
- deleteRecursive(child)
- }
- }
- fileOrDirectory.delete()
- }
-
- override fun unlink(filepath: String) {
- val file = File(filepath)
- if (!file.exists()) throw Exception("File does not exist")
- deleteRecursive(file)
- }
-
- override fun readDir(directory: String): WritableArray {
- val file = File(directory)
- if (!file.exists()) throw Exception("Folder does not exist")
- val files = file.listFiles()
- val fileMaps: WritableArray = WritableNativeArray()
- for (childFile in files!!) {
- val fileMap: WritableMap = WritableNativeMap()
- fileMap.putString("name", childFile.name)
- fileMap.putString("path", childFile.absolutePath)
- fileMap.putBoolean("isDirectory", childFile.isDirectory)
- fileMaps.pushMap(fileMap)
- }
- return fileMaps
- }
-
- private fun decompressStream(input: InputStream?): InputStream {
- val pb = PushbackInputStream(input, 2)
- val signature = ByteArray(2)
- val len = pb.read(signature)
- if(len == -1) return pb;
- pb.unread(signature, 0, len)
- return if (signature[0] == 0x1f.toByte() && signature[1] == 0x8b.toByte())
- GZIPInputStream(pb) else pb
- }
-
- override fun downloadFile(
- url: String,
- destPath: String,
- method: String,
- headers: ReadableMap,
- body: String?,
- promise: Promise
- ) {
- coroutineScope.launch {
- try {
- val headersBuilder = Headers.Builder()
- headers.entryIterator.forEach { entry ->
- headersBuilder.add(entry.key, entry.value.toString())
- }
- val requestBuilder = Request.Builder()
- .url(url)
- .headers(headersBuilder.build())
- if (method.lowercase() == "get") {
- requestBuilder.get()
- } else if (body != null) {
- requestBuilder.post(body.toRequestBody())
- }
-
- okHttpClient.newCall(requestBuilder.build())
- .enqueue(object : Callback {
- override fun onFailure(call: Call, e: IOException) {
- promise.reject(e)
- }
-
- override fun onResponse(call: Call, response: Response) {
- response.use {
- if (!it.isSuccessful || it.body == null) {
- promise.reject(Exception("Failed to download: ${it.code}"))
- return
- }
- try {
- decompressStream(it.body!!.byteStream()).use { inputStream ->
- FileOutputStream(destPath).use { fos ->
- inputStream.copyTo(fos, BUFFER_SIZE)
- }
- }
- promise.resolve(null)
- } catch (e: Exception) {
- promise.reject(e)
- }
- }
- }
- })
- } catch (e: Exception) {
- promise.reject(e)
- }
- }
- }
-
- override fun getTypedExportedConstants(): MutableMap {
- val constants: MutableMap = HashMap()
- val externalDirectory = this.reactApplicationContext.getExternalFilesDir(null)
- if (externalDirectory != null) {
- constants["ExternalDirectoryPath"] = externalDirectory.absolutePath
- }
- val externalCachesDirectory = this.reactApplicationContext.externalCacheDir
- if (externalCachesDirectory != null) {
- constants["ExternalCachesDirectoryPath"] = externalCachesDirectory.absolutePath
- }
- return constants
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativePackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativePackage.kt
deleted file mode 100644
index 62117a93b..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativePackage.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.rajarsheechatterjee.NativeFile
-
-import com.facebook.react.BaseReactPackage
-import com.facebook.react.bridge.NativeModule
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.module.model.ReactModuleInfo
-import com.facebook.react.module.model.ReactModuleInfoProvider
-import com.lnreader.spec.NativeFileSpec
-
-class NativePackage : BaseReactPackage() {
- override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
- if (name == NativeFileSpec.NAME) {
- NativeFile(reactContext)
- } else {
- null
- }
-
- override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
- mapOf(
- NativeFileSpec.NAME to ReactModuleInfo(
- NativeFileSpec.NAME,
- NativeFileSpec.NAME,
- canOverrideExistingModule = false,
- needsEagerInit = false,
- isCxxModule = false,
- isTurboModule = true
- )
- )
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControl.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControl.kt
deleted file mode 100644
index b7adcc413..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControl.kt
+++ /dev/null
@@ -1,355 +0,0 @@
-package com.rajarsheechatterjee.NativeTTSMediaControl
-
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.os.Build
-import android.support.v4.media.MediaMetadataCompat
-import android.support.v4.media.session.MediaSessionCompat
-import android.support.v4.media.session.PlaybackStateCompat
-import androidx.core.app.NotificationCompat
-import androidx.media.app.NotificationCompat.MediaStyle
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.modules.core.DeviceEventManagerModule
-import com.lnreader.spec.NativeTTSMediaControlSpec
-import java.io.File
-import java.net.URL
-
-class NativeTTSMediaControl(private val appContext: ReactApplicationContext) :
- NativeTTSMediaControlSpec(appContext) {
-
- companion object {
- private const val CHANNEL_ID = "tts-media-controls"
- private const val NOTIFICATION_ID = 1001
- private const val ACTION_PLAY = "com.lnreader.TTS_PLAY"
- private const val ACTION_PAUSE = "com.lnreader.TTS_PAUSE"
- private const val ACTION_STOP = "com.lnreader.TTS_STOP"
- private const val ACTION_PREV = "com.lnreader.TTS_PREV"
- private const val ACTION_NEXT = "com.lnreader.TTS_NEXT"
- private const val ACTION_REWIND = "com.lnreader.TTS_REWIND"
- }
-
- private var mediaSession: MediaSessionCompat? = null
- private var isPlaying = false
- private var listenerCount = 0
- private var coverBitmap: Bitmap? = null
- private var currentCoverUri: String? = null
- private var currentTitle: String? = null
- private var currentSubtitle: String? = null
- private var receiverRegistered = false
- private var currentPosition: Long = 0L
- private var totalDuration: Long = 0L
-
- private val mediaReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- when (intent.action) {
- ACTION_PLAY -> {
- isPlaying = true
- sendEvent("TTSPlay")
- updateNotification()
- }
- ACTION_PAUSE -> {
- isPlaying = false
- sendEvent("TTSPause")
- updateNotification()
- }
- ACTION_STOP -> sendEvent("TTSStop")
- ACTION_PREV -> sendEvent("TTSPrev")
- ACTION_NEXT -> sendEvent("TTSNext")
- ACTION_REWIND -> sendEvent("TTSRewind")
- }
- }
- }
-
- private fun sendEvent(eventName: String) {
- if (listenerCount > 0) {
- appContext.getJSModule(
- DeviceEventManagerModule.RCTDeviceEventEmitter::class.java
- ).emit(eventName, null)
- }
- }
-
- private fun sendSeekEvent(elementIndex: Long) {
- if (listenerCount > 0) {
- val params = com.facebook.react.bridge.Arguments.createMap().apply {
- putInt("position", elementIndex.toInt())
- }
- appContext.getJSModule(
- DeviceEventManagerModule.RCTDeviceEventEmitter::class.java
- ).emit("TTSSeekTo", params)
- }
- }
-
- private fun ensureChannel() {
- val manager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val channel = NotificationChannel(
- CHANNEL_ID,
- "TTS Media Controls",
- NotificationManager.IMPORTANCE_LOW
- ).apply {
- description = "Text-to-speech playback controls"
- setShowBadge(false)
- }
- manager.createNotificationChannel(channel)
- }
- }
-
- private fun ensureMediaSession() {
- if (mediaSession == null) {
- mediaSession = MediaSessionCompat(appContext, "LNReaderTTS").apply {
- setCallback(object : MediaSessionCompat.Callback() {
- override fun onPlay() {
- isPlaying = true
- sendEvent("TTSPlay")
- updateNotification()
- }
-
- override fun onPause() {
- isPlaying = false
- sendEvent("TTSPause")
- updateNotification()
- }
-
- override fun onStop() {
- sendEvent("TTSStop")
- }
-
- override fun onSkipToPrevious() {
- sendEvent("TTSPrev")
- }
-
- override fun onSkipToNext() {
- sendEvent("TTSNext")
- }
-
- override fun onSeekTo(pos: Long) {
- // pos is in our scaled ms domain: elementIndex * 1000
- val elementIndex = pos / 1000L
- currentPosition = elementIndex
- updateNotification()
- sendSeekEvent(elementIndex)
- }
- })
- isActive = true
- }
- }
- }
-
- private fun registerReceiver() {
- if (!receiverRegistered) {
- val filter = IntentFilter().apply {
- addAction(ACTION_PLAY)
- addAction(ACTION_PAUSE)
- addAction(ACTION_STOP)
- addAction(ACTION_PREV)
- addAction(ACTION_NEXT)
- addAction(ACTION_REWIND)
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- appContext.registerReceiver(mediaReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
- } else {
- appContext.registerReceiver(mediaReceiver, filter)
- }
- receiverRegistered = true
- }
- }
-
- private fun buildPendingIntent(action: String): PendingIntent {
- val intent = Intent(action).setPackage(appContext.packageName)
- return PendingIntent.getBroadcast(
- appContext,
- action.hashCode(),
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- }
-
- private fun getContentIntent(): PendingIntent {
- val intent = appContext.packageManager.getLaunchIntentForPackage(appContext.packageName)
- ?: Intent()
- return PendingIntent.getActivity(
- appContext,
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- }
-
- private fun updateNotification() {
- val session = mediaSession ?: return
-
- val stateBuilder = PlaybackStateCompat.Builder()
- .setActions(
- PlaybackStateCompat.ACTION_PLAY_PAUSE or
- PlaybackStateCompat.ACTION_STOP or
- PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
- PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
- PlaybackStateCompat.ACTION_SEEK_TO
- )
- .setState(
- if (isPlaying) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED,
- currentPosition * 1000L,
- 0f
- )
- session.setPlaybackState(stateBuilder.build())
-
- val metadataBuilder = MediaMetadataCompat.Builder()
- .putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentSubtitle ?: "")
- .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentTitle ?: "")
- .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, totalDuration * 1000L)
- coverBitmap?.let {
- metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, it)
- }
- session.setMetadata(metadataBuilder.build())
-
- val playPauseAction = if (isPlaying) {
- NotificationCompat.Action.Builder(
- android.R.drawable.ic_media_pause,
- "Pause",
- buildPendingIntent(ACTION_PAUSE)
- ).build()
- } else {
- NotificationCompat.Action.Builder(
- android.R.drawable.ic_media_play,
- "Play",
- buildPendingIntent(ACTION_PLAY)
- ).build()
- }
-
- val prevAction = NotificationCompat.Action.Builder(
- android.R.drawable.ic_media_previous,
- "Previous",
- buildPendingIntent(ACTION_PREV)
- ).build()
-
- val rewindAction = NotificationCompat.Action.Builder(
- android.R.drawable.ic_media_rew,
- "Replay",
- buildPendingIntent(ACTION_REWIND)
- ).build()
-
- val nextAction = NotificationCompat.Action.Builder(
- android.R.drawable.ic_media_next,
- "Next",
- buildPendingIntent(ACTION_NEXT)
- ).build()
-
- val notification = NotificationCompat.Builder(appContext, CHANNEL_ID)
- .setContentTitle(currentSubtitle)
- .setContentText(currentTitle)
- .setSmallIcon(appContext.applicationInfo.icon)
- .setLargeIcon(coverBitmap)
- .setContentIntent(getContentIntent())
- .setDeleteIntent(buildPendingIntent(ACTION_STOP))
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setOngoing(isPlaying)
- .addAction(prevAction)
- .addAction(rewindAction)
- .addAction(playPauseAction)
- .addAction(nextAction)
- .setStyle(
- MediaStyle()
- .setMediaSession(session.sessionToken)
- .setShowActionsInCompactView(1, 2, 3)
- )
- .build()
-
- val manager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- manager.notify(NOTIFICATION_ID, notification)
- }
-
- private fun loadCoverBitmap(coverUri: String) {
- if (coverUri == currentCoverUri && coverBitmap != null) return
- currentCoverUri = coverUri
-
- if (coverUri.isBlank()) {
- coverBitmap = null
- return
- }
-
- if (coverUri.startsWith("file://")) {
- val path = coverUri.removePrefix("file://").split("?")[0]
- val file = File(path)
- if (file.exists()) {
- coverBitmap = BitmapFactory.decodeFile(file.absolutePath)
- }
- } else if (coverUri.startsWith("http")) {
- coverBitmap = null
- Thread {
- try {
- val stream = URL(coverUri).openStream()
- val bitmap = BitmapFactory.decodeStream(stream)
- stream.close()
- coverBitmap = bitmap
- updateNotification()
- } catch (_: Exception) {
- // Silently fail — notification will show without cover
- }
- }.start()
- }
- }
-
- override fun showMediaNotification(
- title: String,
- subtitle: String,
- coverUri: String,
- isPlaying: Boolean
- ) {
- this.isPlaying = isPlaying
- this.currentTitle = title
- this.currentSubtitle = subtitle
-
- ensureChannel()
- ensureMediaSession()
- registerReceiver()
- loadCoverBitmap(coverUri)
- updateNotification()
- }
-
- override fun updatePlaybackState(isPlaying: Boolean) {
- this.isPlaying = isPlaying
- updateNotification()
- }
-
- override fun updateProgress(current: Double, total: Double) {
- currentPosition = current.toLong()
- totalDuration = total.toLong()
- updateNotification()
- }
-
- override fun dismiss() {
- mediaSession?.isActive = false
- mediaSession?.release()
- mediaSession = null
-
- val manager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- manager.cancel(NOTIFICATION_ID)
-
- if (receiverRegistered) {
- try {
- appContext.unregisterReceiver(mediaReceiver)
- } catch (_: Exception) {}
- receiverRegistered = false
- }
-
- coverBitmap = null
- currentCoverUri = null
- currentPosition = 0L
- totalDuration = 0L
- }
-
- override fun addListener(eventName: String?) {
- listenerCount++
- }
-
- override fun removeListeners(count: Double) {
- listenerCount = (listenerCount - count.toInt()).coerceAtLeast(0)
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControlPackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControlPackage.kt
deleted file mode 100644
index 9b87ad07e..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControlPackage.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.rajarsheechatterjee.NativeTTSMediaControl
-
-import com.facebook.react.BaseReactPackage
-import com.facebook.react.bridge.NativeModule
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.module.model.ReactModuleInfo
-import com.facebook.react.module.model.ReactModuleInfoProvider
-import com.lnreader.spec.NativeTTSMediaControlSpec
-
-class NativeTTSMediaControlPackage : BaseReactPackage() {
- override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
- if (name == NativeTTSMediaControlSpec.NAME) {
- NativeTTSMediaControl(reactContext)
- } else {
- null
- }
-
- override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
- mapOf(
- NativeTTSMediaControlSpec.NAME to ReactModuleInfo(
- NativeTTSMediaControlSpec.NAME,
- NativeTTSMediaControlSpec.NAME,
- canOverrideExistingModule = false,
- needsEagerInit = false,
- isCxxModule = false,
- isTurboModule = true
- )
- )
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListener.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListener.kt
deleted file mode 100644
index 601c19429..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListener.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.rajarsheechatterjee.NativeVolumeButtonListener
-
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.modules.core.DeviceEventManagerModule
-import com.lnreader.spec.NativeVolumeButtonListenerSpec
-
-class NativeVolumeButtonListener(appContext: ReactApplicationContext) :
- NativeVolumeButtonListenerSpec(appContext) {
- init {
- NativeVolumeButtonListener.appContext = appContext
- }
-
- override fun addListener(eventName: String?) {
- isActive = true
- }
-
- override fun removeListeners(count: Double) {
- isActive = false
- }
-
- companion object {
- lateinit var appContext: ReactApplicationContext
- var isActive = false
-
- fun sendEvent(up: Boolean) {
- if (isActive) {
- appContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
- .emit(if (up) "VolumeUp" else "VolumeDown", null)
- }
- }
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListenerPackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListenerPackage.kt
deleted file mode 100644
index c66df9baf..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListenerPackage.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.rajarsheechatterjee.NativeVolumeButtonListener
-
-import com.facebook.react.BaseReactPackage
-import com.facebook.react.bridge.NativeModule
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.module.model.ReactModuleInfo
-import com.facebook.react.module.model.ReactModuleInfoProvider
-import com.lnreader.spec.NativeVolumeButtonListenerSpec
-
-class NativeVolumeButtonListenerPackage : BaseReactPackage() {
- override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
- if (name == NativeVolumeButtonListenerSpec.NAME) {
- NativeVolumeButtonListener(reactContext)
- } else {
- null
- }
-
- override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
- mapOf(
- NativeVolumeButtonListenerSpec.NAME to ReactModuleInfo(
- NativeVolumeButtonListenerSpec.NAME,
- NativeVolumeButtonListenerSpec.NAME,
- canOverrideExistingModule = false,
- needsEagerInit = false,
- isCxxModule = false,
- isTurboModule = true
- )
- )
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchive.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchive.kt
deleted file mode 100644
index 81b1276f4..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchive.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-package com.rajarsheechatterjee.NativeZipArchive
-
-import com.facebook.react.bridge.Promise
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.bridge.ReactMethod
-import com.facebook.react.bridge.ReadableMap
-import com.lnreader.spec.NativeZipArchiveSpec
-import java.io.File
-import java.io.FileOutputStream
-import java.net.HttpURLConnection
-import java.net.URL
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipInputStream
-import java.util.zip.ZipOutputStream
-
-class NativeZipArchive(context: ReactApplicationContext) : NativeZipArchiveSpec(context) {
- @ReactMethod
- override fun unzip(sourceFilePath: String, distDirPath: String, promise: Promise) {
- Thread {
- try {
- ZipFile(sourceFilePath).use { zis ->
- zis.entries().asSequence().filterNot { it.isDirectory }.forEach { zipEntry ->
- val newFile = File(distDirPath, zipEntry.name)
- newFile.parentFile?.mkdirs()
- zis.getInputStream(zipEntry).use { inputStream ->
- FileOutputStream(newFile).use { fos -> inputStream.copyTo(fos, 4096) }
- }
- Thread.yield()
- }
- }
- promise.resolve(null)
- } catch (e: Exception) {
- promise.reject(e)
- }
- }.start()
- }
-
- @ReactMethod
- override fun zip(sourceDirPath: String, zipFilePath: String, promise: Promise) {
- Thread {
- try {
- FileOutputStream(zipFilePath).use { fos ->
- ZipOutputStream(fos).use { zos -> zipProcess(sourceDirPath, zos) }
- }
- promise.resolve(null)
- } catch (e: Exception) {
- promise.reject(e)
- }
- }.start()
- }
-
- @ReactMethod
- override fun remoteUnzip(
- distDirPath: String,
- urlString: String,
- headers: ReadableMap,
- promise: Promise
- ) {
- val connection = URL(urlString).openConnection() as HttpURLConnection
- Thread {
- try {
- connection.requestMethod = "GET"
- val it = headers.entryIterator
- while (it.hasNext()) {
- val (key, value) = it.next()
- connection.setRequestProperty(key, value.toString())
- }
- ZipInputStream(connection.inputStream).use { zis ->
- generateSequence { zis.nextEntry }
- .filterNot { it.isDirectory }
- .forEach { zipEntry ->
- val newFile = File(distDirPath, zipEntry.name)
- newFile.parentFile?.mkdirs()
- FileOutputStream(newFile).use { fos -> zis.copyTo(fos, 4096) }
- Thread.yield()
- }
- }
- if (connection.responseCode == 200) {
- promise.resolve(null)
- } else {
- throw Exception("Network request failed")
- }
- } catch (e: Exception) {
- promise.reject(e)
- } finally {
- connection.disconnect()
- }
- }.start()
- }
-
- private fun zipProcess(sourceDirPath: String, zos: ZipOutputStream) {
- val sourceDir = File(sourceDirPath)
- sourceDir.walkBottomUp().filter { it.isFile }.forEach { file ->
- val zipFileName =
- file.absolutePath.removePrefix(sourceDir.absolutePath).removePrefix("/")
- val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
- zos.putNextEntry(entry)
- file.inputStream().use { fis ->
- fis.copyTo(zos, 4096)
- fis.close()
- }
- Thread.yield()
- }
- }
-
- @ReactMethod
- override fun remoteZip(
- sourceDirPath: String,
- urlString: String,
- headers: ReadableMap,
- promise: Promise
- ) {
- Thread {
- val connection = URL(urlString).openConnection() as HttpURLConnection
- try {
- connection.requestMethod = "POST"
- val it = headers.entryIterator
- while (it.hasNext()) {
- val (key, value) = it.next()
- connection.setRequestProperty(key, value.toString())
- }
- ZipOutputStream(connection.outputStream).use { zipProcess(sourceDirPath, it) }
- if (connection.responseCode == 200) {
- promise.resolve(
- connection.inputStream.bufferedReader().use { it.readText() })
- } else {
- throw Exception("Network request failed")
- }
- } catch (e: Exception) {
- promise.reject(e)
- } finally {
- connection.disconnect()
- }
- }.start()
- }
-}
diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchivePackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchivePackage.kt
deleted file mode 100644
index 3f2d05dd1..000000000
--- a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchivePackage.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.rajarsheechatterjee.NativeZipArchive
-
-import com.facebook.react.BaseReactPackage
-import com.facebook.react.bridge.NativeModule
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.module.model.ReactModuleInfo
-import com.facebook.react.module.model.ReactModuleInfoProvider
-import com.lnreader.spec.NativeZipArchiveSpec
-
-class NativeZipArchivePackage : BaseReactPackage() {
- override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
- if (name == NativeZipArchiveSpec.NAME) {
- NativeZipArchive(reactContext)
- } else {
- null
- }
-
- override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
- mapOf(
- NativeZipArchiveSpec.NAME to ReactModuleInfo(
- NativeZipArchiveSpec.NAME,
- NativeZipArchiveSpec.NAME,
- canOverrideExistingModule = false,
- needsEagerInit = false,
- isCxxModule = false,
- isTurboModule = true
- )
- )
- }
-}
diff --git a/android/app/src/main/jni/CMakeLists.txt b/android/app/src/main/jni/CMakeLists.txt
deleted file mode 100644
index 046708789..000000000
--- a/android/app/src/main/jni/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 3.13)
-
-# Define the library name here.
-project(appmodules)
-
-# This file includes all the necessary to let you build your React Native application
-include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
-
-# Define where the additional source code lives. We need to crawl back the jni, main, src, app, android folders
-target_sources(${CMAKE_PROJECT_NAME} PRIVATE
- ../../../../../shared/NativeEpub.cpp
- ../../../../../shared/Epub.cpp
- ../../../../../shared/pugixml.cpp
-)
-
-# Define where CMake can find the additional header files. We need to crawl back the jni, main, src, app, android folders
-target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ../../../../../shared)
\ No newline at end of file
diff --git a/android/app/src/main/jni/OnLoad.cpp b/android/app/src/main/jni/OnLoad.cpp
deleted file mode 100644
index bf31f60b5..000000000
--- a/android/app/src/main/jni/OnLoad.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-// This C++ file is part of the default configuration used by apps and is placed
-// inside react-native to encapsulate it from user space (so you won't need to
-// touch C++/Cmake code at all on Android).
-//
-// If you wish to customize it (because you want to manually link a C++ library
-// or pass a custom compilation flag) you can:
-//
-// 1. Copy this CMake file inside the `android/app/src/main/jni` folder of your
-// project
-// 2. Copy the OnLoad.cpp (in this same folder) file inside the same folder as
-// above.
-// 3. Extend your `android/app/build.gradle` as follows
-//
-// android {
-// // Other config here...
-// externalNativeBuild {
-// cmake {
-// path "src/main/jni/CMakeLists.txt"
-// }
-// }
-// }
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef REACT_NATIVE_APP_CODEGEN_HEADER
-#include REACT_NATIVE_APP_CODEGEN_HEADER
-#endif
-#ifdef REACT_NATIVE_APP_COMPONENT_DESCRIPTORS_HEADER
-#include REACT_NATIVE_APP_COMPONENT_DESCRIPTORS_HEADER
-#endif
-
-namespace facebook::react
-{
-
- void registerComponents(
- std::shared_ptr registry)
- {
- // Custom Fabric Components go here. You can register custom
- // components coming from your App or from 3rd party libraries here.
- //
- // providerRegistry->add(concreteComponentDescriptorProvider<
- // MyComponentDescriptor>());
-
- // We link app local components if available
-#ifdef REACT_NATIVE_APP_COMPONENT_REGISTRATION
- REACT_NATIVE_APP_COMPONENT_REGISTRATION(registry);
-#endif
-
- // And we fallback to the components autolinked
- autolinking_registerProviders(registry);
- }
-
- std::shared_ptr cxxModuleProvider(
- const std::string &name,
- const std::shared_ptr &jsInvoker)
- {
- // Here you can provide your CXX Turbo Modules coming from
- // either your application or from external libraries. The approach to follow
- // is similar to the following (for a module called `NativeCxxModuleExample`):
- //
- // if (name == NativeCxxModuleExample::kModuleName) {
- // return std::make_shared(jsInvoker);
- // }
-
- if (name == NativeEpub::kModuleName)
- {
- return std::make_shared(jsInvoker);
- }
-
- // And we fallback to the CXX module providers autolinked
- return autolinking_cxxModuleProvider(name, jsInvoker);
- }
-
- std::shared_ptr javaModuleProvider(
- const std::string &name,
- const JavaTurboModule::InitParams ¶ms)
- {
- // Here you can provide your own module provider for TurboModules coming from
- // either your application or from external libraries. The approach to follow
- // is similar to the following (for a library called `samplelibrary`):
- //
- // auto module = samplelibrary_ModuleProvider(name, params);
- // if (module != nullptr) {
- // return module;
- // }
- // return FBReactNativeSpec_ModuleProvider(name, params);
-
- // We link app local modules if available
-#ifdef REACT_NATIVE_APP_MODULE_PROVIDER
- auto module = REACT_NATIVE_APP_MODULE_PROVIDER(name, params);
- if (module != nullptr)
- {
- return module;
- }
-#endif
-
- // We first try to look up core modules
- if (auto module = FBReactNativeSpec_ModuleProvider(name, params))
- {
- return module;
- }
-
- // And we fallback to the module providers autolinked
- if (auto module = autolinking_ModuleProvider(name, params))
- {
- return module;
- }
-
- return nullptr;
- }
-
-} // namespace facebook::react
-
-JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
-{
- return facebook::jni::initialize(vm, []
- {
- facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider =
- &facebook::react::cxxModuleProvider;
- facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
- &facebook::react::javaModuleProvider;
- facebook::react::DefaultComponentsRegistry::
- registerComponentDescriptorsFromEntryPoint =
- &facebook::react::registerComponents; });
-}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable-hdpi/ic_action_name.png b/android/app/src/main/res/drawable-hdpi/ic_action_name.png
deleted file mode 100644
index a757dda43..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/ic_action_name.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/notification_icon.png b/android/app/src/main/res/drawable-hdpi/notification_icon.png
deleted file mode 100644
index 39c04b73d..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/notification_icon.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_action_name.png b/android/app/src/main/res/drawable-mdpi/ic_action_name.png
deleted file mode 100644
index b70c04c95..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/ic_action_name.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/notification_icon.png b/android/app/src/main/res/drawable-mdpi/notification_icon.png
deleted file mode 100644
index 3d40c8038..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/notification_icon.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_action_name.png b/android/app/src/main/res/drawable-xhdpi/ic_action_name.png
deleted file mode 100644
index 854745935..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/ic_action_name.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/notification_icon.png b/android/app/src/main/res/drawable-xhdpi/notification_icon.png
deleted file mode 100644
index 3cc027388..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/notification_icon.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png b/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png
deleted file mode 100644
index 54aed9a19..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/notification_icon.png b/android/app/src/main/res/drawable-xxhdpi/notification_icon.png
deleted file mode 100644
index 4d5d81187..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/notification_icon.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png b/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png
deleted file mode 100644
index eee1fd324..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/notification_icon.png b/android/app/src/main/res/drawable-xxxhdpi/notification_icon.png
deleted file mode 100644
index a5626d8f3..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/notification_icon.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable/invisible.xml b/android/app/src/main/res/drawable/invisible.xml
deleted file mode 100644
index 91e9e2224..000000000
--- a/android/app/src/main/res/drawable/invisible.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/app/src/main/res/drawable/rn_edit_text_material.xml b/android/app/src/main/res/drawable/rn_edit_text_material.xml
deleted file mode 100644
index bb6f578c3..000000000
--- a/android/app/src/main/res/drawable/rn_edit_text_material.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/splashscreen.xml b/android/app/src/main/res/drawable/splashscreen.xml
deleted file mode 100644
index 9f19c56e5..000000000
--- a/android/app/src/main/res/drawable/splashscreen.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/android/app/src/main/res/layout/launch_screen.xml b/android/app/src/main/res/layout/launch_screen.xml
deleted file mode 100644
index f3ce30a5f..000000000
--- a/android/app/src/main/res/layout/launch_screen.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
deleted file mode 100644
index 6521c9a7b..000000000
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp
deleted file mode 100644
index 53108616e..000000000
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
deleted file mode 100644
index bf5b28fef..000000000
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 377dc288f..000000000
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index 863294f0a..000000000
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
deleted file mode 100644
index 4d20a97ef..000000000
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp
deleted file mode 100644
index 0154c60bf..000000000
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
deleted file mode 100644
index 1db6cc7e5..000000000
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 8903564af..000000000
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index 7971a36f6..000000000
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
deleted file mode 100644
index 7f81c2812..000000000
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp
deleted file mode 100644
index 73b28cba0..000000000
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
deleted file mode 100644
index d57054667..000000000
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 4346ebe9c..000000000
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 53f189a30..000000000
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
deleted file mode 100644
index 4819c75d3..000000000
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp
deleted file mode 100644
index 74a42e76a..000000000
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
deleted file mode 100644
index 3752fa4f0..000000000
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 508786cb4..000000000
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index eee3f1008..000000000
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
deleted file mode 100644
index 2a0753a4a..000000000
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp
deleted file mode 100644
index f9c00433b..000000000
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
deleted file mode 100644
index f6f35e81f..000000000
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index f194028f0..000000000
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 17cbf24de..000000000
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/android/app/src/main/res/raw/loading.json b/android/app/src/main/res/raw/loading.json
deleted file mode 100644
index 906dbc98d..000000000
--- a/android/app/src/main/res/raw/loading.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{"v":"5.9.0","fr":90,"ip":0,"op":100,"w":500,"h":500,"nm":"Untitled file","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[0]},{"t":30,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[262.75,248.531,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.281,0.906],[0,0],[-0.188,0.625],[0.188,0.281]],"o":[[0,0],[0,0],[0.156,-0.531],[0,0],[0.188,-0.625],[-0.531,-0.5]],"v":[[34.531,-109.562],[34.938,-103.844],[35.594,-105.625],[36.875,-106.875],[37.688,-108.219],[37.5,-109.344]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.453,99.743],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42,"s":[0]},{"t":43,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[21,65],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25,-109.5],"ix":2},"a":{"a":0,"k":[0,-32.5],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":42,"s":[100,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[100,100]},{"t":71,"s":[100,101.54]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[101,18.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.25,-35.625],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[0,100]},{"t":52,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[122.25,18.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.625,-75.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":45,"s":[0,100]},{"t":50,"s":[93.456,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[26.625,-68.125],"ix":2},"a":{"a":0,"k":[26.625,-68.125],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-122,-60],"ix":2},"a":{"a":0,"k":[-32,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":32,"s":[0,47.945]},{"t":38,"s":[119.685,47.945]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-95],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[0,47]},{"t":35,"s":[87.222,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,9.5],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":36,"s":[0,47]},{"t":41,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 3","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-26],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":34,"s":[0,47]},{"t":39,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 4","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,62],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-56.8,50.5],"ix":2},"a":{"a":0,"k":[0,-31],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[100,0]},{"t":49,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k"
-:0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[50.8,15],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-98.2,44],"ix":2},"a":{"a":0,"k":[-25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":40,"s":[0,100]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.6,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-97.9,98],"ix":2},"a":{"a":0,"k":[-16,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[0,99.889]},{"t":49,"s":[100,99.889]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,80.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-106.75,36.525],"ix":2},"a":{"a":0,"k":[0,-40.375],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":39,"s":[100,0]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":-0.026,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-81.5,76.875],"ix":2},"a":{"a":0,"k":[-81.5,76.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19.5,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-30.125,3],"ix":2},"a":{"a":0,"k":[0,-13],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19,26.779],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[81.562,3],"ix":2},"a":{"a":0,"k":[0,-14],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[131,18.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.5,-5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":50,"s":[0,100]},{"t":56,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[25.562,8.062],"ix":2},"a":{"a":0,"k":[25.562,8.062],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[65.5,111],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[-13.5,66.75],"to":[0,0],"ti":[0,0]},{"t":63,"s":[-46,120]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":57,"s":[100,100]},{"t":63,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":57,"s":[0]},{"t":63,"s":[90]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.438,0.188],[0,0],[0,0],[41.507,0],[0,0],[-6.5,54.25],[0,0],[0,0]],"o":[[-9.438,-0.188],[0,0],[0,0],[-0.25,0],[0,0],[0.328,-2.742],[0,0],[0,0]],"v":[[14,17.438],[-3.313,17.5],[-3.5,50.875],[-45,95.5],[-38.5,116.75],[14.25,61.25],[14.5,22.75],[14.656,21.688]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-13.318,67.059],"ix":2},"a":{"a":0,"k":[-13.318,67.059],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[75,104.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[62.75,62.5],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63,"s":[60,96],"to":[0,0],"ti":[0,0]},{"t":68,"s":[98,74]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[100,100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":63,"s":[100,62.5]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":64,"s":[110,50]},{"t":68,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":63,"s":[0]},{"t":68,"s":[-160]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13,0],[0,0],[-3.938,11.938],[0,0],[0,0],[0,0],[6.5,0],[0,0],[0.31,6.262],[0,0],[9.062,0.125]],"o":[[0,0],[0,0],[13,0],[0,0],[4.009,-12.155],[0,0],[0,0],[0,0],[-6.5,0],[0,0],[-0.312,-6.312],[0,0],[-9.062,-0.125]],"v":[[31.062,16.938],[30.812,95.938],[44.062,109.938],[73.688,109.938],[90.312,97.188],[95.812,74.438],[79.062,69.688],[74.562,83.938],[66.75,90.625],[56.062,90.938],[50.562,84.938],[50.312,21.312],[50.125,17]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[63.312,63.43],"ix":2},"a":{"a":0,"k":[63.312,63.43],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[0,0,100]},{"t":30,"s":[340,340,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 3","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847100019455,0.890200018883,0.905900001526,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":5,"s":[0,0,100]},{"t":25,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.070600003004,0.431400001049,0.505900025368,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ellipse 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"t":20,"s":[500,500,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 4","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074500001967,0.172499999404,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
deleted file mode 100644
index f0824a660..000000000
--- a/android/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- #FFFFFF
- #1F2024
- #023c69
- #202125
- #202125
- #00adb5
-
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
deleted file mode 100644
index b897e3b63..000000000
--- a/android/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- LNReader
-
\ No newline at end of file
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
deleted file mode 100644
index 099ab50aa..000000000
--- a/android/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
deleted file mode 100644
index d7e8f5a1c..000000000
--- a/android/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-buildscript {
- ext {
- buildToolsVersion = "36.0.0"
- minSdkVersion = 24
- compileSdkVersion = 36
- targetSdkVersion = 36
- ndkVersion = "27.1.12297006"
- kotlinVersion = "2.1.20"
- }
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath("com.android.tools.build:gradle")
- classpath("com.facebook.react:react-native-gradle-plugin")
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
- }
-}
-
-apply plugin: "com.facebook.react.rootproject"
-apply plugin: "expo-root-project"
diff --git a/android/gradle.properties b/android/gradle.properties
deleted file mode 100644
index 840d7b8aa..000000000
--- a/android/gradle.properties
+++ /dev/null
@@ -1,56 +0,0 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-
-# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
-
-org.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=512m
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
-# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app's APK
-# https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
-
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
-
-# Use this property to specify which architecture you want to build.
-# You can also override it from the CLI using
-# ./gradlew -PreactNativeArchitectures=x86_64
-reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
-# Use this property to enable support to the new architecture.
-# This will allow you to use TurboModules and the Fabric render in
-# your application. You should enable this flag either if you want
-# to write custom TurboModules/Fabric components OR use libraries that
-# are providing them.
-newArchEnabled=true
-
-# Use this property to enable or disable the Hermes JS engine.
-# If set to false, you will be using JSC instead.
-hermesEnabled=true
-
-# Enable GIF support in React Native images (~200 B increase)
-expo.gif.enabled=false
-# Enable webp support in React Native images (~85 KB increase)
-expo.webp.enabled=false
-# Enable animated webp support (~3.4 MB increase)
-# Disabled by default because iOS doesn't support animated webp
-expo.webp.animated=false
-
-# Use this property to enable edge-to-edge display support.
-# This allows your app to draw behind system bars for an immersive UI.
-# Note: Only works with ReactActivity and should not be used with custom Activity.
-edgeToEdgeEnabled=false
\ No newline at end of file
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 8bdaf60c7..000000000
Binary files a/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 2a84e188b..000000000
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
-networkTimeout=10000
-validateDistributionUrl=true
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/android/gradlew b/android/gradlew
deleted file mode 100755
index bbb003279..000000000
--- a/android/gradlew
+++ /dev/null
@@ -1,251 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015 the original authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-##############################################################################
-#
-# Gradle start up script for POSIX generated by Gradle.
-#
-# Important for running:
-#
-# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-# noncompliant, but you have some other compliant shell such as ksh or
-# bash, then to run this script, type that shell name before the whole
-# command line, like:
-#
-# ksh Gradle
-#
-# Busybox and similar reduced shells will NOT work, because this script
-# requires all of these POSIX shell features:
-# * functions;
-# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-# * compound commands having a testable exit status, especially «case»;
-# * various built-in commands including «command», «set», and «ulimit».
-#
-# Important for patching:
-#
-# (2) This script targets any POSIX shell, so it avoids extensions provided
-# by Bash, Ksh, etc; in particular arrays are avoided.
-#
-# The "traditional" practice of packing multiple parameters into a
-# space-separated string is a well documented source of bugs and security
-# problems, so this is (mostly) avoided, by progressively accumulating
-# options in "$@", and eventually passing that to Java.
-#
-# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-# see the in-line comments for details.
-#
-# There are tweaks for specific operating systems such as AIX, CygWin,
-# Darwin, MinGW, and NonStop.
-#
-# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-# within the Gradle project.
-#
-# You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
- APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
- [ -h "$app_path" ]
-do
- ls=$( ls -ld "$app_path" )
- link=${ls#*' -> '}
- case $link in #(
- /*) app_path=$link ;; #(
- *) app_path=$APP_HOME$link ;;
- esac
-done
-
-# This is normally unused
-# shellcheck disable=SC2034
-APP_BASE_NAME=${0##*/}
-# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
- echo "$*"
-} >&2
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in #(
- CYGWIN* ) cygwin=true ;; #(
- Darwin* ) darwin=true ;; #(
- MSYS* | MINGW* ) msys=true ;; #(
- NONSTOP* ) nonstop=true ;;
-esac
-
-CLASSPATH="\\\"\\\""
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD=$JAVA_HOME/jre/sh/java
- else
- JAVACMD=$JAVA_HOME/bin/java
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD=java
- if ! command -v java >/dev/null 2>&1
- then
- die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
- case $MAX_FD in #(
- max*)
- # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- MAX_FD=$( ulimit -H -n ) ||
- warn "Could not query maximum file descriptor limit"
- esac
- case $MAX_FD in #(
- '' | soft) :;; #(
- *)
- # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- ulimit -n "$MAX_FD" ||
- warn "Could not set maximum file descriptor limit to $MAX_FD"
- esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-# * args from the command line
-# * the main class name
-# * -classpath
-# * -D...appname settings
-# * --module-path (only if needed)
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
- APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
- JAVACMD=$( cygpath --unix "$JAVACMD" )
-
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- for arg do
- if
- case $arg in #(
- -*) false ;; # don't mess with options #(
- /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
- [ -e "$t" ] ;; #(
- *) false ;;
- esac
- then
- arg=$( cygpath --path --ignore --mixed "$arg" )
- fi
- # Roll the args list around exactly as many times as the number of
- # args, so each arg winds up back in the position where it started, but
- # possibly modified.
- #
- # NB: a `for` loop captures its iteration list before it begins, so
- # changing the positional parameters here affects neither the number of
- # iterations, nor the values presented in `arg`.
- shift # remove old arg
- set -- "$@" "$arg" # push replacement arg
- done
-fi
-
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command:
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
-# and any embedded shellness will be escaped.
-# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
-# treated as '${Hostname}' itself on the command line.
-
-set -- \
- "-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
- "$@"
-
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
- die "xargs is not available"
-fi
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-# set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
- printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
- xargs -n1 |
- sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
- tr '\n' ' '
- )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
deleted file mode 100644
index 929f7b6ee..000000000
--- a/android/gradlew.bat
+++ /dev/null
@@ -1,99 +0,0 @@
-@REM Copyright (c) Meta Platforms, Inc. and affiliates.
-@REM
-@REM This source code is licensed under the MIT license found in the
-@REM LICENSE file in the root directory of this source tree.
-
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
\ No newline at end of file
diff --git a/android/settings.gradle b/android/settings.gradle
deleted file mode 100644
index 5bd85f93a..000000000
--- a/android/settings.gradle
+++ /dev/null
@@ -1,39 +0,0 @@
-pluginManagement {
- def reactNativeGradlePlugin = new File(
- providers.exec {
- workingDir(rootDir)
- commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })")
- }.standardOutput.asText.get().trim()
- ).getParentFile().absolutePath
- includeBuild(reactNativeGradlePlugin)
-
- def expoPluginsPath = new File(
- providers.exec {
- workingDir(rootDir)
- commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })")
- }.standardOutput.asText.get().trim(),
- "../android/expo-gradle-plugin"
- ).absolutePath
- includeBuild(expoPluginsPath)
-}
-
-plugins {
- id("com.facebook.react.settings")
- id("expo-autolinking-settings")
-}
-
-extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
- if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
- ex.autolinkLibrariesFromCommand()
- } else {
- ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
- }
-}
-
-rootProject.name = "LNReader"
-
-expoAutolinking.useExpoModules()
-expoAutolinking.useExpoVersionCatalog()
-
-include(":app")
-includeBuild(expoAutolinking.reactNativeGradlePlugin)
diff --git a/app.json b/app.json
index 835ede139..e788d72de 100644
--- a/app.json
+++ b/app.json
@@ -1,12 +1,78 @@
{
"expo": {
+ "version": "2.1.2",
"name": "LNReader",
"slug": "LNReader",
"scheme": "lnreader",
+ "icon": "./assets/native/ic_launcher.png",
"plugins": [
+ "./plugins/withAndroidCustomizations",
+ [
+ "expo-splash-screen",
+ {
+ "image": "./assets/native/ic_launcher_round.png",
+ "backgroundColor": "#1D1B20",
+ "resizeMode": "contain",
+ "dark": {
+ "image": "./assets/native/ic_launcher_round.png",
+ "backgroundColor": "#000000"
+ },
+ "android": {
+ "image": "./assets/native/splash_icon.png",
+ "backgroundColor": "#132C33",
+ "imageWidth": 288,
+ "resizeMode": "native",
+ "dark": {
+ "image": "./assets/native/splash_icon.png",
+ "backgroundColor": "#132C33"
+ }
+ }
+ }
+ ],
"expo-localization",
"react-native-edge-to-edge",
- "expo-web-browser"
- ]
+ "expo-web-browser",
+ "./plugins/withReaderAssets",
+ [
+ "expo-build-properties",
+ {
+ "android": {
+ "usesCleartextTraffic": true,
+ "manifestApplicationAttributes": {
+ "android:largeHeap": "true",
+ "android:allowBackup": "true"
+ }
+ }
+ }
+ ]
+ ],
+ "android": {
+ "versionCode": 4196354,
+ "package": "com.rajarsheechatterjee.LNReader",
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/native/ic_launcher_foreground.png",
+ "backgroundImage": "./assets/native/ic_launcher_background.png",
+ "monochromeImage": "./assets/native/ic_launcher_monochrome.png"
+ },
+ "permissions": [
+ "android.permission.DOWNLOAD_WITHOUT_NOTIFICATION",
+ "android.permission.WAKE_LOCK",
+ "android.permission.FOREGROUND_SERVICE",
+ "android.permission.FOREGROUND_SERVICE_DATA_SYNC",
+ "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK",
+ "android.permission.POST_NOTIFICATIONS"
+ ]
+ },
+ "extra": {
+ "eas": {
+ "projectId": "3a63596f-e80c-4342-96b7-a8ac48910489"
+ }
+ },
+ "ios": {
+ "bundleIdentifier": "com.rajarsheechatterjee.LNReader",
+ "infoPlist": {
+ "UIBackgroundModes": ["audio"]
+ }
+ }
}
}
diff --git a/assets/android-icons/debug/res/drawable-hdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-hdpi/notification_icon.png
new file mode 100644
index 000000000..267e5123b
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-hdpi/notification_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-hdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-hdpi/splash_icon.png
new file mode 100644
index 000000000..9f5506379
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-hdpi/splash_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-mdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-mdpi/notification_icon.png
new file mode 100644
index 000000000..dccaf1004
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-mdpi/notification_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-mdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-mdpi/splash_icon.png
new file mode 100644
index 000000000..116d94ad4
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-mdpi/splash_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-xhdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-xhdpi/notification_icon.png
new file mode 100644
index 000000000..f4cfc55c0
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xhdpi/notification_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-xhdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-xhdpi/splash_icon.png
new file mode 100644
index 000000000..d0bf1a199
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xhdpi/splash_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-xxhdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-xxhdpi/notification_icon.png
new file mode 100644
index 000000000..e89da831d
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxhdpi/notification_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-xxhdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-xxhdpi/splash_icon.png
new file mode 100644
index 000000000..213bd6e3c
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxhdpi/splash_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-xxxhdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-xxxhdpi/notification_icon.png
new file mode 100644
index 000000000..ff903841a
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxxhdpi/notification_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable-xxxhdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-xxxhdpi/splash_icon.png
new file mode 100644
index 000000000..a30f6f1a2
Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxxhdpi/splash_icon.png differ
diff --git a/assets/android-icons/debug/res/drawable/ic_launcher_background.xml b/assets/android-icons/debug/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..e12438038
--- /dev/null
+++ b/assets/android-icons/debug/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 75%
rename from android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher.xml
index 1a4bfc938..3585e9988 100644
--- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -2,5 +2,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 74%
rename from android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 4ae7d1237..3585e9988 100644
--- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -2,4 +2,5 @@
-
\ No newline at end of file
+
+
diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..a238c5cc0
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 000000000..cf3a2ae6f
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..eedda3bc7
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..f3308306a
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..625247d7f
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..09e868b4d
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 000000000..75c4da67d
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..e929231c5
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..4d25c1d77
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..034f72740
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..1e90a0684
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..2f7e2eb6f
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..7a38c3f6b
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..ae317e5f2
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..3d36a85a5
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..67547fde5
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..c06374c97
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..db6e9dfc9
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..7b6795cfc
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..76ecc7164
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..94203400b
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..74c0bdbe7
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..91e68e11b
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..e048505fe
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..caded9323
Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/debug/res/values-night/splashscreen_background.xml b/assets/android-icons/debug/res/values-night/splashscreen_background.xml
new file mode 100644
index 000000000..60ac481de
--- /dev/null
+++ b/assets/android-icons/debug/res/values-night/splashscreen_background.xml
@@ -0,0 +1,4 @@
+
+
+ #3A2A0C
+
diff --git a/assets/android-icons/debug/res/values/ic_launcher_background.xml b/assets/android-icons/debug/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..57925e2c0
--- /dev/null
+++ b/assets/android-icons/debug/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #4A3208
+
diff --git a/assets/android-icons/debug/res/values/splashscreen_background.xml b/assets/android-icons/debug/res/values/splashscreen_background.xml
new file mode 100644
index 000000000..60ac481de
--- /dev/null
+++ b/assets/android-icons/debug/res/values/splashscreen_background.xml
@@ -0,0 +1,4 @@
+
+
+ #3A2A0C
+
diff --git a/assets/android-icons/debug/web/ic_launcher-1024.png b/assets/android-icons/debug/web/ic_launcher-1024.png
new file mode 100644
index 000000000..20b5f99d4
Binary files /dev/null and b/assets/android-icons/debug/web/ic_launcher-1024.png differ
diff --git a/assets/android-icons/debug/web/ic_launcher-playstore.png b/assets/android-icons/debug/web/ic_launcher-playstore.png
new file mode 100644
index 000000000..cb9ed3e3a
Binary files /dev/null and b/assets/android-icons/debug/web/ic_launcher-playstore.png differ
diff --git a/assets/android-icons/debug/web/splash_icon.png b/assets/android-icons/debug/web/splash_icon.png
new file mode 100644
index 000000000..a30f6f1a2
Binary files /dev/null and b/assets/android-icons/debug/web/splash_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-hdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-hdpi/notification_icon.png
new file mode 100644
index 000000000..267e5123b
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-hdpi/notification_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-hdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-hdpi/splash_icon.png
new file mode 100644
index 000000000..4f5555979
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-hdpi/splash_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-mdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-mdpi/notification_icon.png
new file mode 100644
index 000000000..dccaf1004
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-mdpi/notification_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-mdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-mdpi/splash_icon.png
new file mode 100644
index 000000000..4f6b628f8
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-mdpi/splash_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-xhdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-xhdpi/notification_icon.png
new file mode 100644
index 000000000..f4cfc55c0
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xhdpi/notification_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-xhdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-xhdpi/splash_icon.png
new file mode 100644
index 000000000..32eff329e
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xhdpi/splash_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-xxhdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-xxhdpi/notification_icon.png
new file mode 100644
index 000000000..e89da831d
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxhdpi/notification_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-xxhdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-xxhdpi/splash_icon.png
new file mode 100644
index 000000000..8c05eafc5
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxhdpi/splash_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-xxxhdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-xxxhdpi/notification_icon.png
new file mode 100644
index 000000000..ff903841a
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxxhdpi/notification_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable-xxxhdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-xxxhdpi/splash_icon.png
new file mode 100644
index 000000000..815db018e
Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxxhdpi/splash_icon.png differ
diff --git a/assets/android-icons/preview/res/drawable/ic_launcher_background.xml b/assets/android-icons/preview/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..e12438038
--- /dev/null
+++ b/assets/android-icons/preview/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
diff --git a/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..3585e9988
--- /dev/null
+++ b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher_round.xml b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..3585e9988
--- /dev/null
+++ b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..ffcb0885c
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 000000000..74f035be5
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..899e13c14
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..f3308306a
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..bcf5964b9
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..220a75e19
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 000000000..c3b63019e
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..589944ea1
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..4d25c1d77
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..9e1536f42
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..e6a3fad40
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..48fd6a276
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..a31413d03
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..ae317e5f2
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..bc3f1cd4c
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..ff5d99924
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..653617e87
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..aaa122181
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..7b6795cfc
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..c078cb948
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..1cc2ff5a1
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..9973963e1
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..85cbc8e3b
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..e048505fe
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..fe09e252a
Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/preview/res/values-night/splashscreen_background.xml b/assets/android-icons/preview/res/values-night/splashscreen_background.xml
new file mode 100644
index 000000000..455c360b5
--- /dev/null
+++ b/assets/android-icons/preview/res/values-night/splashscreen_background.xml
@@ -0,0 +1,4 @@
+
+
+ #DFE2E3
+
diff --git a/assets/android-icons/preview/res/values/ic_launcher_background.xml b/assets/android-icons/preview/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..535864714
--- /dev/null
+++ b/assets/android-icons/preview/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #23292B
+
diff --git a/assets/android-icons/preview/res/values/splashscreen_background.xml b/assets/android-icons/preview/res/values/splashscreen_background.xml
new file mode 100644
index 000000000..455c360b5
--- /dev/null
+++ b/assets/android-icons/preview/res/values/splashscreen_background.xml
@@ -0,0 +1,4 @@
+
+
+ #DFE2E3
+
diff --git a/assets/android-icons/preview/web/ic_launcher-1024.png b/assets/android-icons/preview/web/ic_launcher-1024.png
new file mode 100644
index 000000000..122e030fa
Binary files /dev/null and b/assets/android-icons/preview/web/ic_launcher-1024.png differ
diff --git a/assets/android-icons/preview/web/ic_launcher-playstore.png b/assets/android-icons/preview/web/ic_launcher-playstore.png
new file mode 100644
index 000000000..13f5ad77c
Binary files /dev/null and b/assets/android-icons/preview/web/ic_launcher-playstore.png differ
diff --git a/assets/android-icons/preview/web/splash_icon.png b/assets/android-icons/preview/web/splash_icon.png
new file mode 100644
index 000000000..815db018e
Binary files /dev/null and b/assets/android-icons/preview/web/splash_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-hdpi/notification_icon.png b/assets/android-icons/release/res/drawable-hdpi/notification_icon.png
new file mode 100644
index 000000000..267e5123b
Binary files /dev/null and b/assets/android-icons/release/res/drawable-hdpi/notification_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-hdpi/splash_icon.png b/assets/android-icons/release/res/drawable-hdpi/splash_icon.png
new file mode 100644
index 000000000..6e753f5ab
Binary files /dev/null and b/assets/android-icons/release/res/drawable-hdpi/splash_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-mdpi/notification_icon.png b/assets/android-icons/release/res/drawable-mdpi/notification_icon.png
new file mode 100644
index 000000000..dccaf1004
Binary files /dev/null and b/assets/android-icons/release/res/drawable-mdpi/notification_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-mdpi/splash_icon.png b/assets/android-icons/release/res/drawable-mdpi/splash_icon.png
new file mode 100644
index 000000000..58e3c61e9
Binary files /dev/null and b/assets/android-icons/release/res/drawable-mdpi/splash_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-xhdpi/notification_icon.png b/assets/android-icons/release/res/drawable-xhdpi/notification_icon.png
new file mode 100644
index 000000000..f4cfc55c0
Binary files /dev/null and b/assets/android-icons/release/res/drawable-xhdpi/notification_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-xhdpi/splash_icon.png b/assets/android-icons/release/res/drawable-xhdpi/splash_icon.png
new file mode 100644
index 000000000..0abb4e6b7
Binary files /dev/null and b/assets/android-icons/release/res/drawable-xhdpi/splash_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-xxhdpi/notification_icon.png b/assets/android-icons/release/res/drawable-xxhdpi/notification_icon.png
new file mode 100644
index 000000000..e89da831d
Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxhdpi/notification_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-xxhdpi/splash_icon.png b/assets/android-icons/release/res/drawable-xxhdpi/splash_icon.png
new file mode 100644
index 000000000..d33552676
Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxhdpi/splash_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-xxxhdpi/notification_icon.png b/assets/android-icons/release/res/drawable-xxxhdpi/notification_icon.png
new file mode 100644
index 000000000..ff903841a
Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxxhdpi/notification_icon.png differ
diff --git a/assets/android-icons/release/res/drawable-xxxhdpi/splash_icon.png b/assets/android-icons/release/res/drawable-xxxhdpi/splash_icon.png
new file mode 100644
index 000000000..6550deaed
Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxxhdpi/splash_icon.png differ
diff --git a/assets/android-icons/release/res/drawable/ic_launcher_background.xml b/assets/android-icons/release/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..e12438038
--- /dev/null
+++ b/assets/android-icons/release/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
diff --git a/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..3585e9988
--- /dev/null
+++ b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher_round.xml b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..3585e9988
--- /dev/null
+++ b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..4879e95f1
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 000000000..1c7b89ebc
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..30c481424
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..f3308306a
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..90072a139
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..5c9bd8e8b
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 000000000..57ed6eb2e
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..5ebd85e52
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..4d25c1d77
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..53f40befb
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..f324c66bf
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..526015d0c
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..c99f4d2d8
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..ae317e5f2
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..14558c96c
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..5893bc65d
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..cfcb4afbd
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..48220d198
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..7b6795cfc
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..607bc40a3
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..8d70e77ea
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..c63c00dd8
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..e7c468306
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..e048505fe
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..d0033a9c0
Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/assets/android-icons/release/res/values/ic_launcher_background.xml
similarity index 89%
rename from android/app/src/main/res/values/ic_launcher_background.xml
rename to assets/android-icons/release/res/values/ic_launcher_background.xml
index 11e7f5609..f6457e3b8 100644
--- a/android/app/src/main/res/values/ic_launcher_background.xml
+++ b/assets/android-icons/release/res/values/ic_launcher_background.xml
@@ -1,4 +1,4 @@
#132C33
-
\ No newline at end of file
+
diff --git a/assets/android-icons/release/web/ic_launcher-1024.png b/assets/android-icons/release/web/ic_launcher-1024.png
new file mode 100644
index 000000000..a2685efcf
Binary files /dev/null and b/assets/android-icons/release/web/ic_launcher-1024.png differ
diff --git a/assets/android-icons/release/web/ic_launcher-playstore.png b/assets/android-icons/release/web/ic_launcher-playstore.png
new file mode 100644
index 000000000..4ff8f23bc
Binary files /dev/null and b/assets/android-icons/release/web/ic_launcher-playstore.png differ
diff --git a/assets/android-icons/release/web/splash_icon.png b/assets/android-icons/release/web/splash_icon.png
new file mode 100644
index 000000000..6550deaed
Binary files /dev/null and b/assets/android-icons/release/web/splash_icon.png differ
diff --git a/assets/native/ic_launcher.png b/assets/native/ic_launcher.png
new file mode 100644
index 000000000..8d70e77ea
Binary files /dev/null and b/assets/native/ic_launcher.png differ
diff --git a/assets/native/ic_launcher_background.png b/assets/native/ic_launcher_background.png
new file mode 100644
index 000000000..c63c00dd8
Binary files /dev/null and b/assets/native/ic_launcher_background.png differ
diff --git a/assets/native/ic_launcher_foreground.png b/assets/native/ic_launcher_foreground.png
new file mode 100644
index 000000000..e7c468306
Binary files /dev/null and b/assets/native/ic_launcher_foreground.png differ
diff --git a/assets/native/ic_launcher_monochrome.png b/assets/native/ic_launcher_monochrome.png
new file mode 100644
index 000000000..e048505fe
Binary files /dev/null and b/assets/native/ic_launcher_monochrome.png differ
diff --git a/assets/native/ic_launcher_round.png b/assets/native/ic_launcher_round.png
new file mode 100644
index 000000000..d0033a9c0
Binary files /dev/null and b/assets/native/ic_launcher_round.png differ
diff --git a/assets/native/notification_icon.png b/assets/native/notification_icon.png
new file mode 100644
index 000000000..dccaf1004
Binary files /dev/null and b/assets/native/notification_icon.png differ
diff --git a/assets/native/splash_icon.png b/assets/native/splash_icon.png
new file mode 100644
index 000000000..6550deaed
Binary files /dev/null and b/assets/native/splash_icon.png differ
diff --git a/android/app/src/main/assets/css/index.css b/assets/reader/css/index.css
similarity index 89%
rename from android/app/src/main/assets/css/index.css
rename to assets/reader/css/index.css
index 50a31c03a..b599d4a59 100644
--- a/android/app/src/main/assets/css/index.css
+++ b/assets/reader/css/index.css
@@ -53,6 +53,10 @@ img {
max-width: 100%;
}
+div:has(> table):not(#LNReader-chapter) {
+ overflow: auto;
+}
+
table {
background-color: var(--theme-onPrimary);
border-collapse: collapse;
@@ -83,8 +87,6 @@ td {
margin-left: var(--readerSettings-padding);
margin-right: var(--readerSettings-padding);
border-radius: 50px;
- border-width: 1;
- color: var(--theme-onPrimary);
background-color: var(--theme-primary);
font-family: var(--readerSettings-fontFamily);
font-size: 16px;
@@ -93,6 +95,7 @@ td {
}
.next-button {
+ color: var(--theme-onPrimary);
min-height: 40px;
text-overflow: ellipsis;
overflow: hidden;
@@ -160,6 +163,21 @@ td {
background-color: transparent;
}
+mark.lnreader-search-match {
+ border-radius: 2px;
+ color: inherit;
+ background-color: color-mix(
+ in srgb,
+ var(--theme-tertiary) 55%,
+ transparent
+ );
+}
+
+mark.lnreader-search-match-active {
+ color: var(--theme-onPrimary);
+ background-color: var(--theme-primary);
+}
+
#Image-Modal {
position: fixed;
left: 0;
diff --git a/android/app/src/main/assets/css/pageReader.css b/assets/reader/css/pageReader.css
similarity index 69%
rename from android/app/src/main/assets/css/pageReader.css
rename to assets/reader/css/pageReader.css
index 8efdd26d1..df5cf8c98 100644
--- a/android/app/src/main/assets/css/pageReader.css
+++ b/assets/reader/css/pageReader.css
@@ -1,13 +1,19 @@
body.page-reader {
overflow: hidden;
- padding-bottom: unset;
+ box-sizing: border-box;
+ height: 100vh;
+ height: 100dvh;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-bottom: 0;
}
body.page-reader > #LNReader-chapter {
- height: calc(90vh);
+ height: calc(100% - var(--pageReader-footerHeight, 0px));
column-width: calc(100vw - var(--readerSettings-padding) * 2);
column-gap: calc(var(--readerSettings-padding) * 2);
- transition: 200ms;
+ column-fill: auto;
+ transition: transform 200ms;
}
.transition-chapter {
height: 100vh;
diff --git a/android/app/src/main/assets/css/toolWrapper.css b/assets/reader/css/toolWrapper.css
similarity index 100%
rename from android/app/src/main/assets/css/toolWrapper.css
rename to assets/reader/css/toolWrapper.css
diff --git a/assets/reader/css/tts.css b/assets/reader/css/tts.css
new file mode 100644
index 000000000..801304fe8
--- /dev/null
+++ b/assets/reader/css/tts.css
@@ -0,0 +1,84 @@
+#TTS-Controller {
+ align-items: center;
+ background: color-mix(
+ in srgb,
+ var(--readerSettings-theme) 90%,
+ var(--readerSettings-textColor) 10%
+ );
+ border: 1px solid
+ color-mix(
+ in srgb,
+ var(--readerSettings-textColor) 30%,
+ var(--readerSettings-theme) 70%
+ );
+ border-radius: 28px;
+ box-shadow: 0 3px 12px rgba(0, 0, 0, 0.28);
+ display: flex;
+ gap: 2px;
+ padding: 4px;
+ position: fixed;
+ top: 50%;
+ left: 20px;
+ opacity: 0.78;
+ z-index: 2147483647;
+}
+
+#TTS-Controller button {
+ align-items: center;
+ background: transparent;
+ border: 0;
+ border-radius: 50%;
+ display: flex;
+ height: 40px;
+ justify-content: center;
+ outline: none;
+ padding: 8px;
+ width: 40px;
+}
+
+#TTS-Controller button:active {
+ background: var(--theme-rippleColor);
+}
+
+#TTS-Controller .tts-drag-handle {
+ cursor: move;
+ touch-action: none;
+ width: 32px;
+}
+
+#TTS-Controller .tts-collapse-toggle {
+ width: 32px;
+}
+
+#TTS-Controller.collapsed {
+ border-radius: 50%;
+ gap: 0;
+}
+
+#TTS-Controller.collapsed > :not(.tts-collapse-toggle) {
+ display: none;
+}
+
+#TTS-Controller.collapsed .tts-collapse-toggle {
+ cursor: move;
+ touch-action: none;
+ width: 40px;
+}
+
+#TTS-Controller.active {
+ opacity: 1;
+}
+
+#TTS-Controller svg {
+ fill: var(--readerSettings-textColor);
+ width: 20px;
+ height: 20px;
+}
+
+#TTS-Progress {
+ color: var(--readerSettings-textColor);
+ font-size: 11px;
+ min-width: 34px;
+ padding-right: 6px;
+ text-align: center;
+}
diff --git a/android/app/src/main/assets/fonts/OpenDyslexic3-Regular.ttf b/assets/reader/fonts/OpenDyslexic3-Regular.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/OpenDyslexic3-Regular.ttf
rename to assets/reader/fonts/OpenDyslexic3-Regular.ttf
diff --git a/android/app/src/main/assets/fonts/arbutus-slab.ttf b/assets/reader/fonts/arbutus-slab.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/arbutus-slab.ttf
rename to assets/reader/fonts/arbutus-slab.ttf
diff --git a/android/app/src/main/assets/fonts/domine.ttf b/assets/reader/fonts/domine.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/domine.ttf
rename to assets/reader/fonts/domine.ttf
diff --git a/android/app/src/main/assets/fonts/lato.ttf b/assets/reader/fonts/lato.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/lato.ttf
rename to assets/reader/fonts/lato.ttf
diff --git a/android/app/src/main/assets/fonts/lora.ttf b/assets/reader/fonts/lora.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/lora.ttf
rename to assets/reader/fonts/lora.ttf
diff --git a/android/app/src/main/assets/fonts/noto-sans.ttf b/assets/reader/fonts/noto-sans.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/noto-sans.ttf
rename to assets/reader/fonts/noto-sans.ttf
diff --git a/android/app/src/main/assets/fonts/nunito.ttf b/assets/reader/fonts/nunito.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/nunito.ttf
rename to assets/reader/fonts/nunito.ttf
diff --git a/android/app/src/main/assets/fonts/open-sans.ttf b/assets/reader/fonts/open-sans.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/open-sans.ttf
rename to assets/reader/fonts/open-sans.ttf
diff --git a/android/app/src/main/assets/fonts/pt-sans-bold.ttf b/assets/reader/fonts/pt-sans-bold.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/pt-sans-bold.ttf
rename to assets/reader/fonts/pt-sans-bold.ttf
diff --git a/android/app/src/main/assets/fonts/pt-serif.ttf b/assets/reader/fonts/pt-serif.ttf
similarity index 100%
rename from android/app/src/main/assets/fonts/pt-serif.ttf
rename to assets/reader/fonts/pt-serif.ttf
diff --git a/android/app/src/main/assets/js/core.js b/assets/reader/js/core.js
similarity index 60%
rename from android/app/src/main/assets/js/core.js
rename to assets/reader/js/core.js
index a218595dd..463a9a4cd 100644
--- a/android/app/src/main/assets/js/core.js
+++ b/assets/reader/js/core.js
@@ -1,3 +1,19 @@
+window.onUserInteraction = (() => {
+ let groupStart = 0;
+ // Group multiple interactions within a short time frame to avoid excessive calls
+ const groupTime = 3000;
+ return () => {
+ let now = Date.now();
+ if (now - groupStart < groupTime) {
+ // We're inside the group
+ } else {
+ // We're the first in a while
+ groupStart = now;
+ reader.post({ type: 'interaction' });
+ }
+ };
+})();
+
/* eslint-disable no-console */
window.reader = new (function () {
const {
@@ -5,8 +21,6 @@ window.reader = new (function () {
chapterGeneralSettings,
novel,
chapter,
- nextChapter,
- prevChapter,
batteryLevel,
autoSaveInterval,
DEBUG,
@@ -18,6 +32,12 @@ window.reader = new (function () {
this.batteryLevel = van.state(batteryLevel);
this.readerSettings = van.state(readerSettings);
this.generalSettings = van.state(chapterGeneralSettings);
+ /**
+ * Bumped whenever the app pushes new adjacent chapters. The chapter is
+ * rendered before its neighbours are known, so any UI that depends on them
+ * has to read this state to re-render when they arrive.
+ */
+ this.adjacentVersion = van.state(0);
this.chapterElement = document.querySelector('#LNReader-chapter');
this.selection = window.getSelection();
@@ -25,9 +45,17 @@ window.reader = new (function () {
this.novel = novel;
this.chapter = chapter;
- this.nextChapter = nextChapter;
- this.prevChapter = prevChapter;
+ this.nextChapter = undefined;
+ this.prevChapter = undefined;
this.strings = strings;
+
+ /** Called by the app once the neighbouring chapters have been resolved. */
+ this.setAdjacentChapters = ({ nextChapter, prevChapter, strings: texts }) => {
+ this.nextChapter = nextChapter ?? undefined;
+ this.prevChapter = prevChapter ?? undefined;
+ Object.assign(this.strings, texts);
+ this.adjacentVersion.val++;
+ };
this.autoSaveInterval = autoSaveInterval;
this.rawHTML = this.chapterElement.innerHTML;
@@ -39,14 +67,20 @@ window.reader = new (function () {
10,
);
this.chapterHeight = this.chapterElement.scrollHeight + this.paddingTop;
- this.layoutHeight = window.screen.height;
- this.layoutWidth = window.screen.width;
+ this.layoutHeight = window.innerHeight;
+ this.layoutWidth = window.innerWidth;
this.layoutEvent = undefined;
this.chapterEndingVisible = van.state(false);
this.post = obj => window.ReactNativeWebView.postMessage(JSON.stringify(obj));
this.refresh = () => {
+ this.layoutHeight = window.innerHeight;
+ this.layoutWidth = window.innerWidth;
+ this.paddingTop =
+ parseFloat(
+ getComputedStyle(document.body).getPropertyValue('padding-top'),
+ ) || 0;
if (this.generalSettings.val.pageReader) {
this.chapterWidth = this.chapterElement.scrollWidth;
} else {
@@ -54,6 +88,7 @@ window.reader = new (function () {
}
};
+ let loadedFontFamily = readerSettings.fontFamily || '';
van.derive(() => {
const settings = this.readerSettings.val;
document.documentElement.style.setProperty(
@@ -84,7 +119,8 @@ window.reader = new (function () {
'--readerSettings-fontFamily',
settings.fontFamily,
);
- if (settings.fontFamily) {
+ if (settings.fontFamily && settings.fontFamily !== loadedFontFamily) {
+ loadedFontFamily = settings.fontFamily;
new FontFace(
settings.fontFamily,
'url("file:///android_asset/fonts/' + settings.fontFamily + '.ttf")',
@@ -92,14 +128,18 @@ window.reader = new (function () {
.load()
.then(function (loadedFont) {
document.fonts.add(loadedFont);
+ schedulePageCalculation();
});
- } else {
+ } else if (!settings.fontFamily && loadedFontFamily) {
+ loadedFontFamily = '';
// have no affect with a font declared in head
document.fonts.forEach(fontFace => document.fonts.delete(fontFace));
}
+ schedulePageCalculation();
});
document.onscrollend = () => {
+ onUserInteraction();
if (!this.generalSettings.val.pageReader) {
this.post({
type: 'save',
@@ -111,6 +151,10 @@ window.reader = new (function () {
}
};
+ document.onpointerdown = () => onUserInteraction();
+ document.onpointermove = () => onUserInteraction();
+ document.onpointerup = () => onUserInteraction();
+
if (DEBUG) {
// eslint-disable-next-line no-global-assign, no-new-object
console = new Object();
@@ -135,6 +179,7 @@ window.tts = new (function () {
'BR',
'STRONG',
'A',
+ 'MARK',
];
this.prevElement = null;
this.currentElement = reader.chapterElement;
@@ -240,80 +285,44 @@ window.tts = new (function () {
};
this.next = () => {
- try {
- this.currentElement?.classList?.remove('highlight');
-
- // Use array-based approach instead of DOM traversal (no recursion!)
- while (this.elementsRead < this.totalElements) {
- const nextElement = this.allReadableElements[this.elementsRead];
- if (!nextElement) break;
-
- const text = this.normalizeText(nextElement.innerText);
- if (text) {
- // Found valid text - speak it
- this.currentElement = nextElement;
- this.reading = true;
- this.elementsRead++;
- this.speak();
- return;
- } else {
- // Empty text, skip to next in array (no recursion!)
- this.elementsRead++;
- }
- }
-
- // Reached the end (elementsRead >= totalElements or no more valid elements)
- this.reading = false;
- const autoPageAdvance =
- reader.readerSettings.val.tts?.autoPageAdvance === true;
- const hasNextChapter = !!reader.nextChapter;
+ if (!this.started) return;
+ reader.post({ type: 'tts-command', data: { command: 'next' } });
+ };
- if (autoPageAdvance && hasNextChapter) {
- reader.post({ type: 'next', autoStartTTS: true });
- } else {
- this.stop();
- const controller = document.getElementById('TTS-Controller');
- if (controller?.firstElementChild) {
- controller.firstElementChild.innerHTML = volumnIcon;
- }
- }
- } catch (e) {
- this.stop();
- alert('TTS Error: ' + e.message);
- }
+ this.previous = () => {
+ if (!this.started) return;
+ reader.post({ type: 'tts-command', data: { command: 'previous' } });
};
this.start = element => {
- this.stop();
- this.started = true;
const startElement = element ?? reader.chapterElement;
- this.currentElement = startElement;
- // Get all readable elements from the chapter
- this.allReadableElements = this.getAllReadableElements(
- reader.chapterElement,
- );
+ const readableEntries = this.getAllReadableElements(reader.chapterElement)
+ .map(readableElement => ({
+ element: readableElement,
+ text: this.normalizeText(readableElement.innerText),
+ }))
+ .filter(entry => !!entry.text);
+ this.allReadableElements = readableEntries.map(entry => entry.element);
this.totalElements = this.allReadableElements.length;
- this.textQueue = this.allReadableElements
- .map(el => this.normalizeText(el.innerText))
- .filter(text => !!text);
+ this.textQueue = readableEntries.map(entry => entry.text);
+
+ const requestedIndex =
+ element && element !== reader.chapterElement
+ ? this.allReadableElements.indexOf(startElement)
+ : 0;
+ const startIndex = requestedIndex >= 0 ? requestedIndex : 0;
+
+ this.started = this.totalElements > 0;
+ this.reading = this.started;
+ this.setActiveIndex(startIndex);
reader.post({
type: 'tts-queue',
data: {
queue: this.textQueue,
- startIndex: this.elementsRead,
+ startIndex,
},
});
-
- // If starting from a specific element, count how many are before it
- if (element && element !== reader.chapterElement) {
- const startIndex = this.allReadableElements.indexOf(element);
- this.elementsRead = startIndex >= 0 ? startIndex : 0;
- } else {
- this.elementsRead = 0;
- }
-
- this.next();
};
// Get all readable elements in order
@@ -333,46 +342,35 @@ window.tts = new (function () {
};
this.resume = () => {
- if (!this.reading) {
- if (
- this.currentElement &&
- this.currentElement.id !== 'LNReader-chapter'
- ) {
- this.speak();
- this.reading = true;
- } else {
- this.next();
- }
- }
+ if (!this.started) return;
+ reader.post({ type: 'tts-command', data: { command: 'play' } });
};
this.pause = () => {
- this.reading = false;
- reader.post({ type: 'pause-speak' });
- reader.post({ type: 'tts-state', data: { isReading: false } });
+ if (!this.started) return;
+ reader.post({ type: 'tts-command', data: { command: 'pause' } });
};
this.rewind = () => {
- if (!this.started || !this.currentElement) return;
- reader.post({ type: 'pause-speak' });
- this.reading = true;
- this.speak();
+ if (!this.started) return;
+ reader.post({ type: 'tts-command', data: { command: 'replay' } });
};
this.seekTo = index => {
if (!this.started || !this.allReadableElements.length) return;
const targetIndex = Math.max(0, Math.min(index, this.totalElements - 1));
- reader.post({ type: 'pause-speak' });
- this.currentElement?.classList?.remove('highlight');
- this.elementsRead = targetIndex;
- this.currentElement = this.allReadableElements[targetIndex];
- this.reading = true;
- this.elementsRead++;
- this.speak();
+ reader.post({
+ type: 'tts-command',
+ data: { command: 'seekTo', index: targetIndex },
+ });
};
this.stop = () => {
- reader.post({ type: 'stop-speak' });
+ reader.post({ type: 'tts-command', data: { command: 'stop' } });
+ this.reset();
+ };
+
+ this.reset = () => {
this.currentElement?.classList?.remove('highlight');
this.prevElement = null;
this.currentElement = reader.chapterElement;
@@ -382,12 +380,49 @@ window.tts = new (function () {
this.totalElements = 0;
this.allReadableElements = [];
this.textQueue = [];
- reader.post({ type: 'tts-state', data: { isReading: false } });
- // Ensure icon updates to stopped state
- const controller = document.getElementById('TTS-Controller');
- if (controller?.firstElementChild) {
- controller.firstElementChild.innerHTML = volumnIcon;
+ const playPauseButton = document.getElementById('TTS-PlayPause');
+ if (playPauseButton) playPauseButton.innerHTML = resumeIcon;
+ const progress = document.getElementById('TTS-Progress');
+ if (progress) progress.textContent = '';
+ };
+
+ this.setActiveIndex = index => {
+ if (!this.allReadableElements.length) return;
+ const targetIndex = Math.max(0, Math.min(index, this.totalElements - 1));
+ this.currentElement?.classList?.remove('highlight');
+ this.currentElement = this.allReadableElements[targetIndex];
+ this.elementsRead = targetIndex + 1;
+ this.started = true;
+ this.scrollToElement(this.currentElement);
+ this.currentElement.classList.add('highlight');
+ const progress = document.getElementById('TTS-Progress');
+ if (progress) {
+ progress.textContent = `${targetIndex + 1}/${this.totalElements}`;
+ }
+ };
+
+ this.setPlaybackState = state => {
+ this.reading = state === 'playing';
+ if (state === 'error') {
+ this.reset();
+ return;
+ }
+ const playPauseButton = document.getElementById('TTS-PlayPause');
+ if (playPauseButton) {
+ playPauseButton.innerHTML = this.reading ? pauseIcon : resumeIcon;
+ }
+ };
+
+ this.complete = () => {
+ this.reading = false;
+ if (
+ reader.readerSettings.val.tts?.autoPageAdvance === true &&
+ reader.nextChapter
+ ) {
+ reader.post({ type: 'next', autoStartTTS: true });
+ return;
}
+ this.reset();
};
this.isElementInViewport = element => {
@@ -411,6 +446,21 @@ window.tts = new (function () {
if (!element) return;
// Check if element is partially visible (at least some part is in viewport)
const rect = element.getBoundingClientRect();
+ if (reader.generalSettings.val.pageReader) {
+ const relativePage = Math.floor(
+ (rect.left + rect.width / 2) / reader.layoutWidth,
+ );
+ pageReader.movePage(
+ Math.max(
+ 0,
+ Math.min(
+ pageReader.totalPages.val - 1,
+ pageReader.page.val + relativePage,
+ ),
+ ),
+ );
+ return;
+ }
const windowHeight =
window.innerHeight || document.documentElement.clientHeight;
const isPartiallyVisible =
@@ -446,22 +496,7 @@ window.tts = new (function () {
};
this.speak = () => {
- if (!this.currentElement) return;
- this.prevElement = this.currentElement;
- this.scrollToElement(this.currentElement);
- this.currentElement.classList.add('highlight');
- const text = this.normalizeText(this.currentElement.innerText);
- if (text) {
- reader.post({
- type: 'speak',
- data: text,
- index: this.elementsRead - 1,
- total: this.totalElements,
- });
- reader.post({ type: 'tts-state', data: { isReading: true } });
- } else {
- this.next();
- }
+ this.rewind();
};
})();
@@ -475,10 +510,16 @@ van.derive(() => {
});
window.pageReader = new (function () {
+ const config =
+ typeof initialPageReaderConfig === 'undefined'
+ ? {}
+ : initialPageReaderConfig;
this.page = van.state(0);
this.totalPages = van.state(0);
+ this.ignoreClickUntil = 0;
+ this.chapterNavigationPending = false;
this.chapterEndingVisible = van.state(
- initialPageReaderConfig.nextChapterScreenVisible,
+ config.nextChapterScreenVisible === true,
);
this.chapterEnding = document.getElementsByClassName('transition-chapter')[0];
@@ -492,45 +533,54 @@ window.pageReader = new (function () {
if (bool) {
this.chapterEnding.style.transform = `translateX(${left ? -200 : 0}vw)`;
requestAnimationFrame(() => {
- if (!instant) this.chapterEnding.style.transition = '200ms';
+ if (!instant) {
+ this.chapterEnding.style.transition = 'transform 200ms';
+ }
this.chapterEnding.style.transform = 'translateX(-100vw)';
});
this.chapterEndingVisible.val = true;
} else {
- if (!instant) this.chapterEnding.style.transition = '200ms';
+ if (!instant) {
+ this.chapterEnding.style.transition = 'transform 200ms';
+ }
this.chapterEnding.style.transform = `translateX(${left ? -200 : 0}vw)`;
this.chapterEndingVisible.val = false;
}
};
- this.movePage = destPage => {
+ this.movePage = (destPage, { interaction = true, save = true } = {}) => {
+ if (interaction) {
+ onUserInteraction();
+ }
if (this.chapterEndingVisible.val) {
- if (destPage < 0) {
- this.showChapterEnding(false);
+ if (this.chapterNavigationPending) {
return;
}
- if (destPage < this.totalPages.val) {
- this.showChapterEnding(false, false, true);
+ if (destPage < 0) {
+ this.showChapterEnding(false);
return;
}
- if (destPage >= this.totalPages.val) {
- return reader.post({ type: 'next' });
- }
+ this.showChapterEnding(false, false, true);
+ return;
}
destPage = parseInt(destPage, 10);
if (destPage < 0) {
+ if (!reader.prevChapter) return;
document.getElementsByClassName('transition-chapter')[0].innerText =
reader.prevChapter.name;
this.showChapterEnding(true, false, true);
+ this.chapterNavigationPending = true;
setTimeout(() => {
reader.post({ type: 'prev' });
}, 200);
return;
}
if (destPage >= this.totalPages.val) {
+ if (!reader.nextChapter) return;
document.getElementsByClassName('transition-chapter')[0].innerText =
reader.nextChapter.name;
this.showChapterEnding(true);
+ this.chapterNavigationPending = true;
setTimeout(() => {
reader.post({ type: 'next' });
}, 200);
@@ -545,17 +595,53 @@ window.pageReader = new (function () {
10,
);
- if (newProgress > reader.chapter.progress) {
+ if (save && newProgress > reader.chapter.progress) {
+ reader.chapter.progress = newProgress;
reader.post({
type: 'save',
- data: parseInt(
- ((pageReader.page.val + 1) / pageReader.totalPages.val) * 100,
- 10,
- ),
+ data: newProgress,
});
}
};
+ this.repaginate = ratio => {
+ const previousTotal = this.totalPages.val;
+ const currentRatio =
+ previousTotal > 0 ? (this.page.val + 1) / previousTotal : 0;
+ const positionRatio = Number.isFinite(ratio)
+ ? ratio
+ : currentRatio || reader.chapter.progress / 100;
+
+ reader.refresh();
+ const chapterStyle = getComputedStyle(reader.chapterElement);
+ const horizontalPadding =
+ (parseFloat(chapterStyle.paddingLeft) || 0) +
+ (parseFloat(chapterStyle.paddingRight) || 0);
+ this.totalPages.val = Math.max(
+ 1,
+ Math.floor(
+ (reader.chapterWidth + horizontalPadding) /
+ (reader.chapterElement.clientWidth || reader.layoutWidth) +
+ 0.001,
+ ),
+ );
+
+ if (this.chapterEndingVisible.val) {
+ return;
+ }
+
+ const destination = Math.min(
+ this.totalPages.val - 1,
+ Math.max(
+ 0,
+ Math.round(
+ this.totalPages.val * Math.min(1, Math.max(0, positionRatio)),
+ ) - 1,
+ ),
+ );
+ this.movePage(destination, { interaction: false, save: false });
+ };
+
van.derive(() => {
// ignore if initial or other states change
if (
@@ -570,27 +656,20 @@ window.pageReader = new (function () {
(window.scrollY + reader.layoutHeight) / reader.chapterHeight,
);
document.body.classList.add('page-reader');
- setTimeout(() => {
- reader.refresh();
- this.totalPages.val = parseInt(
- (reader.chapterWidth + reader.readerSettings.val.padding * 2) /
- reader.layoutWidth,
- 10,
- );
- this.movePage(this.totalPages.val * ratio);
- }, 100);
+ requestAnimationFrame(() => this.repaginate(ratio));
} else {
- reader.chapterElement.style = '';
+ const ratio =
+ this.totalPages.val > 0 ? (this.page.val + 1) / this.totalPages.val : 0;
+ reader.chapterElement.style.removeProperty('transform');
+ reader.chapterElement.style.removeProperty('transition');
document.body.classList.remove('page-reader');
- setTimeout(() => {
+ requestAnimationFrame(() => {
reader.refresh();
window.scrollTo({
- top:
- (reader.chapterHeight * (this.page.val + 1)) / this.totalPages.val -
- reader.layoutHeight,
+ top: reader.chapterHeight * ratio - reader.layoutHeight,
behavior: 'smooth',
});
- }, 100);
+ });
}
});
})();
@@ -601,48 +680,124 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
-function calculatePages() {
+/** Scroll offset the reading position was restored to, in scroll mode. */
+let restoredScrollTop = null;
+let positionRestored = false;
+
+function calculatePages(behavior = 'instant') {
reader.refresh();
if (reader.generalSettings.val.pageReader) {
- pageReader.totalPages.val = parseInt(
- (reader.chapterWidth + reader.readerSettings.val.padding * 2) /
- reader.layoutWidth,
- 10,
- );
-
- if (initialPageReaderConfig.nextChapterScreenVisible) return;
-
- pageReader.movePage(
- Math.max(
- 0,
- Math.round(
- (pageReader.totalPages.val * reader.chapter.progress) / 100,
- ) - 1,
- ),
- );
+ pageReader.repaginate(reader.chapter.progress / 100);
} else {
- window.scrollTo({
- top:
- (reader.chapterHeight * reader.chapter.progress) / 100 -
- reader.layoutHeight,
- behavior: 'smooth',
- });
+ restoredScrollTop =
+ (reader.chapterHeight * reader.chapter.progress) / 100 -
+ reader.layoutHeight;
+ window.scrollTo({ top: restoredScrollTop, behavior });
+ }
+}
+
+let pageCalculationFrame;
+let pendingPageRatio;
+function schedulePageCalculation(ratio) {
+ if (Number.isFinite(ratio)) {
+ pendingPageRatio = ratio;
}
+ if (
+ pageCalculationFrame ||
+ !window.pageReader ||
+ !reader.generalSettings.val.pageReader
+ ) {
+ return;
+ }
+ pageCalculationFrame = requestAnimationFrame(() => {
+ pageCalculationFrame = undefined;
+ const nextRatio = pendingPageRatio;
+ pendingPageRatio = undefined;
+ pageReader.repaginate(nextRatio);
+ });
}
const ro = new ResizeObserver(() => {
- if (pageReader.totalPages.val) {
- calculatePages();
+ if (pageReader.totalPages.val && reader.generalSettings.val.pageReader) {
+ schedulePageCalculation();
}
});
ro.observe(reader.chapterElement);
+reader.chapterElement.addEventListener(
+ 'load',
+ () => schedulePageCalculation(),
+ true,
+);
+
+let viewportResizeTimer;
+window.addEventListener('resize', () => {
+ clearTimeout(viewportResizeTimer);
+ viewportResizeTimer = setTimeout(() => {
+ if (reader.generalSettings.val.pageReader) {
+ schedulePageCalculation();
+ } else {
+ reader.refresh();
+ }
+ }, 100);
+});
-// Also call once on load
-window.addEventListener('load', () => {
- document.fonts.ready.then(() => {
- requestAnimationFrame(() => setTimeout(calculatePages, 0));
+/**
+ * Fonts and images can still change the chapter height after the position was
+ * restored. Correct it when that happens - but only while the reader is sitting
+ * exactly where it was put, otherwise this would yank the page from under
+ * someone who has already started reading.
+ */
+const correctReadingPosition = () => {
+ if (reader.generalSettings.val.pageReader || restoredScrollTop === null) {
+ return;
+ }
+
+ const previousHeight = reader.chapterHeight;
+ reader.refresh();
+ if (
+ reader.chapterHeight !== previousHeight &&
+ Math.abs(window.scrollY - Math.max(0, restoredScrollTop)) < 4
+ ) {
+ calculatePages();
+ }
+};
+
+const restoreReadingPosition = () => {
+ requestAnimationFrame(() =>
+ setTimeout(() => {
+ positionRestored = true;
+ calculatePages();
+ // Deliberately not awaited before restoring: `document.fonts.ready` does
+ // not resolve until the document has finished loading, which is what this
+ // is trying to avoid waiting for.
+ document.fonts.ready.then(correctReadingPosition);
+ }, 0),
+ );
+};
+
+// Restore as soon as the chapter itself is parsed and styled. Waiting for
+// `load` means waiting for every image in the chapter, which can take seconds -
+// the reader would sit at the top of the chapter until then.
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', restoreReadingPosition, {
+ once: true,
});
+} else {
+ restoreReadingPosition();
+}
+
+window.addEventListener('load', () => {
+ if (!positionRestored) {
+ restoreReadingPosition();
+ return;
+ }
+
+ if (reader.generalSettings.val.pageReader) {
+ schedulePageCalculation();
+ } else {
+ correctReadingPosition();
+ }
});
// click handler
@@ -666,6 +821,9 @@ window.addEventListener('load', () => {
return 'center';
};
document.onclick = e => {
+ if (Date.now() < pageReader.ignoreClickUntil) {
+ return;
+ }
const { clientX, clientY } = e;
const { x, y } = {
x: clientX / reader.layoutWidth,
@@ -729,7 +887,10 @@ window.addEventListener('load', () => {
const diffX = e.changedTouches[0].screenX - this.initialX;
const diffY = e.changedTouches[0].screenY - this.initialY;
if (reader.generalSettings.val.pageReader) {
- reader.chapterElement.style.transition = '200ms';
+ if (Math.abs(diffX) > 8 || Math.abs(diffY) > 8) {
+ pageReader.ignoreClickUntil = Date.now() + 400;
+ }
+ reader.chapterElement.style.transition = 'transform 200ms';
const diffXPercentage = diffX / reader.layoutWidth;
if (diffXPercentage < -0.3) {
pageReader.movePage(pageReader.page.val + 1);
@@ -764,6 +925,12 @@ window.addEventListener('load', () => {
// text options
(function () {
+ // What the chapter element currently holds. The document is delivered with
+ // the untransformed chapter already parsed, so writing the same markup back
+ // would re-parse and re-layout the whole chapter (and restart image loads)
+ // for nothing - which is exactly what happens when neither transform is on.
+ let appliedHTML = reader.rawHTML;
+
van.derive(() => {
let html = reader.rawHTML;
if (reader.generalSettings.val.bionicReading) {
@@ -780,14 +947,31 @@ window.addEventListener('load', () => {
`${
/\/p>/.test(_)
? _.replace(
- /
\s*
(?:(?=\s*<\/?p[> ])|(?<=<\/?p\b[^>]*>
\s*
))\s*/g,
+ /
\s*
(?:(?=\s*<\/?p[> ])|(?<=<\/?p(?:>| [^>]+>)
\s*
))\s*/g,
'',
)
: _
}`,
) //if p found, delete all double br near p
- .replace(/
(?:(?=\s*<\/?p[> ])|(?<=<\/?p>\s*
))\s*/g, '');
+ .replace(
+ /
(?:(?=\s*<\/?p[> ])|(?<=<\/?p(?:>| [^>]+>)(?:<[^>]+>)*\s*
))\s*/g,
+ '',
+ );
}
+ if (html === appliedHTML) {
+ return;
+ }
+
reader.chapterElement.innerHTML = html;
+ appliedHTML = html;
+ reader.refresh();
+ schedulePageCalculation();
+
+ // Replacing the markup dropped the highlights, so restore the search.
+ const searchQuery = window.readerSearch?.query;
+ const searchIndex = window.readerSearch?.index;
+ if (searchQuery) {
+ window.readerSearch.search(searchQuery, searchIndex);
+ }
});
})();
diff --git a/assets/reader/js/icons.js b/assets/reader/js/icons.js
new file mode 100644
index 000000000..bde94e854
--- /dev/null
+++ b/assets/reader/js/icons.js
@@ -0,0 +1,14 @@
+const pauseIcon =
+ '';
+const resumeIcon =
+ '';
+const previousParagraphIcon =
+ '';
+const nextParagraphIcon =
+ '';
+const dragHandleIcon =
+ '';
+const minimizeIcon =
+ '';
+const textToSpeechIcon =
+ '';
diff --git a/android/app/src/main/assets/js/index.d.ts b/assets/reader/js/index.d.ts
similarity index 81%
rename from android/app/src/main/assets/js/index.d.ts
rename to assets/reader/js/index.d.ts
index 84190e09f..cfdca69bb 100644
--- a/android/app/src/main/assets/js/index.d.ts
+++ b/assets/reader/js/index.d.ts
@@ -16,10 +16,13 @@ export interface Reader {
generalSettings: State;
readerSettings: State;
batteryLevel: State;
+ /** Bumped whenever the app pushes newly resolved adjacent chapters. */
+ adjacentVersion: State;
novel: NovelInfo;
chapter: ChapterInfo;
nextChapter?: ChapterInfo;
+ prevChapter?: ChapterInfo;
autoSaveInterval: number;
rawHTML: string;
strings: {
@@ -28,6 +31,12 @@ export interface Reader {
noNextChapter: string;
};
+ setAdjacentChapters: (adjacent: {
+ nextChapter?: ChapterInfo;
+ prevChapter?: ChapterInfo;
+ strings?: Partial;
+ }) => void;
+
//layout props
paddingTop: number;
layoutHeight: number;
diff --git a/assets/reader/js/index.js b/assets/reader/js/index.js
new file mode 100644
index 000000000..3cd62d3ce
--- /dev/null
+++ b/assets/reader/js/index.js
@@ -0,0 +1,581 @@
+const { div, p, img, button, span } = van.tags;
+
+/**
+ * Registers a callback for the scrolled-through ratio, coalesced to one call
+ * per frame. Every scroll-driven indicator shares this: separate `scroll`
+ * listeners all re-run the same work on the same frames, and that shows up
+ * directly as scrolling smoothness.
+ */
+const onScrollRatio = (() => {
+ const callbacks = [];
+ let queued = false;
+
+ window.addEventListener(
+ 'scroll',
+ () => {
+ if (queued) {
+ return;
+ }
+ queued = true;
+ requestAnimationFrame(() => {
+ queued = false;
+ const ratio = Math.min(
+ 1,
+ (window.scrollY + reader.layoutHeight) / reader.chapterHeight,
+ );
+ for (const callback of callbacks) {
+ callback(ratio);
+ }
+ });
+ },
+ { passive: true },
+ );
+
+ return callback => callbacks.push(callback);
+})();
+
+const ChapterEnding = () => {
+ return () =>
+ reader.generalSettings.val.pageReader
+ ? div()
+ : div(div({ class: 'info-text' }, reader.strings.finished), () =>
+ // Reading `adjacentVersion` subscribes this binding to the adjacent
+ // chapters being pushed in after the chapter itself was rendered.
+ reader.adjacentVersion.val >= 0 && reader.nextChapter
+ ? button(
+ {
+ class: 'next-button',
+ onclick: e => {
+ e.stopPropagation();
+ reader.post({ type: 'next' });
+ },
+ },
+ reader.strings.nextChapter,
+ )
+ : div({ class: 'info-text' }, reader.strings.noNextChapter),
+ );
+};
+
+const Scrollbar = () => {
+ const horizontal = van.derive(
+ () => !reader.generalSettings.val.verticalSeekbar,
+ );
+ let lock = false;
+ const percentage = van.state(0);
+ const update = ratio => {
+ if (ratio === undefined) {
+ ratio = (window.scrollY + reader.layoutHeight) / reader.chapterHeight;
+ }
+ if (ratio > 1) {
+ ratio = 1;
+ }
+ if (reader.generalSettings.val.pageReader) {
+ pageReader.movePage(
+ parseInt(pageReader.totalPages.val * Math.min(0.99, ratio)),
+ );
+ return;
+ }
+ percentage.val = parseInt(ratio * 100);
+ if (lock) {
+ window.scrollTo({
+ top: reader.chapterHeight * ratio - reader.layoutHeight,
+ behavior: 'instant',
+ });
+ }
+ };
+ onScrollRatio(
+ ratio => !lock && !reader.generalSettings.val.pageReader && update(ratio),
+ );
+ return div(
+ { id: 'ScrollBar' },
+ div(
+ { class: 'scrollbar-item scrollbar-text', id: 'scrollbar-percentage' },
+ () =>
+ reader.generalSettings.val.pageReader
+ ? pageReader.page.val + 1
+ : percentage.val,
+ ),
+ div(
+ { class: 'scrollbar-item', id: 'scrollbar-slider' },
+ div(
+ { id: 'scrollbar-track' },
+ div(
+ {
+ id: 'scrollbar-progress',
+ style: () => {
+ const percentageValue = reader.generalSettings.val.pageReader
+ ? ((pageReader.page.val + 1) / pageReader.totalPages.val) * 100
+ : percentage.val;
+ return horizontal.val
+ ? `width: ${percentageValue}%; height: 100%;`
+ : `height: ${percentageValue}%; width: 100%;`;
+ },
+ },
+ div(
+ {
+ id: 'scrollbar-thumb-wrapper',
+ ontouchstart: () => {
+ lock = true;
+ },
+ ontouchend: () => {
+ lock = false;
+ },
+ ontouchmove: function (e) {
+ const slider = this.parentElement.parentElement.parentElement;
+ const sliderHeight = horizontal.val
+ ? slider.clientWidth
+ : slider.clientHeight;
+ const sliderOffsetY = horizontal.val
+ ? slider.getBoundingClientRect().left
+ : slider.getBoundingClientRect().top;
+ const ratio =
+ ((horizontal.val
+ ? e.changedTouches[0].clientX
+ : e.changedTouches[0].clientY) -
+ sliderOffsetY) /
+ sliderHeight;
+ update(ratio < 0 ? 0 : ratio);
+ },
+ },
+ div({ id: 'scrollbar-thumb' }),
+ ),
+ ),
+ ),
+ ),
+ div(
+ {
+ class: 'scrollbar-item scrollbar-text',
+ id: 'scrollbar-percentage-max',
+ },
+ () =>
+ reader.generalSettings.val.pageReader ? pageReader.totalPages.val : 100,
+ ),
+ );
+};
+
+const ToolWrapper = () => {
+ const horizontal = van.derive(
+ () => !reader.generalSettings.val.verticalSeekbar,
+ );
+ return div(
+ {
+ id: 'ToolWrapper',
+ class: () =>
+ `${reader.hidden.val ? 'hidden' : ''} ${
+ horizontal.val ? 'horizontal' : ''
+ }`,
+ },
+ Scrollbar(),
+ );
+};
+
+const ImageModal = ({ src }) => {
+ return div(
+ {
+ id: 'Image-Modal',
+ class: () => (src.val ? 'show' : ''),
+ onclick: e => {
+ if (e.target.id !== 'Image-Modal-img') {
+ e.stopPropagation();
+ src.val = '';
+ }
+ },
+ },
+ img({
+ id: 'Image-Modal-img',
+ src: src,
+ alt: () => (src.val ? `Cant not render image from ${src.val}` : ''),
+ }),
+ );
+};
+
+const ModalWrapper = () => {
+ const imgSrc = van.state('');
+ const showImage = src => {
+ imgSrc.val = src;
+ reader.viewport.setAttribute(
+ 'content',
+ 'width=device-width, initial-scale=1.0, maximum-scale=10',
+ );
+ };
+ const hideImage = () => {
+ imgSrc.val = '';
+ reader.viewport.setAttribute(
+ 'content',
+ 'width=device-width, initial-scale=1.0, maximum-scale=1.0',
+ );
+ };
+
+ document.addEventListener('contextmenu', e => {
+ if (e.target instanceof HTMLImageElement) {
+ if (!imgSrc.val) {
+ showImage(e.target.src);
+ } else {
+ hideImage();
+ }
+ }
+ });
+ return div(ImageModal({ src: imgSrc }));
+};
+
+const Footer = () => {
+ const percentage = van.state(0);
+ const time = van.state(
+ new Date().toLocaleTimeString(undefined, {
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: false,
+ }),
+ );
+ onScrollRatio(ratio => {
+ percentage.val = parseInt(ratio * 100);
+ });
+ setInterval(() => {
+ if (!reader.generalSettings.val.showBatteryAndTime) {
+ return;
+ }
+ time.val = new Date().toLocaleTimeString(undefined, {
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: false,
+ });
+ }, 10000);
+ const wrapper = div(
+ {
+ id: 'reader-footer-wrapper',
+ class: () =>
+ reader.generalSettings.val.showBatteryAndTime ||
+ reader.generalSettings.val.showScrollPercentage
+ ? ''
+ : 'd-none',
+ },
+ div(
+ { id: 'reader-footer' },
+
+ div(
+ {
+ id: 'reader-battery',
+ class: () =>
+ `reader-footer-item ${
+ reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden'
+ }`,
+ },
+ () => Math.ceil(reader.batteryLevel.val * 100) + '%',
+ ),
+ div(
+ {
+ id: 'reader-percentage',
+ class: () =>
+ `reader-footer-item ${
+ reader.generalSettings.val.showScrollPercentage ? '' : 'hidden'
+ }`,
+ },
+ () =>
+ reader.generalSettings.val.pageReader
+ ? `${pageReader.page.val + 1}/${pageReader.totalPages.val}`
+ : percentage.val + '%',
+ ),
+ div(
+ {
+ id: 'reader-time',
+ class: () =>
+ `reader-footer-item ${
+ reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden'
+ }`,
+ },
+ time,
+ ),
+ ),
+ );
+ const footerObserver = new ResizeObserver(() => {
+ document.documentElement.style.setProperty(
+ '--pageReader-footerHeight',
+ `${wrapper.offsetHeight}px`,
+ );
+ });
+ footerObserver.observe(wrapper);
+ requestAnimationFrame(() => {
+ document.documentElement.style.setProperty(
+ '--pageReader-footerHeight',
+ `${wrapper.offsetHeight}px`,
+ );
+ });
+ return wrapper;
+};
+
+const TTSController = () => {
+ let controllerElement = null;
+ let hoverElement = null;
+ let dragOffsetX = 0;
+ let dragOffsetY = 0;
+ let dragStartX = 0;
+ let dragStartY = 0;
+ let moved = false;
+ const collapsed = van.state(true);
+ let collapseButtonElement = null;
+ let lastBubbleTouchEnd = 0;
+
+ const stopEvent = e => {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ const setControllerPosition = touch => {
+ const maxLeft = Math.max(
+ 8,
+ window.innerWidth - controllerElement.offsetWidth - 8,
+ );
+ const maxTop = Math.max(
+ 8,
+ window.innerHeight - controllerElement.offsetHeight - 8,
+ );
+ const left = Math.min(maxLeft, Math.max(8, touch.clientX - dragOffsetX));
+ const top = Math.min(maxTop, Math.max(8, touch.clientY - dragOffsetY));
+
+ controllerElement.style.left = `${left}px`;
+ controllerElement.style.top = `${top}px`;
+ controllerElement.style.right = 'auto';
+ controllerElement.style.bottom = 'auto';
+ };
+
+ const clampControllerToViewport = () => {
+ const bounds = controllerElement.getBoundingClientRect();
+ const maxLeft = Math.max(8, window.innerWidth - bounds.width - 8);
+ const maxTop = Math.max(8, window.innerHeight - bounds.height - 8);
+
+ controllerElement.style.left = `${Math.min(
+ maxLeft,
+ Math.max(8, bounds.left),
+ )}px`;
+ controllerElement.style.top = `${Math.min(
+ maxTop,
+ Math.max(8, bounds.top),
+ )}px`;
+ controllerElement.style.right = 'auto';
+ controllerElement.style.bottom = 'auto';
+ };
+
+ const setCollapsed = value => {
+ collapsed.val = value;
+ controllerElement ??= document.getElementById('TTS-Controller');
+ collapseButtonElement ??= controllerElement.querySelector(
+ '.tts-collapse-toggle',
+ );
+ collapseButtonElement.setAttribute(
+ 'aria-label',
+ collapsed.val
+ ? 'Expand text-to-speech controls'
+ : 'Minimize text-to-speech controls',
+ );
+ collapseButtonElement.innerHTML = collapsed.val
+ ? textToSpeechIcon
+ : minimizeIcon;
+ requestAnimationFrame(clampControllerToViewport);
+ };
+
+ const startDrag = e => {
+ stopEvent(e);
+ controllerElement ??= document.getElementById('TTS-Controller');
+ const touch = e.changedTouches[0];
+ const bounds = controllerElement.getBoundingClientRect();
+ dragOffsetX = touch.clientX - bounds.left;
+ dragOffsetY = touch.clientY - bounds.top;
+ moved = false;
+ controllerElement.classList.add('active');
+ controllerElement.style.transition = 'none';
+ };
+
+ const moveDrag = e => {
+ stopEvent(e);
+ const touch = e.changedTouches[0];
+
+ moved = true;
+ setControllerPosition(touch);
+
+ const newHoverElement = document
+ .elementsFromPoint(touch.clientX, touch.clientY)
+ .find(
+ element =>
+ !element.closest('#TTS-Controller') &&
+ !element.id.includes('scrollbar') &&
+ tts.readable(element),
+ );
+ hoverElement?.classList.remove('highlight');
+ hoverElement = newHoverElement ?? null;
+ hoverElement?.classList.add('highlight');
+ };
+
+ const endDrag = e => {
+ stopEvent(e);
+ controllerElement.classList.remove('active');
+ controllerElement.style.transition = '';
+
+ if (moved && hoverElement && reader.generalSettings.val.TTSEnable) {
+ tts.start(hoverElement);
+ }
+ hoverElement?.classList.remove('highlight');
+ hoverElement = null;
+ moved = false;
+ };
+
+ const startBubbleDrag = e => {
+ if (!collapsed.val) {
+ return;
+ }
+ stopEvent(e);
+ controllerElement ??= document.getElementById('TTS-Controller');
+ const touch = e.changedTouches[0];
+ const bounds = controllerElement.getBoundingClientRect();
+ dragOffsetX = touch.clientX - bounds.left;
+ dragOffsetY = touch.clientY - bounds.top;
+ dragStartX = touch.clientX;
+ dragStartY = touch.clientY;
+ moved = false;
+ controllerElement.classList.add('active');
+ controllerElement.style.transition = 'none';
+ };
+
+ const moveBubbleDrag = e => {
+ if (!collapsed.val) {
+ return;
+ }
+ stopEvent(e);
+ const touch = e.changedTouches[0];
+ if (
+ !moved &&
+ Math.hypot(touch.clientX - dragStartX, touch.clientY - dragStartY) < 6
+ ) {
+ return;
+ }
+ moved = true;
+ setControllerPosition(touch);
+ };
+
+ const finishBubbleDrag = () => {
+ controllerElement.classList.remove('active');
+ controllerElement.style.transition = '';
+ moved = false;
+ };
+
+ const endBubbleDrag = e => {
+ if (!collapsed.val) {
+ return;
+ }
+ stopEvent(e);
+ const shouldExpand = !moved;
+ lastBubbleTouchEnd = Date.now();
+ finishBubbleDrag();
+ if (shouldExpand) {
+ setCollapsed(false);
+ }
+ };
+
+ const cancelBubbleDrag = e => {
+ if (!collapsed.val) {
+ return;
+ }
+ stopEvent(e);
+ lastBubbleTouchEnd = Date.now();
+ finishBubbleDrag();
+ };
+
+ const toggleCollapsed = e => {
+ e.stopPropagation();
+ if (Date.now() - lastBubbleTouchEnd < 500) {
+ return;
+ }
+ setCollapsed(!collapsed.val);
+ };
+
+ const runCommand = command => e => {
+ e.stopPropagation();
+ if (reader.generalSettings.val.TTSEnable) {
+ command();
+ }
+ };
+
+ collapseButtonElement = button({
+ type: 'button',
+ class: 'tts-collapse-toggle',
+ 'aria-label': 'Expand text-to-speech controls',
+ innerHTML: textToSpeechIcon,
+ ontouchstart: startBubbleDrag,
+ ontouchmove: moveBubbleDrag,
+ ontouchend: endBubbleDrag,
+ ontouchcancel: cancelBubbleDrag,
+ onclick: toggleCollapsed,
+ });
+
+ return div(
+ {
+ id: 'TTS-Controller',
+ class: () =>
+ [
+ reader.generalSettings.val.TTSEnable ? '' : 'hidden',
+ collapsed.val ? 'collapsed' : '',
+ ]
+ .filter(Boolean)
+ .join(' '),
+ style: () =>
+ reader.generalSettings.val.TTSEnable
+ ? 'pointer-events: auto;'
+ : 'pointer-events: none; display: none !important; opacity: 0; transition: none;',
+ onclick: e => e.stopPropagation(),
+ },
+ button({
+ type: 'button',
+ class: 'tts-drag-handle',
+ 'aria-label': 'Move text-to-speech controls',
+ innerHTML: dragHandleIcon,
+ ontouchstart: startDrag,
+ ontouchmove: moveDrag,
+ ontouchend: endDrag,
+ ontouchcancel: endDrag,
+ onclick: stopEvent,
+ }),
+ collapseButtonElement,
+ button({
+ type: 'button',
+ class: 'tts-control-button',
+ 'aria-label': 'Previous paragraph',
+ innerHTML: previousParagraphIcon,
+ onclick: runCommand(() => tts.previous()),
+ }),
+ button({
+ id: 'TTS-PlayPause',
+ type: 'button',
+ class: 'tts-control-button tts-play-pause',
+ 'aria-label': 'Play text-to-speech',
+ innerHTML: resumeIcon,
+ onclick: runCommand(() => {
+ if (tts.reading) {
+ tts.pause();
+ } else if (tts.started) {
+ tts.resume();
+ } else {
+ tts.start();
+ }
+ }),
+ }),
+ button({
+ type: 'button',
+ class: 'tts-control-button',
+ 'aria-label': 'Next paragraph',
+ innerHTML: nextParagraphIcon,
+ onclick: runCommand(() => tts.next()),
+ }),
+ span({ id: 'TTS-Progress', 'aria-hidden': 'true' }),
+ );
+};
+
+const ReaderUI = () => {
+ return div(
+ ToolWrapper(),
+ TTSController(),
+ ModalWrapper(),
+ Footer(),
+ ChapterEnding(),
+ );
+};
+
+van.add(document.getElementById('reader-ui'), ReaderUI());
diff --git a/android/app/src/main/assets/js/polyfill-onscrollend.js b/assets/reader/js/polyfill-onscrollend.js
similarity index 100%
rename from android/app/src/main/assets/js/polyfill-onscrollend.js
rename to assets/reader/js/polyfill-onscrollend.js
diff --git a/assets/reader/js/search.js b/assets/reader/js/search.js
new file mode 100644
index 000000000..a0376f6a9
--- /dev/null
+++ b/assets/reader/js/search.js
@@ -0,0 +1,460 @@
+window.readerSearch = new (function () {
+ const MIN_QUERY_LENGTH = 3;
+ const SEGMENT_BATCH_SIZE = 40;
+ const MAX_RENDERED_MATCHES = 1500;
+ const SPECIAL_CHARACTER_REGEX = /[^\p{L}\p{N}\s]/u;
+ const INLINE_TEXT_ELEMENTS = new Set([
+ 'A',
+ 'ABBR',
+ 'B',
+ 'BDI',
+ 'BDO',
+ 'CITE',
+ 'CODE',
+ 'DATA',
+ 'DFN',
+ 'EM',
+ 'I',
+ 'KBD',
+ 'MARK',
+ 'Q',
+ 'RP',
+ 'RT',
+ 'RUBY',
+ 'S',
+ 'SAMP',
+ 'SMALL',
+ 'SPAN',
+ 'STRONG',
+ 'SUB',
+ 'SUP',
+ 'TIME',
+ 'U',
+ 'VAR',
+ 'WBR',
+ ]);
+
+ this.query = '';
+ this.index = -1;
+ this.matches = [];
+ this.total = 0;
+ this.isTruncated = false;
+ this.searchToken = 0;
+ this.pendingSearchTimer = null;
+ this.cachedSegments = null;
+ this.lastSegmentedChapterId = null;
+ this.isValidState = true;
+
+ this.emit = (query = this.query) => {
+ reader.post({
+ type: 'search-result',
+ data: {
+ query,
+ current: this.index >= 0 ? this.index + 1 : 0,
+ total: this.total,
+ renderedTotal: this.matches.length,
+ isTruncated: this.isTruncated,
+ },
+ });
+ };
+
+ this.cancelPendingSearch = () => {
+ this.searchToken += 1;
+
+ if (this.pendingSearchTimer !== null) {
+ clearTimeout(this.pendingSearchTimer);
+ if (typeof cancelIdleCallback !== 'undefined') {
+ cancelIdleCallback(this.pendingSearchTimer);
+ }
+ this.pendingSearchTimer = null;
+ }
+ };
+
+ this.refreshLayout = () => {
+ if (!reader.generalSettings.val.pageReader || !window.pageReader) {
+ reader.refresh();
+ return;
+ }
+
+ pageReader.repaginate();
+ };
+
+ this.resetMatches = () => {
+ const touchedParents = new Set();
+ const marks = document.querySelectorAll('mark.lnreader-search-match');
+ const fragment = document.createDocumentFragment();
+
+ marks.forEach(mark => {
+ const parent = mark.parentNode;
+ if (!parent) {
+ return;
+ }
+
+ while (mark.firstChild) {
+ fragment.appendChild(mark.firstChild);
+ }
+ parent.replaceChild(fragment, mark);
+ touchedParents.add(parent);
+ });
+
+ touchedParents.forEach(parent => {
+ parent.normalize();
+ });
+
+ this.matches = [];
+ this.index = -1;
+ this.total = 0;
+ this.isTruncated = false;
+ this.isValidState = true;
+ this.cachedSegments = null;
+ this.refreshLayout();
+ };
+
+ this.clear = (emit = true, resetQuery = true) => {
+ this.cancelPendingSearch();
+
+ if (resetQuery) {
+ this.query = '';
+ }
+
+ this.resetMatches();
+ this.invalidateCache();
+
+ if (emit) {
+ this.emit();
+ }
+ };
+
+ this.getTextBlock = node => {
+ let element = node.parentElement;
+
+ while (
+ element &&
+ element !== reader.chapterElement &&
+ INLINE_TEXT_ELEMENTS.has(element.nodeName)
+ ) {
+ element = element.parentElement;
+ }
+
+ return element || reader.chapterElement;
+ };
+
+ this.hasElementBetween = (previousNode, nextNode, selector) => {
+ let current = previousNode.nextSibling;
+ while (current && current !== nextNode) {
+ if (current.nodeType === Node.ELEMENT_NODE && current.matches(selector)) {
+ return true;
+ }
+ if (current.querySelector?.(selector)) {
+ return true;
+ }
+ current = current.nextSibling;
+ }
+ return false;
+ };
+
+ this.buildTextSegments = () => {
+ const segments = [];
+ const textNodes = [];
+ const walker = document.createTreeWalker(
+ reader.chapterElement,
+ NodeFilter.SHOW_TEXT,
+ {
+ acceptNode: node => {
+ if (!node.nodeValue) {
+ return NodeFilter.FILTER_REJECT;
+ }
+ if (node.parentElement?.closest('script, style')) {
+ return NodeFilter.FILTER_REJECT;
+ }
+ return NodeFilter.FILTER_ACCEPT;
+ },
+ },
+ );
+ let node = walker.nextNode();
+
+ while (node) {
+ textNodes.push(node);
+ node = walker.nextNode();
+ }
+
+ textNodes.forEach(textNode => {
+ const block = this.getTextBlock(textNode);
+ const previousSegment = segments[segments.length - 1];
+ const previousEntry =
+ previousSegment?.entries[previousSegment.entries.length - 1];
+ const startsNewSegment =
+ !previousSegment ||
+ previousSegment.block !== block ||
+ this.hasElementBetween(
+ previousEntry.node,
+ textNode,
+ 'br, hr, img, table, ul, ol',
+ );
+
+ if (startsNewSegment) {
+ segments.push({
+ block,
+ entries: [],
+ text: '',
+ });
+ }
+
+ const segment = segments[segments.length - 1];
+ const start = segment.text.length;
+ const text = textNode.nodeValue || '';
+
+ segment.entries.push({
+ end: start + text.length,
+ node: textNode,
+ start,
+ });
+ segment.text += text;
+ });
+
+ return segments.filter(segment => segment.text.trim());
+ };
+
+ this.getTextSegments = () => {
+ if (this.cachedSegments) {
+ return this.cachedSegments;
+ }
+ this.cachedSegments = this.buildTextSegments();
+ return this.cachedSegments;
+ };
+
+ this.findSegmentMatches = (segment, normalizedTerm) => {
+ const matches = [];
+ const normalizedText = segment.text.toLowerCase();
+ let matchIndex = normalizedText.indexOf(normalizedTerm);
+
+ while (matchIndex !== -1) {
+ matches.push(matchIndex);
+ matchIndex = normalizedText.indexOf(
+ normalizedTerm,
+ matchIndex + normalizedTerm.length,
+ );
+ }
+
+ return matches;
+ };
+
+ this.getTextPosition = (segment, offset, preferPrevious = false) => {
+ for (const entry of segment.entries) {
+ if (offset >= entry.start && offset < entry.end) {
+ return {
+ node: entry.node,
+ offset: offset - entry.start,
+ };
+ }
+
+ if (preferPrevious && offset === entry.end) {
+ return {
+ node: entry.node,
+ offset: entry.node.nodeValue?.length || 0,
+ };
+ }
+ }
+
+ const entry = segment.entries[segment.entries.length - 1];
+ return {
+ node: entry.node,
+ offset: entry.node.nodeValue?.length || 0,
+ };
+ };
+
+ this.removeEmptyInlineTextElement = node => {
+ if (
+ !node ||
+ node.nodeType !== Node.ELEMENT_NODE ||
+ !INLINE_TEXT_ELEMENTS.has(node.nodeName) ||
+ node.textContent ||
+ node.querySelector('img, svg, canvas, video, audio, iframe')
+ ) {
+ return;
+ }
+
+ const parent = node.parentNode;
+ parent?.removeChild(node);
+ this.removeEmptyInlineTextElement(parent);
+ };
+
+ this.wrapSegmentMatch = (segment, start, length) => {
+ const end = start + length;
+ const range = document.createRange();
+ const mark = document.createElement('mark');
+ const startPosition = this.getTextPosition(segment, start);
+ const endPosition = this.getTextPosition(segment, end, true);
+
+ mark.className = 'lnreader-search-match';
+ range.setStart(startPosition.node, startPosition.offset);
+ range.setEnd(endPosition.node, endPosition.offset);
+ mark.appendChild(range.extractContents());
+ range.insertNode(mark);
+ this.removeEmptyInlineTextElement(mark.previousSibling);
+ this.removeEmptyInlineTextElement(mark.nextSibling);
+ range.detach?.();
+ };
+
+ this.ensureSearch = query => {
+ const term = String(query ?? this.query ?? '').trim();
+ if (!term) {
+ this.clear();
+ return false;
+ }
+
+ if (term !== this.query || !this.isValidState) {
+ this.search(term, Math.max(0, this.index));
+ }
+
+ return this.matches.length > 0;
+ };
+
+ this.scrollToMatch = match => {
+ if (reader.generalSettings.val.pageReader && window.pageReader) {
+ const rect = match.getBoundingClientRect();
+ const relativePage = Math.floor(
+ (rect.left + rect.width / 2) / reader.layoutWidth,
+ );
+ const page = Math.max(
+ 0,
+ Math.min(
+ pageReader.totalPages.val - 1,
+ pageReader.page.val + relativePage,
+ ),
+ );
+ pageReader.movePage(page);
+ return;
+ }
+
+ requestAnimationFrame(() => {
+ match.scrollIntoView({ block: 'center', behavior: 'smooth' });
+ });
+ };
+
+ this.focus = index => {
+ if (!this.matches.length) {
+ this.index = -1;
+ this.emit();
+ return;
+ }
+
+ const oldIndex = this.index;
+ this.index =
+ ((index % this.matches.length) + this.matches.length) %
+ this.matches.length;
+
+ if (oldIndex !== this.index) {
+ if (oldIndex >= 0) {
+ this.matches[oldIndex]?.classList.remove(
+ 'lnreader-search-match-active',
+ );
+ }
+ this.matches[this.index].classList.add('lnreader-search-match-active');
+ this.scrollToMatch(this.matches[this.index]);
+ }
+
+ this.emit();
+ };
+
+ this.finishSearch = (query, preferredIndex, total) => {
+ this.pendingSearchTimer = null;
+ this.matches = Array.from(
+ reader.chapterElement.querySelectorAll('mark.lnreader-search-match'),
+ );
+ this.total = total;
+ this.isTruncated = this.matches.length < this.total;
+ this.isValidState = true;
+ this.refreshLayout();
+
+ if (!this.matches.length) {
+ this.emit(query);
+ return;
+ }
+
+ this.focus(Math.max(0, Math.min(preferredIndex, this.matches.length - 1)));
+ };
+
+ this.invalidateCache = () => {
+ this.cachedSegments = null;
+ this.isValidState = false;
+ };
+
+ this.search = (query, preferredIndex = 0) => {
+ const term = String(query ?? '').trim();
+ this.cancelPendingSearch();
+ this.resetMatches();
+ this.query = term;
+
+ if (
+ !term ||
+ (term.length < MIN_QUERY_LENGTH && !SPECIAL_CHARACTER_REGEX.test(term))
+ ) {
+ this.emit(term);
+ return;
+ }
+
+ const searchToken = this.searchToken;
+ const normalizedTerm = term.toLowerCase();
+ const textSegments = this.getTextSegments();
+ let textSegmentIndex = 0;
+ let totalMatchCount = 0;
+ let renderedMatchCount = 0;
+
+ const processBatch = () => {
+ if (searchToken !== this.searchToken || term !== this.query) {
+ this.pendingSearchTimer = null;
+ return;
+ }
+
+ const batchEnd = Math.min(
+ textSegmentIndex + SEGMENT_BATCH_SIZE,
+ textSegments.length,
+ );
+
+ while (textSegmentIndex < batchEnd) {
+ const segment = textSegments[textSegmentIndex];
+ const matches = this.findSegmentMatches(segment, normalizedTerm);
+ const renderableMatches = matches.slice(
+ 0,
+ Math.max(0, MAX_RENDERED_MATCHES - renderedMatchCount),
+ );
+
+ renderableMatches.reverse().forEach(matchIndex => {
+ this.wrapSegmentMatch(segment, matchIndex, normalizedTerm.length);
+ });
+
+ renderedMatchCount += renderableMatches.length;
+ totalMatchCount += matches.length;
+ textSegmentIndex += 1;
+ }
+
+ if (textSegmentIndex < textSegments.length) {
+ if (typeof requestIdleCallback !== 'undefined') {
+ this.pendingSearchTimer = requestIdleCallback(processBatch, {
+ timeout: 50,
+ });
+ } else {
+ this.pendingSearchTimer = setTimeout(processBatch, 0);
+ }
+ return;
+ }
+
+ this.finishSearch(term, preferredIndex, totalMatchCount);
+ };
+
+ processBatch();
+ };
+
+ this.next = query => {
+ if (this.ensureSearch(query)) {
+ this.focus(this.index + 1);
+ }
+ };
+
+ this.previous = query => {
+ if (this.ensureSearch(query)) {
+ this.focus(this.index - 1);
+ }
+ };
+})();
diff --git a/android/app/src/main/assets/js/text-vibe.js b/assets/reader/js/text-vibe.js
similarity index 100%
rename from android/app/src/main/assets/js/text-vibe.js
rename to assets/reader/js/text-vibe.js
diff --git a/android/app/src/main/assets/js/van.d.ts b/assets/reader/js/van.d.ts
similarity index 100%
rename from android/app/src/main/assets/js/van.d.ts
rename to assets/reader/js/van.d.ts
diff --git a/android/app/src/main/assets/js/van.js b/assets/reader/js/van.js
similarity index 100%
rename from android/app/src/main/assets/js/van.js
rename to assets/reader/js/van.js
diff --git a/babel.config.js b/babel.config.js
index d5de9ae37..44242b422 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -5,7 +5,7 @@ const ReactCompilerConfig = {
export default function (api) {
api.cache(true);
return {
- presets: ['module:@react-native/babel-preset'],
+ presets: ['babel-preset-expo'],
plugins: [
'module:@babel/plugin-transform-export-namespace-from',
['babel-plugin-react-compiler', ReactCompilerConfig],
@@ -17,7 +17,7 @@ export default function (api) {
'@database': './src/database',
'@hooks': './src/hooks',
'@screens': './src/screens',
- '@strings': './strings',
+ '@i18n': './src/i18n',
'@services': './src/services',
'@plugins': './src/plugins',
'@utils': './src/utils',
@@ -26,7 +26,11 @@ export default function (api) {
'@api': './src/api',
'@type': './src/type',
'@specs': './specs',
- '@test-utils': './__tests-modules__/test-utils',
+ '@test-utils': './test/test-utils',
+ '@env': './src/generated/build-info',
+ '@modules/nitro-epub': './modules/nitro-epub/src/index',
+ '@modules/nitro-tts': './modules/nitro-tts/src/index',
+ '@modules': './modules',
'react-native-vector-icons/MaterialCommunityIcons':
'@react-native-vector-icons/material-design-icons',
},
diff --git a/crowdin.yml b/crowdin.yml
index df73f2f91..d791cbcdf 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -1,5 +1,5 @@
pull_request_title: 'chore: Update Translations'
commit_message: '[ci skip]'
files:
- - source: /strings/languages/en/strings.json
- translation: /strings/languages/%locale_with_underscore%/strings.json
+ - source: /src/i18n/languages/en/strings.json
+ translation: /src/i18n/languages/%locale_with_underscore%/strings.json
diff --git a/drizzle/20260612232322_normal_saracen/migration.sql b/drizzle/20260612232322_normal_saracen/migration.sql
new file mode 100644
index 000000000..9977fd82b
--- /dev/null
+++ b/drizzle/20260612232322_normal_saracen/migration.sql
@@ -0,0 +1 @@
+ALTER TABLE `Chapter` ADD `scanlator` text;
\ No newline at end of file
diff --git a/drizzle/20260612232322_normal_saracen/snapshot.json b/drizzle/20260612232322_normal_saracen/snapshot.json
new file mode 100644
index 000000000..6d211c12d
--- /dev/null
+++ b/drizzle/20260612232322_normal_saracen/snapshot.json
@@ -0,0 +1,634 @@
+{
+ "version": "7",
+ "dialect": "sqlite",
+ "id": "51b6018f-2f9b-49d9-9762-37522abbdbf1",
+ "prevIds": [
+ "cf9a3ebf-235d-4a1d-8b65-297a5d2847e8"
+ ],
+ "ddl": [
+ {
+ "name": "Category",
+ "entityType": "tables"
+ },
+ {
+ "name": "Chapter",
+ "entityType": "tables"
+ },
+ {
+ "name": "NovelCategory",
+ "entityType": "tables"
+ },
+ {
+ "name": "Novel",
+ "entityType": "tables"
+ },
+ {
+ "name": "Repository",
+ "entityType": "tables"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Category"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "Category"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "sort",
+ "entityType": "columns",
+ "table": "Category"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "novelId",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "path",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "releaseTime",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "bookmark",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "unread",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "readTime",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "isDownloaded",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "updatedTime",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "real",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "chapterNumber",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'1'",
+ "generated": null,
+ "name": "page",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "position",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "progress",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "scanlator",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "NovelCategory"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "novelId",
+ "entityType": "columns",
+ "table": "NovelCategory"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "categoryId",
+ "entityType": "columns",
+ "table": "NovelCategory"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "path",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "pluginId",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "cover",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "summary",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "author",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "artist",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'Unknown'",
+ "generated": null,
+ "name": "status",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "genres",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "inLibrary",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "isLocal",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "totalPages",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "chaptersDownloaded",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "chaptersUnread",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "totalChapters",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "lastReadAt",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "lastUpdatedAt",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Repository"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "url",
+ "entityType": "columns",
+ "table": "Repository"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Category_pk",
+ "table": "Category",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Chapter_pk",
+ "table": "Chapter",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "NovelCategory_pk",
+ "table": "NovelCategory",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Novel_pk",
+ "table": "Novel",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Repository_pk",
+ "table": "Repository",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ {
+ "value": "name",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "category_name_unique",
+ "entityType": "indexes",
+ "table": "Category"
+ },
+ {
+ "columns": [
+ {
+ "value": "sort",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "category_sort_idx",
+ "entityType": "indexes",
+ "table": "Category"
+ },
+ {
+ "columns": [
+ {
+ "value": "novelId",
+ "isExpression": false
+ },
+ {
+ "value": "path",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "chapter_novel_path_unique",
+ "entityType": "indexes",
+ "table": "Chapter"
+ },
+ {
+ "columns": [
+ {
+ "value": "novelId",
+ "isExpression": false
+ },
+ {
+ "value": "position",
+ "isExpression": false
+ },
+ {
+ "value": "page",
+ "isExpression": false
+ },
+ {
+ "value": "id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "chapterNovelIdIndex",
+ "entityType": "indexes",
+ "table": "Chapter"
+ },
+ {
+ "columns": [
+ {
+ "value": "novelId",
+ "isExpression": false
+ },
+ {
+ "value": "categoryId",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "novel_category_unique",
+ "entityType": "indexes",
+ "table": "NovelCategory"
+ },
+ {
+ "columns": [
+ {
+ "value": "path",
+ "isExpression": false
+ },
+ {
+ "value": "pluginId",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "novel_path_plugin_unique",
+ "entityType": "indexes",
+ "table": "Novel"
+ },
+ {
+ "columns": [
+ {
+ "value": "pluginId",
+ "isExpression": false
+ },
+ {
+ "value": "path",
+ "isExpression": false
+ },
+ {
+ "value": "id",
+ "isExpression": false
+ },
+ {
+ "value": "inLibrary",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "NovelIndex",
+ "entityType": "indexes",
+ "table": "Novel"
+ },
+ {
+ "columns": [
+ {
+ "value": "url",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "repository_url_unique",
+ "entityType": "indexes",
+ "table": "Repository"
+ }
+ ],
+ "renames": []
+}
\ No newline at end of file
diff --git a/drizzle/20260719143427_long_moondragon/migration.sql b/drizzle/20260719143427_long_moondragon/migration.sql
new file mode 100644
index 000000000..b0f9ccbe6
--- /dev/null
+++ b/drizzle/20260719143427_long_moondragon/migration.sql
@@ -0,0 +1 @@
+ALTER TABLE `Chapter` ADD `timeSpent` integer DEFAULT 0;
\ No newline at end of file
diff --git a/drizzle/20260719143427_long_moondragon/snapshot.json b/drizzle/20260719143427_long_moondragon/snapshot.json
new file mode 100644
index 000000000..27cabe777
--- /dev/null
+++ b/drizzle/20260719143427_long_moondragon/snapshot.json
@@ -0,0 +1,644 @@
+{
+ "version": "7",
+ "dialect": "sqlite",
+ "id": "498c0b85-a3f4-413f-8fed-7914c3ba15d9",
+ "prevIds": [
+ "51b6018f-2f9b-49d9-9762-37522abbdbf1"
+ ],
+ "ddl": [
+ {
+ "name": "Category",
+ "entityType": "tables"
+ },
+ {
+ "name": "Chapter",
+ "entityType": "tables"
+ },
+ {
+ "name": "NovelCategory",
+ "entityType": "tables"
+ },
+ {
+ "name": "Novel",
+ "entityType": "tables"
+ },
+ {
+ "name": "Repository",
+ "entityType": "tables"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Category"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "Category"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "sort",
+ "entityType": "columns",
+ "table": "Category"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "novelId",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "path",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "releaseTime",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "bookmark",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "unread",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "readTime",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "isDownloaded",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "updatedTime",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "real",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "chapterNumber",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'1'",
+ "generated": null,
+ "name": "page",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "position",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "progress",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "scanlator",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "timeSpent",
+ "entityType": "columns",
+ "table": "Chapter"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "NovelCategory"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "novelId",
+ "entityType": "columns",
+ "table": "NovelCategory"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "categoryId",
+ "entityType": "columns",
+ "table": "NovelCategory"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "path",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "pluginId",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "cover",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "summary",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "author",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "artist",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'Unknown'",
+ "generated": null,
+ "name": "status",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "genres",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "inLibrary",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "isLocal",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "totalPages",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "chaptersDownloaded",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "chaptersUnread",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "totalChapters",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "lastReadAt",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "lastUpdatedAt",
+ "entityType": "columns",
+ "table": "Novel"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "Repository"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "url",
+ "entityType": "columns",
+ "table": "Repository"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Category_pk",
+ "table": "Category",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Chapter_pk",
+ "table": "Chapter",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "NovelCategory_pk",
+ "table": "NovelCategory",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Novel_pk",
+ "table": "Novel",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ "id"
+ ],
+ "nameExplicit": false,
+ "name": "Repository_pk",
+ "table": "Repository",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ {
+ "value": "name",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "category_name_unique",
+ "entityType": "indexes",
+ "table": "Category"
+ },
+ {
+ "columns": [
+ {
+ "value": "sort",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "category_sort_idx",
+ "entityType": "indexes",
+ "table": "Category"
+ },
+ {
+ "columns": [
+ {
+ "value": "novelId",
+ "isExpression": false
+ },
+ {
+ "value": "path",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "chapter_novel_path_unique",
+ "entityType": "indexes",
+ "table": "Chapter"
+ },
+ {
+ "columns": [
+ {
+ "value": "novelId",
+ "isExpression": false
+ },
+ {
+ "value": "position",
+ "isExpression": false
+ },
+ {
+ "value": "page",
+ "isExpression": false
+ },
+ {
+ "value": "id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "chapterNovelIdIndex",
+ "entityType": "indexes",
+ "table": "Chapter"
+ },
+ {
+ "columns": [
+ {
+ "value": "novelId",
+ "isExpression": false
+ },
+ {
+ "value": "categoryId",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "novel_category_unique",
+ "entityType": "indexes",
+ "table": "NovelCategory"
+ },
+ {
+ "columns": [
+ {
+ "value": "path",
+ "isExpression": false
+ },
+ {
+ "value": "pluginId",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "novel_path_plugin_unique",
+ "entityType": "indexes",
+ "table": "Novel"
+ },
+ {
+ "columns": [
+ {
+ "value": "pluginId",
+ "isExpression": false
+ },
+ {
+ "value": "path",
+ "isExpression": false
+ },
+ {
+ "value": "id",
+ "isExpression": false
+ },
+ {
+ "value": "inLibrary",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "NovelIndex",
+ "entityType": "indexes",
+ "table": "Novel"
+ },
+ {
+ "columns": [
+ {
+ "value": "url",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "repository_url_unique",
+ "entityType": "indexes",
+ "table": "Repository"
+ }
+ ],
+ "renames": []
+}
\ No newline at end of file
diff --git a/drizzle/20260727081855_calm_chimera/migration.sql b/drizzle/20260727081855_calm_chimera/migration.sql
new file mode 100644
index 000000000..1f709f3a5
--- /dev/null
+++ b/drizzle/20260727081855_calm_chimera/migration.sql
@@ -0,0 +1,126 @@
+DROP TRIGGER IF EXISTS `update_novel_stats`;--> statement-breakpoint
+DROP TRIGGER IF EXISTS `update_novel_stats_on_update`;--> statement-breakpoint
+DROP TRIGGER IF EXISTS `update_novel_stats_on_delete`;--> statement-breakpoint
+CREATE TABLE IF NOT EXISTS `__migration_Novel` AS SELECT * FROM `Novel`;--> statement-breakpoint
+CREATE TABLE IF NOT EXISTS `__migration_Chapter` AS SELECT * FROM `Chapter`;--> statement-breakpoint
+CREATE TABLE IF NOT EXISTS `__migration_NovelCategory` AS SELECT * FROM `NovelCategory`;--> statement-breakpoint
+DROP TABLE IF EXISTS `__new_Novel`;--> statement-breakpoint
+CREATE TABLE `__new_Novel` (
+ `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `path` text NOT NULL,
+ `pluginId` text NOT NULL,
+ `name` text NOT NULL,
+ `cover` text,
+ `summary` text,
+ `author` text,
+ `artist` text,
+ `status` text DEFAULT 'Unknown',
+ `genres` text,
+ `inLibrary` integer DEFAULT false,
+ `isLocal` integer DEFAULT false,
+ `totalPages` integer DEFAULT 0,
+ `chaptersDownloaded` integer DEFAULT 0,
+ `chaptersUnread` integer DEFAULT 0,
+ `totalChapters` integer DEFAULT 0,
+ `lastReadAt` text,
+ `lastUpdatedAt` text
+);
+--> statement-breakpoint
+INSERT INTO `__new_Novel` (
+ `id`,
+ `path`,
+ `pluginId`,
+ `name`,
+ `cover`,
+ `summary`,
+ `author`,
+ `artist`,
+ `status`,
+ `genres`,
+ `inLibrary`,
+ `isLocal`,
+ `totalPages`,
+ `chaptersDownloaded`,
+ `chaptersUnread`,
+ `totalChapters`,
+ `lastReadAt`,
+ `lastUpdatedAt`
+)
+SELECT
+ `__migration_Novel`.`id`,
+ `__migration_Novel`.`path`,
+ `__migration_Novel`.`pluginId`,
+ `__migration_Novel`.`name`,
+ `__migration_Novel`.`cover`,
+ `__migration_Novel`.`summary`,
+ `__migration_Novel`.`author`,
+ `__migration_Novel`.`artist`,
+ `__migration_Novel`.`status`,
+ `__migration_Novel`.`genres`,
+ `__migration_Novel`.`inLibrary`,
+ `__migration_Novel`.`isLocal`,
+ `__migration_Novel`.`totalPages`,
+ COALESCE(`chapter_stats`.`chaptersDownloaded`, 0),
+ COALESCE(`chapter_stats`.`chaptersUnread`, 0),
+ COALESCE(`chapter_stats`.`totalChapters`, 0),
+ `chapter_stats`.`lastReadAt`,
+ `chapter_stats`.`lastUpdatedAt`
+FROM `__migration_Novel`
+LEFT JOIN (
+ SELECT
+ `novelId`,
+ SUM(CASE WHEN `isDownloaded` = 1 THEN 1 ELSE 0 END) AS `chaptersDownloaded`,
+ SUM(CASE WHEN `unread` = 1 THEN 1 ELSE 0 END) AS `chaptersUnread`,
+ COUNT(*) AS `totalChapters`,
+ MAX(`readTime`) AS `lastReadAt`,
+ MAX(`updatedTime`) AS `lastUpdatedAt`
+ FROM `__migration_Chapter`
+ GROUP BY `novelId`
+) AS `chapter_stats`
+ ON `chapter_stats`.`novelId` = `__migration_Novel`.`id`;--> statement-breakpoint
+DELETE FROM `NovelCategory`;--> statement-breakpoint
+DELETE FROM `Chapter`;--> statement-breakpoint
+DROP TABLE IF EXISTS `Novel`;--> statement-breakpoint
+ALTER TABLE `__new_Novel` RENAME TO `Novel`;--> statement-breakpoint
+INSERT OR REPLACE INTO `Chapter` (
+ `id`,
+ `novelId`,
+ `path`,
+ `name`,
+ `releaseTime`,
+ `bookmark`,
+ `unread`,
+ `readTime`,
+ `isDownloaded`,
+ `updatedTime`,
+ `chapterNumber`,
+ `page`,
+ `position`,
+ `progress`,
+ `scanlator`,
+ `timeSpent`
+)
+SELECT
+ `id`,
+ `novelId`,
+ `path`,
+ `name`,
+ `releaseTime`,
+ `bookmark`,
+ `unread`,
+ `readTime`,
+ `isDownloaded`,
+ `updatedTime`,
+ `chapterNumber`,
+ `page`,
+ `position`,
+ `progress`,
+ `scanlator`,
+ `timeSpent`
+FROM `__migration_Chapter`;--> statement-breakpoint
+INSERT OR REPLACE INTO `NovelCategory` SELECT * FROM `__migration_NovelCategory`;--> statement-breakpoint
+DROP TABLE `__migration_Novel`;--> statement-breakpoint
+DROP TABLE `__migration_Chapter`;--> statement-breakpoint
+DROP TABLE `__migration_NovelCategory`;--> statement-breakpoint
+CREATE UNIQUE INDEX `novel_path_plugin_unique` ON `Novel` (`path`,`pluginId`);--> statement-breakpoint
+CREATE INDEX `NovelIndex` ON `Novel` (`pluginId`,`path`,`id`,`inLibrary`);
diff --git a/drizzle/migrations.js b/drizzle/migrations.js
index 42832eb58..6a8167e3f 100644
--- a/drizzle/migrations.js
+++ b/drizzle/migrations.js
@@ -1,7 +1,15 @@
+// This file is required for Expo/React Native SQLite migrations - https://orm.drizzle.team/quick-sqlite/expo
+
import m0000 from './20251222152612_past_mandrill/migration.sql';
+import m0001 from './20260612232322_normal_saracen/migration.sql';
+import m0002 from './20260719143427_long_moondragon/migration.sql';
+import m0003 from './20260727081855_calm_chimera/migration.sql';
export default {
migrations: {
'20251222152612_past_mandrill': m0000,
+ '20260612232322_normal_saracen': m0001,
+ '20260719143427_long_moondragon': m0002,
+ '20260727081855_calm_chimera': m0003,
},
};
diff --git a/eas.json b/eas.json
new file mode 100644
index 000000000..62049ab70
--- /dev/null
+++ b/eas.json
@@ -0,0 +1,21 @@
+{
+ "cli": {
+ "version": ">= 20.5.1",
+ "appVersionSource": "local"
+ },
+ "build": {
+ "development": {
+ "developmentClient": true,
+ "distribution": "internal"
+ },
+ "preview": {
+ "distribution": "internal"
+ },
+ "production": {
+ "autoIncrement": true
+ }
+ },
+ "submit": {
+ "production": {}
+ }
+}
diff --git a/env.d.ts b/env.d.ts
deleted file mode 100644
index a62595a63..000000000
--- a/env.d.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-declare module 'react-native-config' {
- export interface NativeConfig {
- MYANIMELIST_CLIENT_ID: string;
- ANILIST_CLIENT_ID: string;
- GIT_HASH: string;
- RELEASE_DATE: string;
- BUILD_TYPE: 'Debug' | 'Release' | 'Beta' | 'Github Action';
- }
-
- export const Config: NativeConfig;
- export default Config;
-}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 000000000..0f106f207
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,62 @@
+const { defineConfig, globalIgnores } = require('eslint/config');
+const expoConfig = require('eslint-config-expo/flat');
+const { FlatCompat } = require('@eslint/eslintrc');
+
+const compat = new FlatCompat({
+ baseDirectory: __dirname,
+});
+
+const testFiles = [
+ '**/__tests__/**/*.[jt]s?(x)',
+ '**/?(*.)+(spec|test).[jt]s?(x)',
+];
+
+module.exports = defineConfig([
+ globalIgnores([
+ 'android/**',
+ 'ios/**',
+ 'node_modules/**',
+ '.expo/**',
+ 'dist/**',
+ 'coverage/**',
+ ]),
+ expoConfig,
+ ...compat
+ .extends('plugin:testing-library/react', 'plugin:jest/recommended')
+ .map(config => ({
+ ...config,
+ files: testFiles,
+ })),
+
+ {
+ files: ['**/*.{js,jsx,ts,tsx}'],
+ rules: {
+ 'no-shadow': 'off',
+ 'no-undef': 'off',
+ 'no-console': 'error',
+
+ '@typescript-eslint/no-shadow': 'warn',
+
+ 'react-hooks/exhaustive-deps': 'warn',
+ 'react-hooks/static-components': 'warn',
+ 'react-hooks/purity': 'warn',
+ 'react-hooks/set-state-in-effect': 'warn',
+ 'react-hooks/refs': 'warn',
+ 'react-hooks/immutability': 'warn',
+ 'react/no-children-prop': 'warn',
+
+ 'react/display-name': 'off',
+
+ curly: ['error', 'multi-line', 'consistent'],
+ 'no-useless-return': 'error',
+ 'block-scoped-var': 'error',
+ 'no-var': 'error',
+ 'prefer-const': 'error',
+ 'no-dupe-else-if': 'error',
+ 'no-duplicate-imports': 'error',
+
+ '@react-native/no-deep-imports': 'off',
+ '@typescript-eslint/no-require-imports': 'off',
+ },
+ },
+]);
diff --git a/index.js b/index.js
index 6d720428b..8f42a73fd 100644
--- a/index.js
+++ b/index.js
@@ -1,7 +1,13 @@
import 'react-native-gesture-handler';
import { registerRootComponent } from 'expo';
-import { I18nManager } from 'react-native';
-import { i18n } from './strings/translations';
+import { AppRegistry, I18nManager } from 'react-native';
+import { i18n } from './src/i18n/translations';
+import { runHeadlessBackgroundTask } from './src/services/backgroundTasks';
+
+AppRegistry.registerHeadlessTask(
+ 'LNReaderBackgroundTask',
+ () => runHeadlessBackgroundTask,
+);
const isRTL = i18n.locale.startsWith('ar') || i18n.locale.startsWith('he');
I18nManager.allowRTL(isRTL);
diff --git a/ios/.xcode.env b/ios/.xcode.env
deleted file mode 100644
index 772b339b4..000000000
--- a/ios/.xcode.env
+++ /dev/null
@@ -1 +0,0 @@
-export NODE_BINARY=$(command -v node)
diff --git a/ios/Dynamic.swift b/ios/Dynamic.swift
deleted file mode 100644
index cdfde8eac..000000000
--- a/ios/Dynamic.swift
+++ /dev/null
@@ -1,22 +0,0 @@
-import UIKit
-import Foundation
-import Lottie
-
-@objc class Dynamic: NSObject {
-
- @objc func createAnimationView(rootView: UIView, lottieName: String) -> AnimationView {
- let animationView = AnimationView(name: lottieName)
- animationView.frame = rootView.frame
- animationView.center = rootView.center
- animationView.backgroundColor = UIColor.white;
- return animationView;
- }
-
- @objc func play(animationView: AnimationView) {
- animationView.play(
- completion: { (success) in
- RNSplashScreen.setAnimationFinished(true)
- }
- );
- }
-}
diff --git a/ios/LNReader.xcodeproj/project.pbxproj b/ios/LNReader.xcodeproj/project.pbxproj
deleted file mode 100644
index ad17a7e0c..000000000
--- a/ios/LNReader.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,598 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 54;
- objects = {
-
-/* Begin PBXBuildFile section */
- 0C80B921A6F3F58F76C31292 /* libPods-LNReader.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-LNReader.a */; };
- 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
- 147488B02DD8E9BD00C6D0A2 /* Dynamic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147488AF2DD8E9BD00C6D0A2 /* Dynamic.swift */; };
- 147488B22DD8EA0700C6D0A2 /* loading.json in Resources */ = {isa = PBXBuildFile; fileRef = 147488B12DD8EA0700C6D0A2 /* loading.json */; };
- 147488B72DD97E8A00C6D0A2 /* RCTNativeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147488B62DD97E8A00C6D0A2 /* RCTNativeFile.swift */; };
- 147488BC2DD9882000C6D0A2 /* RCTNativeFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488BB2DD9882000C6D0A2 /* RCTNativeFile.mm */; };
- 147488C02DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488BF2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm */; };
- 147488C42DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488C32DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm */; };
- 147488C82DD9A68600C6D0A2 /* RCTNativeZipArchive.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488C72DD9A68600C6D0A2 /* RCTNativeZipArchive.mm */; };
- 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
- 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
- A1FA4E89FDA7CFAD14C43693 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F74819B237E0768BB72DED8 /* ExpoModulesProvider.swift */; };
- CF1A2676AC104DBC83DBFFF9 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXFileReference section */
- 13B07F961A680F5B00A75B9A /* LNReader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LNReader.app; sourceTree = BUILT_PRODUCTS_DIR; };
- 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = LNReader/Images.xcassets; sourceTree = ""; };
- 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = LNReader/Info.plist; sourceTree = ""; };
- 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = LNReader/PrivacyInfo.xcprivacy; sourceTree = ""; };
- 147488AA2DD8E7DE00C6D0A2 /* LNReader-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "LNReader-Bridging-Header.h"; path = "LNReader/LNReader-Bridging-Header.h"; sourceTree = ""; };
- 147488AF2DD8E9BD00C6D0A2 /* Dynamic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dynamic.swift; sourceTree = ""; };
- 147488B12DD8EA0700C6D0A2 /* loading.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = loading.json; sourceTree = ""; };
- 147488B62DD97E8A00C6D0A2 /* RCTNativeFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RCTNativeFile.swift; sourceTree = ""; };
- 147488BA2DD9882000C6D0A2 /* RCTNativeFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeFile.h; sourceTree = ""; };
- 147488BB2DD9882000C6D0A2 /* RCTNativeFile.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeFile.mm; sourceTree = ""; };
- 147488BE2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeVolumeButtonListener.h; sourceTree = ""; };
- 147488BF2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeVolumeButtonListener.mm; sourceTree = ""; };
- 147488C22DD9A27400C6D0A2 /* RCTNativeEpubUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeEpubUtil.h; sourceTree = ""; };
- 147488C32DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeEpubUtil.mm; sourceTree = ""; };
- 147488C62DD9A68600C6D0A2 /* RCTNativeZipArchive.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeZipArchive.h; sourceTree = ""; };
- 147488C72DD9A68600C6D0A2 /* RCTNativeZipArchive.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeZipArchive.mm; sourceTree = ""; };
- 1F74819B237E0768BB72DED8 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-LNReader/ExpoModulesProvider.swift"; sourceTree = ""; };
- 3B4392A12AC88292D35C810B /* Pods-LNReader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LNReader.debug.xcconfig"; path = "Target Support Files/Pods-LNReader/Pods-LNReader.debug.xcconfig"; sourceTree = ""; };
- 5709B34CF0A7D63546082F79 /* Pods-LNReader.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LNReader.release.xcconfig"; path = "Target Support Files/Pods-LNReader/Pods-LNReader.release.xcconfig"; sourceTree = ""; };
- 5DCACB8F33CDC322A6C60F78 /* libPods-LNReader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LNReader.a"; sourceTree = BUILT_PRODUCTS_DIR; };
- 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = LNReader/AppDelegate.swift; sourceTree = ""; };
- 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = LNReader/LaunchScreen.storyboard; sourceTree = ""; };
- ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 0C80B921A6F3F58F76C31292 /* libPods-LNReader.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- 13B07FAE1A68108700A75B9A /* LNReader */ = {
- isa = PBXGroup;
- children = (
- 13B07FB51A68108700A75B9A /* Images.xcassets */,
- 761780EC2CA45674006654EE /* AppDelegate.swift */,
- 147488B12DD8EA0700C6D0A2 /* loading.json */,
- 13B07FB61A68108700A75B9A /* Info.plist */,
- 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
- 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
- 147488AA2DD8E7DE00C6D0A2 /* LNReader-Bridging-Header.h */,
- 147488AF2DD8E9BD00C6D0A2 /* Dynamic.swift */,
- 147488B62DD97E8A00C6D0A2 /* RCTNativeFile.swift */,
- );
- name = LNReader;
- sourceTree = "";
- };
- 147488B92DD987DF00C6D0A2 /* NativeFile */ = {
- isa = PBXGroup;
- children = (
- 147488BA2DD9882000C6D0A2 /* RCTNativeFile.h */,
- 147488BB2DD9882000C6D0A2 /* RCTNativeFile.mm */,
- );
- path = NativeFile;
- sourceTree = "";
- };
- 147488BD2DD9A0E900C6D0A2 /* NativeVolumeButtonListener */ = {
- isa = PBXGroup;
- children = (
- 147488BE2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.h */,
- 147488BF2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm */,
- );
- path = NativeVolumeButtonListener;
- sourceTree = "";
- };
- 147488C12DD9A25E00C6D0A2 /* NativeEpubUtil */ = {
- isa = PBXGroup;
- children = (
- 147488C22DD9A27400C6D0A2 /* RCTNativeEpubUtil.h */,
- 147488C32DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm */,
- );
- path = NativeEpubUtil;
- sourceTree = "";
- };
- 147488C52DD9A66F00C6D0A2 /* NativeZipArchive */ = {
- isa = PBXGroup;
- children = (
- 147488C62DD9A68600C6D0A2 /* RCTNativeZipArchive.h */,
- 147488C72DD9A68600C6D0A2 /* RCTNativeZipArchive.mm */,
- );
- path = NativeZipArchive;
- sourceTree = "";
- };
- 24615CBE8A657680F1260CBB /* ExpoModulesProviders */ = {
- isa = PBXGroup;
- children = (
- 28BBFC4322E6EFCA529574FE /* LNReader */,
- );
- name = ExpoModulesProviders;
- sourceTree = "";
- };
- 28BBFC4322E6EFCA529574FE /* LNReader */ = {
- isa = PBXGroup;
- children = (
- 1F74819B237E0768BB72DED8 /* ExpoModulesProvider.swift */,
- );
- name = LNReader;
- sourceTree = "";
- };
- 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
- 5DCACB8F33CDC322A6C60F78 /* libPods-LNReader.a */,
- );
- name = Frameworks;
- sourceTree = "";
- };
- 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
- isa = PBXGroup;
- children = (
- );
- name = Libraries;
- sourceTree = "";
- };
- 83CBB9F61A601CBA00E9B192 /* PBXGroup */ = {
- isa = PBXGroup;
- children = (
- 147488C52DD9A66F00C6D0A2 /* NativeZipArchive */,
- 147488C12DD9A25E00C6D0A2 /* NativeEpubUtil */,
- 147488BD2DD9A0E900C6D0A2 /* NativeVolumeButtonListener */,
- 147488B92DD987DF00C6D0A2 /* NativeFile */,
- 13B07FAE1A68108700A75B9A /* LNReader */,
- 832341AE1AAA6A7D00B99B32 /* Libraries */,
- 83CBBA001A601CBA00E9B192 /* Products */,
- 2D16E6871FA4F8E400B85C8A /* Frameworks */,
- BBD78D7AC51CEA395F1C20DB /* Pods */,
- 24615CBE8A657680F1260CBB /* ExpoModulesProviders */,
- );
- indentWidth = 2;
- sourceTree = "";
- tabWidth = 2;
- usesTabs = 0;
- };
- 83CBBA001A601CBA00E9B192 /* Products */ = {
- isa = PBXGroup;
- children = (
- 13B07F961A680F5B00A75B9A /* LNReader.app */,
- );
- name = Products;
- sourceTree = "";
- };
- BBD78D7AC51CEA395F1C20DB /* Pods */ = {
- isa = PBXGroup;
- children = (
- 3B4392A12AC88292D35C810B /* Pods-LNReader.debug.xcconfig */,
- 5709B34CF0A7D63546082F79 /* Pods-LNReader.release.xcconfig */,
- );
- path = Pods;
- sourceTree = "";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- 13B07F861A680F5B00A75B9A /* LNReader */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "LNReader" */;
- buildPhases = (
- C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
- E2CB0E1FD58C9A385B2CE9DA /* [Expo] Configure project */,
- 13B07F871A680F5B00A75B9A /* Sources */,
- 13B07F8C1A680F5B00A75B9A /* Frameworks */,
- 13B07F8E1A680F5B00A75B9A /* Resources */,
- 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
- 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
- E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = LNReader;
- productName = LNReader;
- productReference = 13B07F961A680F5B00A75B9A /* LNReader.app */;
- productType = "com.apple.product-type.application";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- 83CBB9F71A601CBA00E9B192 = {
- isa = PBXProject;
- attributes = {
- LastUpgradeCheck = 1210;
- TargetAttributes = {
- 13B07F861A680F5B00A75B9A = {
- LastSwiftMigration = 1120;
- };
- };
- };
- buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "LNReader" */;
- compatibilityVersion = "Xcode 12.0";
- developmentRegion = en;
- hasScannedForEncodings = 0;
- knownRegions = (
- en,
- Base,
- );
- mainGroup = 83CBB9F61A601CBA00E9B192 /* PBXGroup */;
- productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
- projectDirPath = "";
- projectRoot = "";
- targets = (
- 13B07F861A680F5B00A75B9A /* LNReader */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
- 13B07F8E1A680F5B00A75B9A /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
- 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
- 147488B22DD8EA0700C6D0A2 /* loading.json in Resources */,
- CF1A2676AC104DBC83DBFFF9 /* PrivacyInfo.xcprivacy in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
- 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- "$(SRCROOT)/.xcode.env.local",
- "$(SRCROOT)/.xcode.env",
- );
- name = "Bundle React Native code and images";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
- };
- 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-frameworks-${CONFIGURATION}-input-files.xcfilelist",
- );
- name = "[CP] Embed Pods Frameworks";
- outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-frameworks-${CONFIGURATION}-output-files.xcfilelist",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-frameworks.sh\"\n";
- showEnvVarsInLog = 0;
- };
- C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
- );
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-LNReader-checkManifestLockResult.txt",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
- showEnvVarsInLog = 0;
- };
- E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-resources-${CONFIGURATION}-input-files.xcfilelist",
- );
- name = "[CP] Copy Pods Resources";
- outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-resources-${CONFIGURATION}-output-files.xcfilelist",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-resources.sh\"\n";
- showEnvVarsInLog = 0;
- };
- E2CB0E1FD58C9A385B2CE9DA /* [Expo] Configure project */ = {
- isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- );
- name = "[Expo] Configure project";
- outputFileListPaths = (
- );
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-LNReader/expo-configure-project.sh\"\n";
- };
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
- 13B07F871A680F5B00A75B9A /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 147488C02DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm in Sources */,
- 147488B02DD8E9BD00C6D0A2 /* Dynamic.swift in Sources */,
- 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */,
- 147488BC2DD9882000C6D0A2 /* RCTNativeFile.mm in Sources */,
- 147488B72DD97E8A00C6D0A2 /* RCTNativeFile.swift in Sources */,
- 147488C82DD9A68600C6D0A2 /* RCTNativeZipArchive.mm in Sources */,
- A1FA4E89FDA7CFAD14C43693 /* ExpoModulesProvider.swift in Sources */,
- 147488C42DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin XCBuildConfiguration section */
- 13B07F941A680F5B00A75B9A /* Debug */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-LNReader.debug.xcconfig */;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
- CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 1;
- ENABLE_BITCODE = NO;
- INFOPLIST_FILE = LNReader/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 15.1;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/Frameworks",
- );
- MARKETING_VERSION = 1.0;
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-ObjC",
- "-lc++",
- );
- OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
- PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
- PRODUCT_NAME = LNReader;
- SWIFT_OBJC_BRIDGING_HEADER = "LNReader/LNReader-Bridging-Header.h";
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 5.0;
- VERSIONING_SYSTEM = "apple-generic";
- };
- name = Debug;
- };
- 13B07F951A680F5B00A75B9A /* Release */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-LNReader.release.xcconfig */;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
- CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 1;
- INFOPLIST_FILE = LNReader/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 15.1;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/Frameworks",
- );
- MARKETING_VERSION = 1.0;
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-ObjC",
- "-lc++",
- );
- OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
- PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
- PRODUCT_NAME = LNReader;
- SWIFT_OBJC_BRIDGING_HEADER = "LNReader/LNReader-Bridging-Header.h";
- SWIFT_VERSION = 5.0;
- VERSIONING_SYSTEM = "apple-generic";
- };
- name = Release;
- };
- 83CBBA201A601CBA00E9B192 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
- CLANG_CXX_LANGUAGE_STANDARD = "c++20";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_COMMA = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- COPY_PHASE_STRIP = NO;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- ENABLE_TESTABILITY = YES;
- "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.1;
- LD_RUNPATH_SEARCH_PATHS = (
- /usr/lib/swift,
- "$(inherited)",
- );
- LIBRARY_SEARCH_PATHS = (
- "\"$(SDKROOT)/usr/lib/swift\"",
- "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
- "\"$(inherited)\"",
- );
- MTL_ENABLE_DEBUG_INFO = YES;
- ONLY_ACTIVE_ARCH = YES;
- OTHER_CPLUSPLUSFLAGS = (
- "$(OTHER_CFLAGS)",
- "-DFOLLY_NO_CONFIG",
- "-DFOLLY_MOBILE=1",
- "-DFOLLY_USE_LIBCPP=1",
- "-DFOLLY_CFG_NO_COROUTINES=1",
- "-DFOLLY_HAVE_CLOCK_GETTIME=1",
- );
- OTHER_LDFLAGS = "$(inherited) ";
- REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
- SDKROOT = iphoneos;
- SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
- USE_HERMES = true;
- SWIFT_VERSION = 5.0;
- };
- name = Debug;
- };
- 83CBBA211A601CBA00E9B192 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
- CLANG_CXX_LANGUAGE_STANDARD = "c++20";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_COMMA = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- COPY_PHASE_STRIP = YES;
- ENABLE_NS_ASSERTIONS = NO;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 15.1;
- LD_RUNPATH_SEARCH_PATHS = (
- /usr/lib/swift,
- "$(inherited)",
- );
- LIBRARY_SEARCH_PATHS = (
- "\"$(SDKROOT)/usr/lib/swift\"",
- "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
- "\"$(inherited)\"",
- );
- MTL_ENABLE_DEBUG_INFO = NO;
- OTHER_CPLUSPLUSFLAGS = (
- "$(OTHER_CFLAGS)",
- "-DFOLLY_NO_CONFIG",
- "-DFOLLY_MOBILE=1",
- "-DFOLLY_USE_LIBCPP=1",
- "-DFOLLY_CFG_NO_COROUTINES=1",
- "-DFOLLY_HAVE_CLOCK_GETTIME=1",
- );
- OTHER_LDFLAGS = "$(inherited) ";
- REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
- SDKROOT = iphoneos;
- USE_HERMES = true;
- VALIDATE_PRODUCT = YES;
- SWIFT_VERSION = 5.0;
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "LNReader" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 13B07F941A680F5B00A75B9A /* Debug */,
- 13B07F951A680F5B00A75B9A /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "LNReader" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 83CBBA201A601CBA00E9B192 /* Debug */,
- 83CBBA211A601CBA00E9B192 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = 83CBB9F71A601CBA00E9B192;
-}
diff --git a/ios/LNReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/LNReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 919434a62..000000000
--- a/ios/LNReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/ios/LNReader.xcodeproj/xcshareddata/xcschemes/LNReader.xcscheme b/ios/LNReader.xcodeproj/xcshareddata/xcschemes/LNReader.xcscheme
deleted file mode 100644
index 55cde05a9..000000000
--- a/ios/LNReader.xcodeproj/xcshareddata/xcschemes/LNReader.xcscheme
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/ios/LNReader.xcworkspace/contents.xcworkspacedata b/ios/LNReader.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index f7ebad31d..000000000
--- a/ios/LNReader.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
diff --git a/ios/LNReader/AppDelegate.swift b/ios/LNReader/AppDelegate.swift
deleted file mode 100644
index 61dae719a..000000000
--- a/ios/LNReader/AppDelegate.swift
+++ /dev/null
@@ -1,71 +0,0 @@
-import UIKit
-import Expo
-import React
-import React_RCTAppDelegate
-import ReactAppDependencyProvider
-import Lottie
-
-@main
-class AppDelegate: ExpoAppDelegate {
- var window: UIWindow?
-
- var reactNativeDelegate: ReactNativeDelegate?
- var reactNativeFactory: RCTReactNativeFactory?
-
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
- ) -> Bool {
- let delegate = ReactNativeDelegate()
- let factory = ExpoReactNativeFactory(delegate: delegate)
- delegate.dependencyProvider = RCTAppDependencyProvider()
-
- reactNativeDelegate = delegate
- reactNativeFactory = factory
- bindReactNativeFactory(factory)
- window = UIWindow(frame: UIScreen.main.bounds)
-
- factory.startReactNative(
- withModuleName: "main",
- in: window,
- launchOptions: launchOptions
- )
-
- if let w = window {
- let splashScreenBackground = UIColor(red: 31/255, green: 32/255, blue: 36/255, alpha: 1)
- w.backgroundColor = splashScreenBackground
- w.rootViewController?.view.backgroundColor = splashScreenBackground
- let t = Dynamic()
- let animationSize = CGSize(width: 200, height: 200)
- let screenBounds = UIScreen.main.bounds
- let animationX = (screenBounds.width - animationSize.width) / 2
- let animationY = (screenBounds.height - animationSize.height) / 2
- let animationUIView: UIView = t.createAnimationView(rootView: UIView(frame:CGRect(x: animationX, y: animationY, width: animationSize.width, height: animationSize.height)), lottieName:"loading")
- RNSplashScreen.showLottieSplash(animationUIView, inRootView: w)
- animationUIView.backgroundColor = UIColor(white: 1, alpha:0)
- t.play(animationView: animationUIView as! AnimationView)
- RNSplashScreen.setAnimationFinished(true)
- }
-
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
-
- override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
- return RCTLinkingManager.application(application, open: url, options: options);
- }
-}
-
-class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
- override func sourceURL(for bridge: RCTBridge) -> URL? {
- // needed to return the correct URL for expo-dev-client.
- bridge.bundleURL ?? bundleURL()
- }
-
- override func bundleURL() -> URL? {
-#if DEBUG
- RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
-#else
- Bundle.main.url(forResource: "main", withExtension: "jsbundle")
-#endif
- }
-}
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/1024.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/1024.png
deleted file mode 100644
index 67c91d55b..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/1024.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/114.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/114.png
deleted file mode 100644
index 65003476e..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/114.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/120.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/120.png
deleted file mode 100644
index 9ebd23d84..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/120.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/180.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/180.png
deleted file mode 100644
index 814e97e13..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/180.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/29.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/29.png
deleted file mode 100644
index 06e5d710a..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/29.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/40.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/40.png
deleted file mode 100644
index 6d40b8d99..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/40.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/57.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/57.png
deleted file mode 100644
index 09a575473..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/57.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/58.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/58.png
deleted file mode 100644
index fe907c053..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/58.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/60.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/60.png
deleted file mode 100644
index 16771fd77..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/60.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/80.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/80.png
deleted file mode 100644
index 600dfe8e3..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/80.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/87.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/87.png
deleted file mode 100644
index 2feb37b7c..000000000
Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/87.png and /dev/null differ
diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/LNReader/Images.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index 73d3b7f6d..000000000
--- a/ios/LNReader/Images.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1 +0,0 @@
-{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]}
\ No newline at end of file
diff --git a/ios/LNReader/Info.plist b/ios/LNReader/Info.plist
deleted file mode 100644
index 968307d57..000000000
--- a/ios/LNReader/Info.plist
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
- CFBundleDevelopmentRegion
- en
- CFBundleDisplayName
- LNReader
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- $(MARKETING_VERSION)
- CFBundleSignature
- ????
- CFBundleURLTypes
-
-
- CFBundleTypeRole
- Editor
- CFBundleURLIconFile
-
- CFBundleURLName
- LNReader
- CFBundleURLSchemes
-
- lnreader
-
-
-
- CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
- LSRequiresIPhoneOS
-
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
- NSAllowsLocalNetworking
-
-
- NSLocationWhenInUseUsageDescription
-
- UILaunchStoryboardName
- LaunchScreen
- UIRequiredDeviceCapabilities
-
- arm64
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
-
-
diff --git a/ios/LNReader/LNReader-Bridging-Header.h b/ios/LNReader/LNReader-Bridging-Header.h
deleted file mode 100644
index b577135ed..000000000
--- a/ios/LNReader/LNReader-Bridging-Header.h
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// LNReader-Bridging-Header.h
-// LNReader
-//
-// Created by QUAN on 17/5/25.
-//
-
-#ifndef LNReader_Bridging_Header_h
-#define LNReader_Bridging_Header_h
-
-#import "RNSplashScreen.h"
-#import
-#import
-
-#endif /* LNReader_Bridging_Header_h */
diff --git a/ios/LNReader/LaunchScreen.storyboard b/ios/LNReader/LaunchScreen.storyboard
deleted file mode 100644
index 1c1f4d25f..000000000
--- a/ios/LNReader/LaunchScreen.storyboard
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/ios/LNReader/PrivacyInfo.xcprivacy b/ios/LNReader/PrivacyInfo.xcprivacy
deleted file mode 100644
index 49b8ded26..000000000
--- a/ios/LNReader/PrivacyInfo.xcprivacy
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
- NSPrivacyAccessedAPITypes
-
-
- NSPrivacyAccessedAPIType
- NSPrivacyAccessedAPICategoryFileTimestamp
- NSPrivacyAccessedAPITypeReasons
-
- C617.1
- 0A2A.1
- 3B52.1
-
-
-
- NSPrivacyAccessedAPIType
- NSPrivacyAccessedAPICategoryUserDefaults
- NSPrivacyAccessedAPITypeReasons
-
- CA92.1
-
-
-
- NSPrivacyAccessedAPIType
- NSPrivacyAccessedAPICategoryDiskSpace
- NSPrivacyAccessedAPITypeReasons
-
- E174.1
- 85F4.1
-
-
-
- NSPrivacyAccessedAPIType
- NSPrivacyAccessedAPICategorySystemBootTime
- NSPrivacyAccessedAPITypeReasons
-
- 35F9.1
-
-
-
- NSPrivacyCollectedDataTypes
-
- NSPrivacyTracking
-
-
-
diff --git a/ios/NativeEpubUtil/RCTNativeEpubUtil.h b/ios/NativeEpubUtil/RCTNativeEpubUtil.h
deleted file mode 100644
index 3d0bf5b69..000000000
--- a/ios/NativeEpubUtil/RCTNativeEpubUtil.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface RCTNativeEpubUtil : NSObject
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/NativeEpubUtil/RCTNativeEpubUtil.mm b/ios/NativeEpubUtil/RCTNativeEpubUtil.mm
deleted file mode 100644
index 2197e3500..000000000
--- a/ios/NativeEpubUtil/RCTNativeEpubUtil.mm
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-
-#import "RCTNativeEpubUtil.h"
-
-@implementation RCTNativeEpubUtil
-
-
-+ (NSString *)moduleName {
- return @"NativeEpubUtil";
-}
-
-- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
- return std::make_shared(params);
-}
-
-- (NSDictionary * _Nullable)parseNovelAndChapters:(nonnull NSString *)epubDirPath {
- NSMutableDictionary * res = [NSMutableDictionary dictionary];
- // TODO: implement parse epub
- return res;
-}
-
-@end
diff --git a/ios/NativeFile/RCTNativeFile.h b/ios/NativeFile/RCTNativeFile.h
deleted file mode 100644
index 4533b71e0..000000000
--- a/ios/NativeFile/RCTNativeFile.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface RCTNativeFile : NSObject
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/NativeFile/RCTNativeFile.mm b/ios/NativeFile/RCTNativeFile.mm
deleted file mode 100644
index 9b5723ce6..000000000
--- a/ios/NativeFile/RCTNativeFile.mm
+++ /dev/null
@@ -1,92 +0,0 @@
-#import "RCTNativeFile.h"
-
-@implementation RCTNativeFile
-
-+ (NSString *)moduleName {
- return @"NativeFile";
-}
-
-- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
- return std::make_shared(params);
-}
-
-- (void)copyFile:(nonnull NSString *)sourcePath destPath:(nonnull NSString *)destPath {
- [[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destPath error:nil];
-}
-
-- (void)downloadFile:(nonnull NSString *)url destPath:(nonnull NSString *)destPath method:(nonnull NSString *)method headers:(nonnull NSDictionary *)headers body:(nonnull NSString *)body resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
-}
-
-- (nonnull NSNumber *)exists:(nonnull NSString *)filePath {
-
- BOOL isExisted = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
-
- return [NSNumber numberWithBool:isExisted];
-}
-
-- (NSDictionary *)getConstants {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString *documentsDirectory = [paths firstObject];
-
- NSArray *cachePaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
- NSString *cachesDirectory = [cachePaths firstObject];
-
- return @{
- @"ExternalDirectoryPath": documentsDirectory,
- @"ExternalCachesDirectoryPath": cachesDirectory
- };
-}
-
-- (void)mkdir:(nonnull NSString *)filePath {
- [
- [NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil
- ];
-}
-
-- (void)moveFile:(nonnull NSString *)sourcePath destPath:(nonnull NSString *)destPath {
- [
- [NSFileManager defaultManager] moveItemAtPath:sourcePath toPath:destPath error:nil
- ];
-}
-
-- (nonnull NSArray *)readDir:(nonnull NSString *)dirPath {
- NSMutableArray* res = [NSMutableArray array];
- NSArray *content = [
- [NSFileManager defaultManager]
- contentsOfDirectoryAtPath:dirPath error:nil
- ];
- for(int i=0; i)constantsToExport {
- return [self getConstants];
-}
-
-
-
-@end
diff --git a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.h b/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.h
deleted file mode 100644
index 185d9b86f..000000000
--- a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface RCTNativeVolumeButtonListener : NSObject
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.mm b/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.mm
deleted file mode 100644
index 06b6cd6c9..000000000
--- a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.mm
+++ /dev/null
@@ -1,21 +0,0 @@
-#import "RCTNativeVolumeButtonListener.h"
-
-@implementation RCTNativeVolumeButtonListener
-
-+ (NSString *)moduleName {
- return @"NativeVolumeButtonListener";
-}
-
-- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
- return std::make_shared(params);
-}
-
-- (void)addListener:(nonnull NSString *)eventName {
- // TODO: implement addlistener
-}
-
-- (void)removeListeners:(double)count {
- // TODO: implement count listeners
-}
-
-@end
diff --git a/ios/NativeZipArchive/RCTNativeZipArchive.h b/ios/NativeZipArchive/RCTNativeZipArchive.h
deleted file mode 100644
index 1475d365b..000000000
--- a/ios/NativeZipArchive/RCTNativeZipArchive.h
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// RCTNativeZipArchive.h
-// LNReader
-//
-// Created by QUAN on 18/5/25.
-//
-
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface RCTNativeZipArchive : NSObject
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/NativeZipArchive/RCTNativeZipArchive.mm b/ios/NativeZipArchive/RCTNativeZipArchive.mm
deleted file mode 100644
index fb513dc7e..000000000
--- a/ios/NativeZipArchive/RCTNativeZipArchive.mm
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// RCTNativeZipArchive.m
-// LNReader
-//
-// Created by QUAN on 18/5/25.
-//
-
-#import "RCTNativeZipArchive.h"
-
-@implementation RCTNativeZipArchive
-
-+ (NSString *)moduleName {
- return @"NativeZipArchive";
-}
-
-- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
- return std::make_shared(params);
-}
-
-- (void)remoteUnzip:(nonnull NSString *)distDirPath url:(nonnull NSString *)url headers:(nonnull NSDictionary *)headers resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
- // TODO: implement remoteUnzip
-}
-
-- (void)remoteZip:(nonnull NSString *)sourceDirPath url:(nonnull NSString *)url headers:(nonnull NSDictionary *)headers resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
- // TODO: implement remoteZip
-}
-
-- (void)unzip:(nonnull NSString *)sourceFilePath distDirPath:(nonnull NSString *)distDirPath resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
- // TODO: implement unzip
-}
-- (void)zip:(nonnull NSString *)sourceDirPath zipFilePath:(nonnull NSString *)zipFilePath resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
- // TODO: implement zip
-}
-
-@end
diff --git a/ios/Podfile b/ios/Podfile
deleted file mode 100644
index fc71ca6de..000000000
--- a/ios/Podfile
+++ /dev/null
@@ -1,53 +0,0 @@
-require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
-# Resolve react_native_pods.rb with node to allow for hoisting
-require Pod::Executable.execute_command('node', ['-p',
- 'require.resolve(
- "react-native/scripts/react_native_pods.rb",
- {paths: [process.argv[1]]},
- )', __dir__]).strip
-
-platform :ios, 15.5
-prepare_react_native_project!
-
-linkage = ENV['USE_FRAMEWORKS']
-if linkage != nil
- Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
- use_frameworks! :linkage => linkage.to_sym
-end
-
-target 'LNReader' do
- use_expo_modules!
-
- if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
- config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
- else
- config_command = [
- 'node',
- '--no-warnings',
- '--eval',
- 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
- 'react-native-config',
- '--json',
- '--platform',
- 'ios'
- ]
- end
-
- config = use_native_modules!(config_command)
-
- use_react_native!(
- :path => config[:reactNativePath],
- # An absolute path to your application root.
- :app_path => "#{Pod::Config.instance.installation_root}/.."
- )
-
- post_install do |installer|
- # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
- react_native_post_install(
- installer,
- config[:reactNativePath],
- :mac_catalyst_enabled => false,
- # :ccache_enabled => true
- )
- end
-end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
deleted file mode 100644
index 30fe80875..000000000
--- a/ios/Podfile.lock
+++ /dev/null
@@ -1,2735 +0,0 @@
-PODS:
- - AppAuth (1.7.6):
- - AppAuth/Core (= 1.7.6)
- - AppAuth/ExternalUserAgent (= 1.7.6)
- - AppAuth/Core (1.7.6)
- - AppAuth/ExternalUserAgent (1.7.6):
- - AppAuth/Core
- - boost (1.84.0)
- - DoubleConversion (1.1.6)
- - EXApplication (6.1.4):
- - ExpoModulesCore
- - EXConstants (17.1.6):
- - ExpoModulesCore
- - EXNotifications (0.31.1):
- - ExpoModulesCore
- - Expo (53.0.8):
- - DoubleConversion
- - ExpoModulesCore
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTAppDelegate
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactAppDependencyProvider
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - ExpoAdapterGoogleSignIn (13.2.0):
- - ExpoModulesCore
- - GoogleSignIn (~> 7.1)
- - React-Core
- - ExpoAsset (11.1.5):
- - ExpoModulesCore
- - ExpoClipboard (7.1.4):
- - ExpoModulesCore
- - ExpoDocumentPicker (13.1.5):
- - ExpoModulesCore
- - ExpoFileSystem (18.1.9):
- - ExpoModulesCore
- - ExpoFont (13.3.1):
- - ExpoModulesCore
- - ExpoHaptics (14.1.4):
- - ExpoModulesCore
- - ExpoKeepAwake (14.1.4):
- - ExpoModulesCore
- - ExpoLinearGradient (14.1.4):
- - ExpoModulesCore
- - ExpoLinking (7.1.4):
- - ExpoModulesCore
- - ExpoLocalization (16.1.5):
- - ExpoModulesCore
- - ExpoModulesCore (2.3.12):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-jsinspector
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - ExpoSpeech (13.1.6):
- - ExpoModulesCore
- - ExpoSQLite (15.2.9):
- - ExpoModulesCore
- - ExpoWebBrowser (14.1.6):
- - ExpoModulesCore
- - fast_float (6.1.4)
- - FBLazyVector (0.79.2)
- - fmt (11.0.2)
- - glog (0.3.5)
- - GoogleSignIn (7.1.0):
- - AppAuth (< 2.0, >= 1.7.3)
- - GTMAppAuth (< 5.0, >= 4.1.1)
- - GTMSessionFetcher/Core (~> 3.3)
- - GTMAppAuth (4.1.1):
- - AppAuth/Core (~> 1.7)
- - GTMSessionFetcher/Core (< 4.0, >= 3.3)
- - GTMSessionFetcher/Core (3.5.0)
- - hermes-engine (0.79.2):
- - hermes-engine/Pre-built (= 0.79.2)
- - hermes-engine/Pre-built (0.79.2)
- - lottie-ios (3.2.3)
- - RCT-Folly (2024.11.18.00):
- - boost
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - RCT-Folly/Default (= 2024.11.18.00)
- - RCT-Folly/Default (2024.11.18.00):
- - boost
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - RCT-Folly/Fabric (2024.11.18.00):
- - boost
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - RCTDeprecation (0.79.2)
- - RCTRequired (0.79.2)
- - RCTTypeSafety (0.79.2):
- - FBLazyVector (= 0.79.2)
- - RCTRequired (= 0.79.2)
- - React-Core (= 0.79.2)
- - React (0.79.2):
- - React-Core (= 0.79.2)
- - React-Core/DevSupport (= 0.79.2)
- - React-Core/RCTWebSocket (= 0.79.2)
- - React-RCTActionSheet (= 0.79.2)
- - React-RCTAnimation (= 0.79.2)
- - React-RCTBlob (= 0.79.2)
- - React-RCTImage (= 0.79.2)
- - React-RCTLinking (= 0.79.2)
- - React-RCTNetwork (= 0.79.2)
- - React-RCTSettings (= 0.79.2)
- - React-RCTText (= 0.79.2)
- - React-RCTVibration (= 0.79.2)
- - React-callinvoker (0.79.2)
- - React-Core (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default (= 0.79.2)
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/CoreModulesHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/Default (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/DevSupport (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default (= 0.79.2)
- - React-Core/RCTWebSocket (= 0.79.2)
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTActionSheetHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTAnimationHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTBlobHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTImageHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTLinkingHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTNetworkHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTSettingsHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTTextHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTVibrationHeaders (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-Core/RCTWebSocket (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTDeprecation
- - React-Core/Default (= 0.79.2)
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-perflogger
- - React-runtimescheduler
- - React-utils
- - SocketRocket (= 0.7.1)
- - Yoga
- - React-CoreModules (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - RCT-Folly (= 2024.11.18.00)
- - RCTTypeSafety (= 0.79.2)
- - React-Core/CoreModulesHeaders (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-jsinspector
- - React-jsinspectortracing
- - React-NativeModulesApple
- - React-RCTBlob
- - React-RCTFBReactNativeSpec
- - React-RCTImage (= 0.79.2)
- - ReactCommon
- - SocketRocket (= 0.7.1)
- - React-cxxreact (0.79.2):
- - boost
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-callinvoker (= 0.79.2)
- - React-debug (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-jsinspector
- - React-jsinspectortracing
- - React-logger (= 0.79.2)
- - React-perflogger (= 0.79.2)
- - React-runtimeexecutor (= 0.79.2)
- - React-timing (= 0.79.2)
- - React-debug (0.79.2)
- - React-defaultsnativemodule (0.79.2):
- - hermes-engine
- - RCT-Folly
- - React-domnativemodule
- - React-featureflagsnativemodule
- - React-hermes
- - React-idlecallbacksnativemodule
- - React-jsi
- - React-jsiexecutor
- - React-microtasksnativemodule
- - React-RCTFBReactNativeSpec
- - React-domnativemodule (0.79.2):
- - hermes-engine
- - RCT-Folly
- - React-Fabric
- - React-FabricComponents
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-RCTFBReactNativeSpec
- - ReactCommon/turbomodule/core
- - Yoga
- - React-Fabric (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric/animations (= 0.79.2)
- - React-Fabric/attributedstring (= 0.79.2)
- - React-Fabric/componentregistry (= 0.79.2)
- - React-Fabric/componentregistrynative (= 0.79.2)
- - React-Fabric/components (= 0.79.2)
- - React-Fabric/consistency (= 0.79.2)
- - React-Fabric/core (= 0.79.2)
- - React-Fabric/dom (= 0.79.2)
- - React-Fabric/imagemanager (= 0.79.2)
- - React-Fabric/leakchecker (= 0.79.2)
- - React-Fabric/mounting (= 0.79.2)
- - React-Fabric/observers (= 0.79.2)
- - React-Fabric/scheduler (= 0.79.2)
- - React-Fabric/telemetry (= 0.79.2)
- - React-Fabric/templateprocessor (= 0.79.2)
- - React-Fabric/uimanager (= 0.79.2)
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/animations (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/attributedstring (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/componentregistry (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/componentregistrynative (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/components (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric/components/legacyviewmanagerinterop (= 0.79.2)
- - React-Fabric/components/root (= 0.79.2)
- - React-Fabric/components/scrollview (= 0.79.2)
- - React-Fabric/components/view (= 0.79.2)
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/components/legacyviewmanagerinterop (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/components/root (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/components/scrollview (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/components/view (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-renderercss
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-Fabric/consistency (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/core (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/dom (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/imagemanager (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/leakchecker (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/mounting (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/observers (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric/observers/events (= 0.79.2)
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/observers/events (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/scheduler (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric/observers/events
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-performancetimeline
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/telemetry (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/templateprocessor (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/uimanager (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric/uimanager/consistency (= 0.79.2)
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererconsistency
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-Fabric/uimanager/consistency (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererconsistency
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - React-FabricComponents (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-FabricComponents/components (= 0.79.2)
- - React-FabricComponents/textlayoutmanager (= 0.79.2)
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-FabricComponents/components/inputaccessory (= 0.79.2)
- - React-FabricComponents/components/iostextinput (= 0.79.2)
- - React-FabricComponents/components/modal (= 0.79.2)
- - React-FabricComponents/components/rncore (= 0.79.2)
- - React-FabricComponents/components/safeareaview (= 0.79.2)
- - React-FabricComponents/components/scrollview (= 0.79.2)
- - React-FabricComponents/components/text (= 0.79.2)
- - React-FabricComponents/components/textinput (= 0.79.2)
- - React-FabricComponents/components/unimplementedview (= 0.79.2)
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/inputaccessory (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/iostextinput (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/modal (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/rncore (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/safeareaview (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/scrollview (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/text (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/textinput (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/components/unimplementedview (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricComponents/textlayoutmanager (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-cxxreact
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-logger
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - ReactCommon/turbomodule/core
- - Yoga
- - React-FabricImage (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - RCTRequired (= 0.79.2)
- - RCTTypeSafety (= 0.79.2)
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-jsiexecutor (= 0.79.2)
- - React-logger
- - React-rendererdebug
- - React-utils
- - ReactCommon
- - Yoga
- - React-featureflags (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - React-featureflagsnativemodule (0.79.2):
- - hermes-engine
- - RCT-Folly
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-RCTFBReactNativeSpec
- - ReactCommon/turbomodule/core
- - React-graphics (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-utils
- - React-hermes (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-cxxreact (= 0.79.2)
- - React-jsi
- - React-jsiexecutor (= 0.79.2)
- - React-jsinspector
- - React-jsinspectortracing
- - React-perflogger (= 0.79.2)
- - React-runtimeexecutor
- - React-idlecallbacksnativemodule (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-RCTFBReactNativeSpec
- - React-runtimescheduler
- - ReactCommon/turbomodule/core
- - React-ImageManager (0.79.2):
- - glog
- - RCT-Folly/Fabric
- - React-Core/Default
- - React-debug
- - React-Fabric
- - React-graphics
- - React-rendererdebug
- - React-utils
- - React-jserrorhandler (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-jsi
- - ReactCommon/turbomodule/bridging
- - React-jsi (0.79.2):
- - boost
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-jsiexecutor (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-cxxreact (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-jsinspector
- - React-jsinspectortracing
- - React-perflogger (= 0.79.2)
- - React-jsinspector (0.79.2):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly
- - React-featureflags
- - React-jsi
- - React-jsinspectortracing
- - React-perflogger (= 0.79.2)
- - React-runtimeexecutor (= 0.79.2)
- - React-jsinspectortracing (0.79.2):
- - RCT-Folly
- - React-oscompat
- - React-jsitooling (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - RCT-Folly (= 2024.11.18.00)
- - React-cxxreact (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-jsinspector
- - React-jsinspectortracing
- - React-jsitracing (0.79.2):
- - React-jsi
- - React-logger (0.79.2):
- - glog
- - React-Mapbuffer (0.79.2):
- - glog
- - React-debug
- - React-microtasksnativemodule (0.79.2):
- - hermes-engine
- - RCT-Folly
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-RCTFBReactNativeSpec
- - ReactCommon/turbomodule/core
- - react-native-background-actions (4.0.1):
- - React-Core
- - react-native-cookies (6.2.1):
- - React-Core
- - react-native-lottie-splash-screen (1.1.2):
- - React
- - react-native-mmkv (3.2.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-pager-view (6.7.1):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-saf-x (2.2.3):
- - React-Core
- - react-native-safe-area-context (5.4.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - react-native-safe-area-context/common (= 5.4.0)
- - react-native-safe-area-context/fabric (= 5.4.0)
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-safe-area-context/common (5.4.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-safe-area-context/fabric (5.4.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - react-native-safe-area-context/common
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-slider (4.5.6):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - react-native-slider/common (= 4.5.6)
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-slider/common (4.5.6):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-vector-icons (11.0.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-webview (13.13.5):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - React-NativeModulesApple (0.79.2):
- - glog
- - hermes-engine
- - React-callinvoker
- - React-Core
- - React-cxxreact
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsinspector
- - React-runtimeexecutor
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - React-oscompat (0.79.2)
- - React-perflogger (0.79.2):
- - DoubleConversion
- - RCT-Folly (= 2024.11.18.00)
- - React-performancetimeline (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - React-cxxreact
- - React-featureflags
- - React-jsinspectortracing
- - React-perflogger
- - React-timing
- - React-RCTActionSheet (0.79.2):
- - React-Core/RCTActionSheetHeaders (= 0.79.2)
- - React-RCTAnimation (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - RCTTypeSafety
- - React-Core/RCTAnimationHeaders
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - ReactCommon
- - React-RCTAppDelegate (0.79.2):
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-CoreModules
- - React-debug
- - React-defaultsnativemodule
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsitooling
- - React-NativeModulesApple
- - React-RCTFabric
- - React-RCTFBReactNativeSpec
- - React-RCTImage
- - React-RCTNetwork
- - React-RCTRuntime
- - React-rendererdebug
- - React-RuntimeApple
- - React-RuntimeCore
- - React-runtimescheduler
- - React-utils
- - ReactCommon
- - React-RCTBlob (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-Core/RCTBlobHeaders
- - React-Core/RCTWebSocket
- - React-jsi
- - React-jsinspector
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - React-RCTNetwork
- - ReactCommon
- - React-RCTFabric (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-Core
- - React-debug
- - React-Fabric
- - React-FabricComponents
- - React-FabricImage
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-jsinspector
- - React-jsinspectortracing
- - React-performancetimeline
- - React-RCTAnimation
- - React-RCTImage
- - React-RCTText
- - React-rendererconsistency
- - React-renderercss
- - React-rendererdebug
- - React-runtimescheduler
- - React-utils
- - Yoga
- - React-RCTFBReactNativeSpec (0.79.2):
- - hermes-engine
- - RCT-Folly
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-NativeModulesApple
- - ReactCommon
- - React-RCTImage (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - RCTTypeSafety
- - React-Core/RCTImageHeaders
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - React-RCTNetwork
- - ReactCommon
- - React-RCTLinking (0.79.2):
- - React-Core/RCTLinkingHeaders (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - ReactCommon
- - ReactCommon/turbomodule/core (= 0.79.2)
- - React-RCTNetwork (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - RCTTypeSafety
- - React-Core/RCTNetworkHeaders
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - ReactCommon
- - React-RCTRuntime (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-Core
- - React-hermes
- - React-jsi
- - React-jsinspector
- - React-jsinspectortracing
- - React-jsitooling
- - React-RuntimeApple
- - React-RuntimeCore
- - React-RuntimeHermes
- - React-RCTSettings (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - RCTTypeSafety
- - React-Core/RCTSettingsHeaders
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - ReactCommon
- - React-RCTText (0.79.2):
- - React-Core/RCTTextHeaders (= 0.79.2)
- - Yoga
- - React-RCTVibration (0.79.2):
- - RCT-Folly (= 2024.11.18.00)
- - React-Core/RCTVibrationHeaders
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFBReactNativeSpec
- - ReactCommon
- - React-rendererconsistency (0.79.2)
- - React-renderercss (0.79.2):
- - React-debug
- - React-utils
- - React-rendererdebug (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - RCT-Folly (= 2024.11.18.00)
- - React-debug
- - React-rncore (0.79.2)
- - React-RuntimeApple (0.79.2):
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-callinvoker
- - React-Core/Default
- - React-CoreModules
- - React-cxxreact
- - React-featureflags
- - React-jserrorhandler
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-Mapbuffer
- - React-NativeModulesApple
- - React-RCTFabric
- - React-RCTFBReactNativeSpec
- - React-RuntimeCore
- - React-runtimeexecutor
- - React-RuntimeHermes
- - React-runtimescheduler
- - React-utils
- - React-RuntimeCore (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-cxxreact
- - React-Fabric
- - React-featureflags
- - React-hermes
- - React-jserrorhandler
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-jsitooling
- - React-performancetimeline
- - React-runtimeexecutor
- - React-runtimescheduler
- - React-utils
- - React-runtimeexecutor (0.79.2):
- - React-jsi (= 0.79.2)
- - React-RuntimeHermes (0.79.2):
- - hermes-engine
- - RCT-Folly/Fabric (= 2024.11.18.00)
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsinspector
- - React-jsinspectortracing
- - React-jsitooling
- - React-jsitracing
- - React-RuntimeCore
- - React-utils
- - React-runtimescheduler (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-callinvoker
- - React-cxxreact
- - React-debug
- - React-featureflags
- - React-hermes
- - React-jsi
- - React-jsinspectortracing
- - React-performancetimeline
- - React-rendererconsistency
- - React-rendererdebug
- - React-runtimeexecutor
- - React-timing
- - React-utils
- - React-timing (0.79.2)
- - React-utils (0.79.2):
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-debug
- - React-hermes
- - React-jsi (= 0.79.2)
- - ReactAppDependencyProvider (0.79.2):
- - ReactCodegen
- - ReactCodegen (0.79.2):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-FabricImage
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-NativeModulesApple
- - React-RCTAppDelegate
- - React-rendererdebug
- - React-utils
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - ReactCommon (0.79.2):
- - ReactCommon/turbomodule (= 0.79.2)
- - ReactCommon/turbomodule (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-callinvoker (= 0.79.2)
- - React-cxxreact (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-logger (= 0.79.2)
- - React-perflogger (= 0.79.2)
- - ReactCommon/turbomodule/bridging (= 0.79.2)
- - ReactCommon/turbomodule/core (= 0.79.2)
- - ReactCommon/turbomodule/bridging (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-callinvoker (= 0.79.2)
- - React-cxxreact (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-logger (= 0.79.2)
- - React-perflogger (= 0.79.2)
- - ReactCommon/turbomodule/core (0.79.2):
- - DoubleConversion
- - fast_float (= 6.1.4)
- - fmt (= 11.0.2)
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - React-callinvoker (= 0.79.2)
- - React-cxxreact (= 0.79.2)
- - React-debug (= 0.79.2)
- - React-featureflags (= 0.79.2)
- - React-jsi (= 0.79.2)
- - React-logger (= 0.79.2)
- - React-perflogger (= 0.79.2)
- - React-utils (= 0.79.2)
- - ReactNativeFileAccess (3.1.1):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - ZIPFoundation
- - RNDeviceInfo (14.0.4):
- - React-Core
- - RNFlashList (1.7.6):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - RNGestureHandler (2.25.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - RNGoogleSignin (13.2.0):
- - DoubleConversion
- - glog
- - GoogleSignIn (~> 7.1)
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - RNReanimated (3.17.5):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - RNReanimated/reanimated (= 3.17.5)
- - RNReanimated/worklets (= 3.17.5)
- - Yoga
- - RNReanimated/reanimated (3.17.5):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - RNReanimated/reanimated/apple (= 3.17.5)
- - Yoga
- - RNReanimated/reanimated/apple (3.17.5):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - RNReanimated/worklets (3.17.5):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - RNReanimated/worklets/apple (= 3.17.5)
- - Yoga
- - RNReanimated/worklets/apple (3.17.5):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - RNScreens (4.10.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-RCTImage
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - RNScreens/common (= 4.10.0)
- - Yoga
- - RNScreens/common (4.10.0):
- - DoubleConversion
- - glog
- - hermes-engine
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-RCTImage
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - RNZipArchive (7.0.1):
- - React-Core
- - RNZipArchive/Core (= 7.0.1)
- - SSZipArchive (~> 2.5.5)
- - RNZipArchive/Core (7.0.1):
- - React-Core
- - SSZipArchive (~> 2.5.5)
- - SocketRocket (0.7.1)
- - SSZipArchive (2.5.5)
- - Yoga (0.0.0)
- - ZIPFoundation (0.9.19)
-
-DEPENDENCIES:
- - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- - EXApplication (from `../node_modules/expo-application/ios`)
- - EXConstants (from `../node_modules/expo-constants/ios`)
- - EXNotifications (from `../node_modules/expo-notifications/ios`)
- - Expo (from `../node_modules/expo`)
- - "ExpoAdapterGoogleSignIn (from `../node_modules/@react-native-google-signin/google-signin/expo/ios`)"
- - ExpoAsset (from `../node_modules/expo-asset/ios`)
- - ExpoClipboard (from `../node_modules/expo-clipboard/ios`)
- - ExpoDocumentPicker (from `../node_modules/expo-document-picker/ios`)
- - ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
- - ExpoFont (from `../node_modules/expo-font/ios`)
- - ExpoHaptics (from `../node_modules/expo-haptics/ios`)
- - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
- - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
- - ExpoLinking (from `../node_modules/expo-linking/ios`)
- - ExpoLocalization (from `../node_modules/expo-localization/ios`)
- - ExpoModulesCore (from `../node_modules/expo-modules-core`)
- - ExpoSpeech (from `../node_modules/expo-speech/ios`)
- - ExpoSQLite (from `../node_modules/expo-sqlite/ios`)
- - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
- - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`)
- - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- - lottie-ios (from `../node_modules/lottie-ios`)
- - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
- - RCTRequired (from `../node_modules/react-native/Libraries/Required`)
- - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- - React (from `../node_modules/react-native/`)
- - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- - React-Core (from `../node_modules/react-native/`)
- - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
- - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`)
- - React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`)
- - React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`)
- - React-Fabric (from `../node_modules/react-native/ReactCommon`)
- - React-FabricComponents (from `../node_modules/react-native/ReactCommon`)
- - React-FabricImage (from `../node_modules/react-native/ReactCommon`)
- - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`)
- - React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`)
- - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`)
- - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
- - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`)
- - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`)
- - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`)
- - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
- - React-jsinspectortracing (from `../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`)
- - React-jsitooling (from `../node_modules/react-native/ReactCommon/jsitooling`)
- - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`)
- - React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- - react-native-background-actions (from `../node_modules/react-native-background-actions`)
- - "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)"
- - react-native-lottie-splash-screen (from `../node_modules/react-native-lottie-splash-screen`)
- - react-native-mmkv (from `../node_modules/react-native-mmkv`)
- - react-native-pager-view (from `../node_modules/react-native-pager-view`)
- - react-native-saf-x (from `../node_modules/react-native-saf-x`)
- - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- - "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- - "react-native-vector-icons (from `../node_modules/@react-native-vector-icons/common`)"
- - react-native-webview (from `../node_modules/react-native-webview`)
- - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- - React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
- - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`)
- - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
- - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
- - React-RCTFabric (from `../node_modules/react-native/React`)
- - React-RCTFBReactNativeSpec (from `../node_modules/react-native/React`)
- - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
- - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
- - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
- - React-RCTRuntime (from `../node_modules/react-native/React/Runtime`)
- - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- - React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`)
- - React-renderercss (from `../node_modules/react-native/ReactCommon/react/renderer/css`)
- - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`)
- - React-rncore (from `../node_modules/react-native/ReactCommon`)
- - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`)
- - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`)
- - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`)
- - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
- - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`)
- - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
- - ReactAppDependencyProvider (from `build/generated/ios`)
- - ReactCodegen (from `build/generated/ios`)
- - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- - ReactNativeFileAccess (from `../node_modules/react-native-file-access`)
- - RNDeviceInfo (from `../node_modules/react-native-device-info`)
- - "RNFlashList (from `../node_modules/@shopify/flash-list`)"
- - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- - "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)"
- - RNReanimated (from `../node_modules/react-native-reanimated`)
- - RNScreens (from `../node_modules/react-native-screens`)
- - RNZipArchive (from `../node_modules/react-native-zip-archive`)
- - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
-
-SPEC REPOS:
- trunk:
- - AppAuth
- - GoogleSignIn
- - GTMAppAuth
- - GTMSessionFetcher
- - SocketRocket
- - SSZipArchive
- - ZIPFoundation
-
-EXTERNAL SOURCES:
- boost:
- :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
- DoubleConversion:
- :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
- EXApplication:
- :path: "../node_modules/expo-application/ios"
- EXConstants:
- :path: "../node_modules/expo-constants/ios"
- EXNotifications:
- :path: "../node_modules/expo-notifications/ios"
- Expo:
- :path: "../node_modules/expo"
- ExpoAdapterGoogleSignIn:
- :path: "../node_modules/@react-native-google-signin/google-signin/expo/ios"
- ExpoAsset:
- :path: "../node_modules/expo-asset/ios"
- ExpoClipboard:
- :path: "../node_modules/expo-clipboard/ios"
- ExpoDocumentPicker:
- :path: "../node_modules/expo-document-picker/ios"
- ExpoFileSystem:
- :path: "../node_modules/expo-file-system/ios"
- ExpoFont:
- :path: "../node_modules/expo-font/ios"
- ExpoHaptics:
- :path: "../node_modules/expo-haptics/ios"
- ExpoKeepAwake:
- :path: "../node_modules/expo-keep-awake/ios"
- ExpoLinearGradient:
- :path: "../node_modules/expo-linear-gradient/ios"
- ExpoLinking:
- :path: "../node_modules/expo-linking/ios"
- ExpoLocalization:
- :path: "../node_modules/expo-localization/ios"
- ExpoModulesCore:
- :path: "../node_modules/expo-modules-core"
- ExpoSpeech:
- :path: "../node_modules/expo-speech/ios"
- ExpoSQLite:
- :path: "../node_modules/expo-sqlite/ios"
- ExpoWebBrowser:
- :path: "../node_modules/expo-web-browser/ios"
- fast_float:
- :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec"
- FBLazyVector:
- :path: "../node_modules/react-native/Libraries/FBLazyVector"
- fmt:
- :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec"
- glog:
- :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
- hermes-engine:
- :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
- :tag: hermes-2025-03-03-RNv0.79.0-bc17d964d03743424823d7dd1a9f37633459c5c5
- lottie-ios:
- :path: "../node_modules/lottie-ios"
- RCT-Folly:
- :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
- RCTDeprecation:
- :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
- RCTRequired:
- :path: "../node_modules/react-native/Libraries/Required"
- RCTTypeSafety:
- :path: "../node_modules/react-native/Libraries/TypeSafety"
- React:
- :path: "../node_modules/react-native/"
- React-callinvoker:
- :path: "../node_modules/react-native/ReactCommon/callinvoker"
- React-Core:
- :path: "../node_modules/react-native/"
- React-CoreModules:
- :path: "../node_modules/react-native/React/CoreModules"
- React-cxxreact:
- :path: "../node_modules/react-native/ReactCommon/cxxreact"
- React-debug:
- :path: "../node_modules/react-native/ReactCommon/react/debug"
- React-defaultsnativemodule:
- :path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults"
- React-domnativemodule:
- :path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom"
- React-Fabric:
- :path: "../node_modules/react-native/ReactCommon"
- React-FabricComponents:
- :path: "../node_modules/react-native/ReactCommon"
- React-FabricImage:
- :path: "../node_modules/react-native/ReactCommon"
- React-featureflags:
- :path: "../node_modules/react-native/ReactCommon/react/featureflags"
- React-featureflagsnativemodule:
- :path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags"
- React-graphics:
- :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics"
- React-hermes:
- :path: "../node_modules/react-native/ReactCommon/hermes"
- React-idlecallbacksnativemodule:
- :path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks"
- React-ImageManager:
- :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios"
- React-jserrorhandler:
- :path: "../node_modules/react-native/ReactCommon/jserrorhandler"
- React-jsi:
- :path: "../node_modules/react-native/ReactCommon/jsi"
- React-jsiexecutor:
- :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
- React-jsinspector:
- :path: "../node_modules/react-native/ReactCommon/jsinspector-modern"
- React-jsinspectortracing:
- :path: "../node_modules/react-native/ReactCommon/jsinspector-modern/tracing"
- React-jsitooling:
- :path: "../node_modules/react-native/ReactCommon/jsitooling"
- React-jsitracing:
- :path: "../node_modules/react-native/ReactCommon/hermes/executor/"
- React-logger:
- :path: "../node_modules/react-native/ReactCommon/logger"
- React-Mapbuffer:
- :path: "../node_modules/react-native/ReactCommon"
- React-microtasksnativemodule:
- :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
- react-native-background-actions:
- :path: "../node_modules/react-native-background-actions"
- react-native-cookies:
- :path: "../node_modules/@react-native-cookies/cookies"
- react-native-lottie-splash-screen:
- :path: "../node_modules/react-native-lottie-splash-screen"
- react-native-mmkv:
- :path: "../node_modules/react-native-mmkv"
- react-native-pager-view:
- :path: "../node_modules/react-native-pager-view"
- react-native-saf-x:
- :path: "../node_modules/react-native-saf-x"
- react-native-safe-area-context:
- :path: "../node_modules/react-native-safe-area-context"
- react-native-slider:
- :path: "../node_modules/@react-native-community/slider"
- react-native-vector-icons:
- :path: "../node_modules/@react-native-vector-icons/common"
- react-native-webview:
- :path: "../node_modules/react-native-webview"
- React-NativeModulesApple:
- :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
- React-oscompat:
- :path: "../node_modules/react-native/ReactCommon/oscompat"
- React-perflogger:
- :path: "../node_modules/react-native/ReactCommon/reactperflogger"
- React-performancetimeline:
- :path: "../node_modules/react-native/ReactCommon/react/performance/timeline"
- React-RCTActionSheet:
- :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
- React-RCTAnimation:
- :path: "../node_modules/react-native/Libraries/NativeAnimation"
- React-RCTAppDelegate:
- :path: "../node_modules/react-native/Libraries/AppDelegate"
- React-RCTBlob:
- :path: "../node_modules/react-native/Libraries/Blob"
- React-RCTFabric:
- :path: "../node_modules/react-native/React"
- React-RCTFBReactNativeSpec:
- :path: "../node_modules/react-native/React"
- React-RCTImage:
- :path: "../node_modules/react-native/Libraries/Image"
- React-RCTLinking:
- :path: "../node_modules/react-native/Libraries/LinkingIOS"
- React-RCTNetwork:
- :path: "../node_modules/react-native/Libraries/Network"
- React-RCTRuntime:
- :path: "../node_modules/react-native/React/Runtime"
- React-RCTSettings:
- :path: "../node_modules/react-native/Libraries/Settings"
- React-RCTText:
- :path: "../node_modules/react-native/Libraries/Text"
- React-RCTVibration:
- :path: "../node_modules/react-native/Libraries/Vibration"
- React-rendererconsistency:
- :path: "../node_modules/react-native/ReactCommon/react/renderer/consistency"
- React-renderercss:
- :path: "../node_modules/react-native/ReactCommon/react/renderer/css"
- React-rendererdebug:
- :path: "../node_modules/react-native/ReactCommon/react/renderer/debug"
- React-rncore:
- :path: "../node_modules/react-native/ReactCommon"
- React-RuntimeApple:
- :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios"
- React-RuntimeCore:
- :path: "../node_modules/react-native/ReactCommon/react/runtime"
- React-runtimeexecutor:
- :path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
- React-RuntimeHermes:
- :path: "../node_modules/react-native/ReactCommon/react/runtime"
- React-runtimescheduler:
- :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler"
- React-timing:
- :path: "../node_modules/react-native/ReactCommon/react/timing"
- React-utils:
- :path: "../node_modules/react-native/ReactCommon/react/utils"
- ReactAppDependencyProvider:
- :path: build/generated/ios
- ReactCodegen:
- :path: build/generated/ios
- ReactCommon:
- :path: "../node_modules/react-native/ReactCommon"
- ReactNativeFileAccess:
- :path: "../node_modules/react-native-file-access"
- RNDeviceInfo:
- :path: "../node_modules/react-native-device-info"
- RNFlashList:
- :path: "../node_modules/@shopify/flash-list"
- RNGestureHandler:
- :path: "../node_modules/react-native-gesture-handler"
- RNGoogleSignin:
- :path: "../node_modules/@react-native-google-signin/google-signin"
- RNReanimated:
- :path: "../node_modules/react-native-reanimated"
- RNScreens:
- :path: "../node_modules/react-native-screens"
- RNZipArchive:
- :path: "../node_modules/react-native-zip-archive"
- Yoga:
- :path: "../node_modules/react-native/ReactCommon/yoga"
-
-SPEC CHECKSUMS:
- AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
- boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
- DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
- EXApplication: 63b87ca5204304007fac6b1be432f8afa3731c17
- EXConstants: 9f310f44bfedba09087042756802040e464323c0
- EXNotifications: 9123abeeecc540790f2ae0e2f08bcb5e97b4ebaa
- Expo: 769ab5c190382eedebc733af6708bbc9ca5f643b
- ExpoAdapterGoogleSignIn: f5ca2b67502442629d99dbe202b6ae821d77c75d
- ExpoAsset: 3bc9adb7dbbf27ae82c18ca97eb988a3ae7e73b1
- ExpoClipboard: c9771e7aa5f1316183e4a6f40a5755a0b98a6b8d
- ExpoDocumentPicker: 5487ce33530198f4ecf724e650d390b2abe84c15
- ExpoFileSystem: 0f3f466ecd3560f55768cd3f94ac3a17f093b8e6
- ExpoFont: abbb91a911eb961652c2b0a22eef801860425ed6
- ExpoHaptics: 0ff6e0d83cd891178a306e548da1450249d54500
- ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7
- ExpoLinearGradient: a3126d055dd021c1eb38a2cf28052ccbd1a2b3e2
- ExpoLinking: b6a0320bf46ae36807d3dc27b4a1d4474505f99c
- ExpoLocalization: f6c6aaa3bfff77b666bb958bdfeb5c55df21d990
- ExpoModulesCore: 3ac17421302df62928fc99c133cf25bdbcf0b004
- ExpoSpeech: 45bbe42358d8ed3d101cc0222890f93e58cece38
- ExpoSQLite: 06c004fa0c71f62de09f34710534a90958bdeb50
- ExpoWebBrowser: 06fb5f767f53ad53944b068cdd207984cb998712
- fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6
- FBLazyVector: 84b955f7b4da8b895faf5946f73748267347c975
- fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
- glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
- GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
- GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
- GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
- hermes-engine: 314be5250afa5692b57b4dd1705959e1973a8ebe
- lottie-ios: c058aeafa76daa4cf64d773554bccc8385d0150e
- RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82
- RCTDeprecation: 83ffb90c23ee5cea353bd32008a7bca100908f8c
- RCTRequired: eb7c0aba998009f47a540bec9e9d69a54f68136e
- RCTTypeSafety: 659ae318c09de0477fd27bbc9e140071c7ea5c93
- React: c2d3aa44c49bb34e4dfd49d3ee92da5ebacc1c1c
- React-callinvoker: 1bdfb7549b5af266d85757193b5069f60659ef9d
- React-Core: 10597593fdbae06f0089881e025a172e51d4a769
- React-CoreModules: 6907b255529dd46895cf687daa67b24484a612c2
- React-cxxreact: a9f5b8180d6955bc3f6a3fcd657c4d9b4d95c1f6
- React-debug: e74e76912b91e08d580c481c34881899ccf63da9
- React-defaultsnativemodule: 11f6ee2cf69bf3af9d0f28a6253def33d21b5266
- React-domnativemodule: f940bbc4fa9e134190acbf3a4a9f95621b5a8f51
- React-Fabric: 6f5c357bf3a42ff11f8844ad3fc7a1eb04f4b9de
- React-FabricComponents: 10e0c0209822ac9e69412913a8af1ca33573379b
- React-FabricImage: f582e764072dfa4715ae8c42979a5bace9cbcc12
- React-featureflags: d5facceff8f8f6de430e0acecf4979a9a0839ba9
- React-featureflagsnativemodule: a7dd141f1ef4b7c1331af0035689fbc742a49ff4
- React-graphics: 36ae3407172c1c77cea29265d2b12b90aaef6aa0
- React-hermes: 9116d4e6d07abeb519a2852672de087f44da8f12
- React-idlecallbacksnativemodule: ae7f5ffc6cf2d2058b007b78248e5b08172ad5c3
- React-ImageManager: 9daee0dc99ad6a001d4b9e691fbf37107e2b7b54
- React-jserrorhandler: 1e6211581071edaf4ecd5303147328120c73f4dc
- React-jsi: 753ba30c902f3a41fa7f956aca8eea3317a44ee6
- React-jsiexecutor: 47520714aa7d9589c51c0f3713dfbfca4895d4f9
- React-jsinspector: cfd27107f6d6f1076a57d88c932401251560fe5f
- React-jsinspectortracing: 76a7d791f3c0c09a0d2bf6f46dfb0e79a4fcc0ac
- React-jsitooling: 995e826570dd58f802251490486ebd3244a037ab
- React-jsitracing: 094ae3d8c123cea67b50211c945b7c0443d3e97b
- React-logger: 8edfcedc100544791cd82692ca5a574240a16219
- React-Mapbuffer: c3f4b608e4a59dd2f6a416ef4d47a14400194468
- React-microtasksnativemodule: 054f34e9b82f02bd40f09cebd4083828b5b2beb6
- react-native-background-actions: 48e6bad9e2a47e3b04858634c5a05ea11062f680
- react-native-cookies: d648ab7025833b977c0b19e142503034f5f29411
- react-native-lottie-splash-screen: 2d84b1c81c176981d3e5e17df14da5a99a2f5082
- react-native-mmkv: d3cc73d2554fafa20dc5b86386359034d1faf8ff
- react-native-pager-view: f238ed7fb53458bd03366944a33686f067c83e9a
- react-native-saf-x: 684113757246eb8f86d2bae21d965652f78f8eeb
- react-native-safe-area-context: 562163222d999b79a51577eda2ea8ad2c32b4d06
- react-native-slider: 78ccabe016aef7418b1a846b31115b4165c4dde6
- react-native-vector-icons: 9bc211082d39578babf726855c2f7f01ae3e9834
- react-native-webview: 520bcb79c3f2af91e157cdd695732a34ab5f25c8
- React-NativeModulesApple: 2c4377e139522c3d73f5df582e4f051a838ff25e
- React-oscompat: ef5df1c734f19b8003e149317d041b8ce1f7d29c
- React-perflogger: 9a151e0b4c933c9205fd648c246506a83f31395d
- React-performancetimeline: 5b0dfc0acba29ea0269ddb34cd6dd59d3b8a1c66
- React-RCTActionSheet: a499b0d6d9793886b67ba3e16046a3fef2cdbbc3
- React-RCTAnimation: cc64adc259aabc3354b73065e2231d796dfce576
- React-RCTAppDelegate: 9d523da768f1c9e84c5f3b7e3624d097dfb0e16b
- React-RCTBlob: e727f53eeefded7e6432eb76bd22b57bc880e5d1
- React-RCTFabric: 58590aa4fdb4ad546c06a7449b486cf6844e991f
- React-RCTFBReactNativeSpec: 9064c63d99e467a3893e328ba3612745c3c3a338
- React-RCTImage: 7159cbdbb18a09d97ba1a611416eced75b3ccb29
- React-RCTLinking: 46293afdb859bccc63e1d3dedc6901a3c04ef360
- React-RCTNetwork: 4a6cd18f5bcd0363657789c64043123a896b1170
- React-RCTRuntime: 5ab904fd749aa52f267ef771d265612582a17880
- React-RCTSettings: 61e361dc85136d1cb0e148b7541993d2ee950ea7
- React-RCTText: abd1e196c3167175e6baef18199c6d9d8ac54b4e
- React-RCTVibration: 490e0dcb01a3fe4a0dfb7bc51ad5856d8b84f343
- React-rendererconsistency: 351fdbc5c1fe4da24243d939094a80f0e149c7a1
- React-renderercss: 3438814bee838ae7840a633ab085ac81699fd5cf
- React-rendererdebug: 0ac2b9419ad6f88444f066d4b476180af311fb1e
- React-rncore: 57ed480649bb678d8bdc386d20fee8bf2b0c307c
- React-RuntimeApple: 8b7a9788f31548298ba1990620fe06b40de65ad7
- React-RuntimeCore: e03d96fbd57ce69fd9bca8c925942194a5126dbc
- React-runtimeexecutor: d60846710facedd1edb70c08b738119b3ee2c6c2
- React-RuntimeHermes: aab794755d9f6efd249b61f3af4417296904e3ba
- React-runtimescheduler: c3cd124fa5db7c37f601ee49ca0d97019acd8788
- React-timing: a90f4654cbda9c628614f9bee68967f1768bd6a5
- React-utils: a612d50555b6f0f90c74b7d79954019ad47f5de6
- ReactAppDependencyProvider: 04d5eb15eb46be6720e17a4a7fa92940a776e584
- ReactCodegen: d366308232580af80966d4dac79123c899fa2d37
- ReactCommon: 76d2dc87136d0a667678668b86f0fca0c16fdeb0
- ReactNativeFileAccess: 2eddebbd53df4aba596debebe3c0a5893e1996bd
- RNDeviceInfo: d863506092aef7e7af3a1c350c913d867d795047
- RNFlashList: 7ad51f0d0d51a3b7b1d1bb07947b927cb352afc4
- RNGestureHandler: ebef699ea17e7c0006c1074e1e423ead60ce0121
- RNGoogleSignin: f3168a11bf7c59eaa34a1b466e2b372f1e5ca95a
- RNReanimated: 2313402fe27fecb7237619e9c6fcee3177f08a65
- RNScreens: 5621e3ad5a329fbd16de683344ac5af4192b40d3
- RNZipArchive: f4a5af907d1581e995c879e34799be35c6bc3e21
- SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
- SSZipArchive: c69881e8ac5521f0e622291387add5f60f30f3c4
- Yoga: c758bfb934100bb4bf9cbaccb52557cee35e8bdf
- ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
-
-PODFILE CHECKSUM: 720aa79d2a01f2036c463649ebeda6adc89b71e0
-
-COCOAPODS: 1.15.2
diff --git a/ios/loading.json b/ios/loading.json
deleted file mode 100644
index 906dbc98d..000000000
--- a/ios/loading.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{"v":"5.9.0","fr":90,"ip":0,"op":100,"w":500,"h":500,"nm":"Untitled file","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[0]},{"t":30,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[262.75,248.531,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.281,0.906],[0,0],[-0.188,0.625],[0.188,0.281]],"o":[[0,0],[0,0],[0.156,-0.531],[0,0],[0.188,-0.625],[-0.531,-0.5]],"v":[[34.531,-109.562],[34.938,-103.844],[35.594,-105.625],[36.875,-106.875],[37.688,-108.219],[37.5,-109.344]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.453,99.743],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42,"s":[0]},{"t":43,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[21,65],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25,-109.5],"ix":2},"a":{"a":0,"k":[0,-32.5],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":42,"s":[100,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[100,100]},{"t":71,"s":[100,101.54]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[101,18.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.25,-35.625],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[0,100]},{"t":52,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[122.25,18.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.625,-75.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":45,"s":[0,100]},{"t":50,"s":[93.456,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[26.625,-68.125],"ix":2},"a":{"a":0,"k":[26.625,-68.125],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-122,-60],"ix":2},"a":{"a":0,"k":[-32,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":32,"s":[0,47.945]},{"t":38,"s":[119.685,47.945]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-95],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[0,47]},{"t":35,"s":[87.222,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,9.5],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":36,"s":[0,47]},{"t":41,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 3","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-26],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":34,"s":[0,47]},{"t":39,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 4","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,62],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-56.8,50.5],"ix":2},"a":{"a":0,"k":[0,-31],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[100,0]},{"t":49,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k"
-:0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[50.8,15],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-98.2,44],"ix":2},"a":{"a":0,"k":[-25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":40,"s":[0,100]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.6,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-97.9,98],"ix":2},"a":{"a":0,"k":[-16,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[0,99.889]},{"t":49,"s":[100,99.889]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,80.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-106.75,36.525],"ix":2},"a":{"a":0,"k":[0,-40.375],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":39,"s":[100,0]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":-0.026,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-81.5,76.875],"ix":2},"a":{"a":0,"k":[-81.5,76.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19.5,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-30.125,3],"ix":2},"a":{"a":0,"k":[0,-13],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19,26.779],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[81.562,3],"ix":2},"a":{"a":0,"k":[0,-14],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[131,18.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.5,-5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":50,"s":[0,100]},{"t":56,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[25.562,8.062],"ix":2},"a":{"a":0,"k":[25.562,8.062],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[65.5,111],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[-13.5,66.75],"to":[0,0],"ti":[0,0]},{"t":63,"s":[-46,120]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":57,"s":[100,100]},{"t":63,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":57,"s":[0]},{"t":63,"s":[90]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.438,0.188],[0,0],[0,0],[41.507,0],[0,0],[-6.5,54.25],[0,0],[0,0]],"o":[[-9.438,-0.188],[0,0],[0,0],[-0.25,0],[0,0],[0.328,-2.742],[0,0],[0,0]],"v":[[14,17.438],[-3.313,17.5],[-3.5,50.875],[-45,95.5],[-38.5,116.75],[14.25,61.25],[14.5,22.75],[14.656,21.688]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-13.318,67.059],"ix":2},"a":{"a":0,"k":[-13.318,67.059],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[75,104.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[62.75,62.5],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63,"s":[60,96],"to":[0,0],"ti":[0,0]},{"t":68,"s":[98,74]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[100,100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":63,"s":[100,62.5]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":64,"s":[110,50]},{"t":68,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":63,"s":[0]},{"t":68,"s":[-160]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13,0],[0,0],[-3.938,11.938],[0,0],[0,0],[0,0],[6.5,0],[0,0],[0.31,6.262],[0,0],[9.062,0.125]],"o":[[0,0],[0,0],[13,0],[0,0],[4.009,-12.155],[0,0],[0,0],[0,0],[-6.5,0],[0,0],[-0.312,-6.312],[0,0],[-9.062,-0.125]],"v":[[31.062,16.938],[30.812,95.938],[44.062,109.938],[73.688,109.938],[90.312,97.188],[95.812,74.438],[79.062,69.688],[74.562,83.938],[66.75,90.625],[56.062,90.938],[50.562,84.938],[50.312,21.312],[50.125,17]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[63.312,63.43],"ix":2},"a":{"a":0,"k":[63.312,63.43],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[0,0,100]},{"t":30,"s":[340,340,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 3","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847100019455,0.890200018883,0.905900001526,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":5,"s":[0,0,100]},{"t":25,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.070600003004,0.431400001049,0.505900025368,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ellipse 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"t":20,"s":[500,500,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 4","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074500001967,0.172499999404,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/jest.config.js b/jest.config.js
index 5268de455..4b993cd48 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -6,7 +6,7 @@ const baseModuleNameMapper = {
'^@hooks$': '/src/hooks/index',
'^@hooks/(.*)$': '/src/hooks/$1',
'^@screens/(.*)$': '/src/screens/$1',
- '^@strings/(.*)$': '/strings/$1',
+ '^@i18n/(.*)$': '/src/i18n/$1',
'^@theme/(.*)$': '/src/theme/$1',
'^@utils/(.*)$': '/src/utils/$1',
'^@plugins/(.*)$': '/src/plugins/$1',
@@ -16,9 +16,13 @@ const baseModuleNameMapper = {
'^@api/(.*)$': '/src/api/$1',
'^@type/(.*)$': '/src/type/$1',
'^@specs/(.*)$': '/specs/$1',
- '^@test-utils$': '/__tests-modules__/test-utils',
+ '^@modules/nitro-epub$': '/modules/nitro-epub/src/index',
+ '^@modules/nitro-tts$': '/modules/nitro-tts/src/index',
+ '^@modules/(.*)$': '/modules/$1',
+ '^@test-utils$': '/test/test-utils',
+ '^@env$': '/src/generated/build-info',
// Mock static assets
- '\\.(jpg|jpeg|png|gif|webp|svg)$': '/__mocks__/fileMock.js',
+ '\\.(jpg|jpeg|png|gif|webp|svg)$': '/test/mocks/fileMock.js',
};
const baseTransform = {
@@ -31,7 +35,7 @@ const baseTransformIgnorePatterns = [
];
module.exports = {
- moduleDirectories: ['node_modules', '__tests-modules__'],
+ moduleDirectories: ['node_modules'],
projects: [
// --- Project 1: Database / pure logic tests (node environment) ---
{
@@ -65,8 +69,8 @@ module.exports = {
transform: baseTransform,
transformIgnorePatterns: baseTransformIgnorePatterns,
moduleNameMapper: baseModuleNameMapper,
- setupFiles: ['/__mocks__/index.js'],
- setupFilesAfterEnv: ['/__tests__/jest.setup.ts'],
+ setupFiles: ['/test/mocks/index.js'],
+ setupFilesAfterEnv: ['/test/setup/jest.ts'],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/database/queries/**/__tests__/**',
diff --git a/metro.config.js b/metro.config.js
index 07147c371..2987f0660 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -1,55 +1,64 @@
-const { getDefaultConfig } = require('expo/metro-config');
-const { mergeConfig } = require('@react-native/metro-config');
-
-/**
- * Metro configuration
- * https://reactnative.dev/docs/metro
- *
- * @type {import('@react-native/metro-config').MetroConfig}
- */
+const { withRozenite } = require('@rozenite/metro');
+const { withRozeniteExpoAtlasPlugin } = require('@rozenite/expo-atlas-plugin');
-const path = require('path');
+const { getDefaultConfig } = require('expo/metro-config');
const fs = require('fs');
-const defaultConfig = getDefaultConfig(__dirname);
-
-const map = {
- '.ico': 'image/x-icon',
- '.html': 'text/html',
- '.js': 'text/javascript',
- '.json': 'application/json',
- '.css': 'text/css',
- '.png': 'image/png',
- '.jpg': 'image/jpeg',
+const path = require('path');
+
+const config = getDefaultConfig(__dirname);
+const readerAssetsRoot = path.resolve(__dirname, 'assets', 'reader');
+const readerAssetContentTypes = {
+ '.css': 'text/css; charset=utf-8',
+ '.js': 'text/javascript; charset=utf-8',
+ '.ttf': 'font/ttf',
};
-const customConfig = {
- resolver: {
- unstable_enableSymlinks: true,
- sourceExts: [...defaultConfig.resolver.sourceExts, 'sql'],
- },
- server: {
- port: 8081,
- enhanceMiddleware: (metroMiddleware, metroServer) => {
- return (request, res, next) => {
- const filePath = path.join(
- __dirname,
- 'android/app/src/main',
- request._parsedUrl.path || '',
- );
- const ext = path.parse(filePath).ext;
- if (fs.existsSync(filePath)) {
- try {
- const data = fs.readFileSync(filePath);
- res.setHeader('Content-type', map[ext] || 'text/plain');
- res.end(data);
- } catch (err) {
- res.statusCode = 500;
- res.end(`Error getting the file: ${err}.`);
- }
- } else {
- return metroMiddleware(request, res, next);
- }
- };
- },
- },
+
+config.resolver.sourceExts.push('sql');
+
+config.server.enhanceMiddleware = metroMiddleware => {
+ return (request, response, next) => {
+ let pathname;
+
+ try {
+ pathname = decodeURIComponent(
+ new URL(request.url, 'http://localhost').pathname,
+ );
+ } catch {
+ return metroMiddleware(request, response, next);
+ }
+
+ if (!pathname.startsWith('/assets/')) {
+ return metroMiddleware(request, response, next);
+ }
+
+ const assetPath = path.resolve(
+ readerAssetsRoot,
+ pathname.slice('/assets/'.length),
+ );
+ const isReaderAsset =
+ assetPath.startsWith(`${readerAssetsRoot}${path.sep}`) &&
+ fs.existsSync(assetPath) &&
+ fs.statSync(assetPath).isFile();
+
+ if (!isReaderAsset) {
+ return metroMiddleware(request, response, next);
+ }
+
+ response.setHeader(
+ 'Content-Type',
+ readerAssetContentTypes[path.extname(assetPath)] ||
+ 'application/octet-stream',
+ );
+
+ if (request.method === 'HEAD') {
+ return response.end();
+ }
+
+ fs.createReadStream(assetPath).pipe(response);
+ };
};
-module.exports = mergeConfig(defaultConfig, customConfig);
+
+module.exports = withRozenite(config, {
+ enabled: process.env.WITH_ROZENITE === 'true',
+ enhanceMetroConfig: metroConfig => withRozeniteExpoAtlasPlugin(metroConfig),
+});
diff --git a/modules/native-background-tasks/android/build.gradle b/modules/native-background-tasks/android/build.gradle
new file mode 100644
index 000000000..6a0a03991
--- /dev/null
+++ b/modules/native-background-tasks/android/build.gradle
@@ -0,0 +1,28 @@
+plugins {
+ id 'com.android.library'
+ id 'expo-module-gradle-plugin'
+ id 'org.jetbrains.kotlin.kapt'
+}
+
+group = 'expo.modules.nativebackgroundtasks'
+version = '0.1.0'
+
+android {
+ namespace "expo.modules.nativebackgroundtasks"
+ defaultConfig {
+ versionCode 1
+ versionName "0.1.0"
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ implementation 'com.facebook.react:react-android'
+ implementation 'androidx.room:room-runtime:2.7.0'
+ implementation 'androidx.room:room-ktx:2.7.0'
+ implementation 'androidx.work:work-runtime-ktx:2.10.0'
+ kapt 'androidx.room:room-compiler:2.7.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
+}
\ No newline at end of file
diff --git a/modules/native-background-tasks/android/src/main/AndroidManifest.xml b/modules/native-background-tasks/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..cc38dfd3b
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduleWorker.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduleWorker.kt
new file mode 100644
index 000000000..c29471058
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduleWorker.kt
@@ -0,0 +1,30 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+
+class AutomaticBackupScheduleWorker(
+ appContext: Context,
+ workerParams: WorkerParameters,
+) : CoroutineWorker(appContext, workerParams) {
+ override suspend fun doWork(): Result {
+ val title = inputData.getString(AutomaticBackupScheduler.TITLE)
+ ?: AutomaticBackupScheduler.DEFAULT_TITLE
+ val description = inputData.getString(AutomaticBackupScheduler.DESCRIPTION)
+ ?: AutomaticBackupScheduler.DEFAULT_DESCRIPTION
+ val directoryUri = inputData.getString(AutomaticBackupScheduler.DIRECTORY_URI)
+
+ return try {
+ BackgroundTaskScheduler.enqueueAutomaticBackup(
+ applicationContext,
+ title,
+ description,
+ directoryUri,
+ )
+ Result.success()
+ } catch (_: Exception) {
+ Result.retry()
+ }
+ }
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduler.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduler.kt
new file mode 100644
index 000000000..24eb68aea
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduler.kt
@@ -0,0 +1,56 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import androidx.work.Data
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import java.util.concurrent.TimeUnit
+
+object AutomaticBackupScheduler {
+ const val TITLE = "title"
+ const val DESCRIPTION = "description"
+ const val DIRECTORY_URI = "directoryUri"
+ const val DEFAULT_TITLE = "Local Backup"
+ const val DEFAULT_DESCRIPTION = "Preparing"
+
+ private const val WORK_NAME = "lnreader-automatic-backup"
+ private val allowedIntervals = setOf(6L, 12L, 24L, 48L, 168L)
+
+ fun schedule(
+ context: Context,
+ intervalHours: Long,
+ title: String,
+ description: String,
+ directoryUri: String?,
+ ) {
+ require(intervalHours in allowedIntervals) {
+ "Unsupported automatic backup interval: $intervalHours"
+ }
+
+ val request =
+ PeriodicWorkRequestBuilder(
+ intervalHours,
+ TimeUnit.HOURS,
+ )
+ .setInitialDelay(intervalHours, TimeUnit.HOURS)
+ .setInputData(
+ Data.Builder()
+ .putString(TITLE, title)
+ .putString(DESCRIPTION, description)
+ .putString(DIRECTORY_URI, directoryUri)
+ .build(),
+ )
+ .build()
+
+ WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+ WORK_NAME,
+ ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ request,
+ )
+ }
+
+ fun cancel(context: Context) {
+ WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
+ }
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDao.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDao.kt
new file mode 100644
index 000000000..bda77abcb
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDao.kt
@@ -0,0 +1,59 @@
+package expo.modules.nativebackgroundtasks
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+@Dao
+interface BackgroundTaskDao {
+ @Insert(onConflict = OnConflictStrategy.ABORT)
+ suspend fun insert(task: BackgroundTaskEntity)
+
+ @Query("SELECT * FROM background_tasks ORDER BY createdAt ASC")
+ suspend fun getAll(): List
+
+ @Query("SELECT * FROM background_tasks WHERE id = :id LIMIT 1")
+ suspend fun get(id: String): BackgroundTaskEntity?
+
+ @Query("SELECT * FROM background_tasks WHERE type = :type AND state IN ('queued', 'running', 'paused') LIMIT 1")
+ suspend fun getActiveByType(type: String): BackgroundTaskEntity?
+
+ @Query("UPDATE background_tasks SET state = :state, updatedAt = :updatedAt WHERE id = :id")
+ suspend fun updateState(id: String, state: String, updatedAt: Long)
+
+ @Query("UPDATE background_tasks SET state = :state, updatedAt = :updatedAt WHERE id = :id AND state = 'running'")
+ suspend fun finishRunning(id: String, state: String, updatedAt: Long)
+
+ @Query(
+ """
+ UPDATE background_tasks
+ SET state = :state, attempt = attempt + 1, updatedAt = :updatedAt
+ WHERE id = :id
+ AND state = 'queued'
+ AND (
+ type != 'DOWNLOAD_CHAPTER'
+ OR (
+ SELECT COUNT(*)
+ FROM background_tasks
+ WHERE type = 'DOWNLOAD_CHAPTER' AND state = 'running'
+ ) < :maxConcurrentDownloads
+ )
+ """,
+ )
+ suspend fun tryMarkRunning(
+ id: String,
+ state: String,
+ updatedAt: Long,
+ maxConcurrentDownloads: Int,
+ ): Int
+
+ @Query("UPDATE background_tasks SET progress = :progress, progressText = :progressText, updatedAt = :updatedAt WHERE id = :id")
+ suspend fun updateProgress(id: String, progress: Double?, progressText: String?, updatedAt: Long)
+
+ @Query("UPDATE background_tasks SET checkpoint = :checkpoint, updatedAt = :updatedAt WHERE id = :id")
+ suspend fun updateCheckpoint(id: String, checkpoint: String?, updatedAt: Long)
+
+ @Query("UPDATE background_tasks SET workId = :workId, updatedAt = :updatedAt WHERE id = :id")
+ suspend fun assignWork(id: String, workId: String, updatedAt: Long)
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDatabase.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDatabase.kt
new file mode 100644
index 000000000..006ab0440
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDatabase.kt
@@ -0,0 +1,37 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.migration.Migration
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+@Database(entities = [BackgroundTaskEntity::class], version = 2, exportSchema = false)
+abstract class BackgroundTaskDatabase : RoomDatabase() {
+ abstract fun tasks(): BackgroundTaskDao
+
+ companion object {
+ @Volatile private var instance: BackgroundTaskDatabase? = null
+
+ fun get(context: Context): BackgroundTaskDatabase =
+ instance ?: synchronized(this) {
+ instance ?: Room.databaseBuilder(
+ context.applicationContext,
+ BackgroundTaskDatabase::class.java,
+ "lnreader-background-tasks.db",
+ ).addMigrations(MIGRATION_1_2)
+ .build()
+ .also { instance = it }
+ }
+
+ private val MIGRATION_1_2 = object : Migration(1, 2) {
+ override fun migrate(db: SupportSQLiteDatabase) {
+ db.execSQL(
+ "ALTER TABLE background_tasks ADD COLUMN queueName TEXT NOT NULL " +
+ "DEFAULT 'lnreader-background-task-queue'",
+ )
+ }
+ }
+ }
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskEntity.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskEntity.kt
new file mode 100644
index 000000000..018e70bc0
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskEntity.kt
@@ -0,0 +1,33 @@
+package expo.modules.nativebackgroundtasks
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "background_tasks")
+data class BackgroundTaskEntity(
+ @PrimaryKey val id: String,
+ val type: String,
+ val payload: String,
+ val title: String,
+ val description: String,
+ val queueName: String,
+ val state: String,
+ val progress: Double?,
+ val progressText: String?,
+ val checkpoint: String?,
+ val attempt: Int,
+ val workId: String?,
+ val createdAt: Long,
+ val updatedAt: Long,
+)
+
+object BackgroundTaskState {
+ const val QUEUED = "queued"
+ const val RUNNING = "running"
+ const val PAUSED = "paused"
+ const val SUCCEEDED = "succeeded"
+ const val FAILED = "failed"
+ const val CANCELLED = "cancelled"
+
+ val active = listOf(QUEUED, RUNNING, PAUSED)
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskScheduler.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskScheduler.kt
new file mode 100644
index 000000000..6ef1018e0
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskScheduler.kt
@@ -0,0 +1,143 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import android.net.Uri
+import android.provider.DocumentsContract
+import androidx.work.Data
+import androidx.work.Constraints
+import androidx.work.ExistingWorkPolicy
+import androidx.work.NetworkType
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.await
+import java.util.UUID
+
+object BackgroundTaskScheduler {
+ const val TASK_ID = "taskId"
+
+ suspend fun enqueue(context: Context, taskId: String): UUID {
+ val task = BackgroundTaskDatabase.get(context).tasks().get(taskId)
+ ?: throw IllegalArgumentException("Unknown background task: $taskId")
+ val requestBuilder = OneTimeWorkRequestBuilder()
+ .setInputData(Data.Builder().putString(TASK_ID, taskId).build())
+ .addTag(taskId)
+ if (task.type in NETWORK_TASKS) {
+ requestBuilder.setConstraints(
+ Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(),
+ )
+ }
+ val request = requestBuilder.build()
+ BackgroundTaskDatabase.get(context).tasks()
+ .assignWork(taskId, request.id.toString(), System.currentTimeMillis())
+ WorkManager.getInstance(context).enqueueUniqueWork(
+ task.queueName,
+ ExistingWorkPolicy.APPEND_OR_REPLACE,
+ request,
+ )
+ return request.id
+ }
+
+ suspend fun cancel(context: Context, taskId: String) {
+ WorkManager.getInstance(context).cancelAllWorkByTag(taskId).await()
+ }
+
+ suspend fun enqueueLibraryUpdate(
+ context: Context,
+ title: String,
+ description: String,
+ ): UUID? {
+ val dao = BackgroundTaskDatabase.get(context).tasks()
+ if (dao.getActiveByType(LIBRARY_UPDATE_TASK_TYPE) != null) {
+ return null
+ }
+
+ val now = System.currentTimeMillis()
+ val task = BackgroundTaskEntity(
+ id = UUID.randomUUID().toString(),
+ type = LIBRARY_UPDATE_TASK_TYPE,
+ payload = """{"name":"$LIBRARY_UPDATE_TASK_TYPE"}""",
+ title = title,
+ description = description,
+ queueName = "lnreader-background-task:task:$LIBRARY_UPDATE_TASK_TYPE",
+ state = BackgroundTaskState.QUEUED,
+ progress = null,
+ progressText = null,
+ checkpoint = null,
+ attempt = 0,
+ workId = null,
+ createdAt = now,
+ updatedAt = now,
+ )
+ dao.insert(task)
+ return enqueue(context, task.id)
+ }
+
+ suspend fun enqueueAutomaticBackup(
+ context: Context,
+ title: String,
+ description: String,
+ directoryUri: String?,
+ ): UUID? {
+ val dao = BackgroundTaskDatabase.get(context).tasks()
+ if (dao.getActiveByType(LOCAL_BACKUP_TASK_TYPE) != null) {
+ return null
+ }
+
+ val filename = "lnreader_backup_${System.currentTimeMillis()}.zip"
+ val destination = if (directoryUri != null) {
+ val treeUri = Uri.parse(directoryUri)
+ val documentId = DocumentsContract.getTreeDocumentId(treeUri)
+ val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
+ DocumentsContract.createDocument(
+ context.contentResolver,
+ parentUri,
+ "application/zip",
+ filename,
+ )?.toString() ?: throw IllegalStateException(
+ "Could not create a backup in the selected directory",
+ )
+ } else {
+ val backupDirectory = context.getExternalFilesDir(null)?.resolve("Backups")
+ ?: throw IllegalStateException("External files directory is unavailable")
+ if (!backupDirectory.exists() && !backupDirectory.mkdirs()) {
+ throw IllegalStateException("Could not create automatic backup directory")
+ }
+ backupDirectory.resolve(filename).absolutePath
+ }
+ val escapedDestination = destination
+ .replace("\\", "\\\\")
+ .replace("\"", "\\\"")
+ val now = System.currentTimeMillis()
+ val task = BackgroundTaskEntity(
+ id = UUID.randomUUID().toString(),
+ type = LOCAL_BACKUP_TASK_TYPE,
+ payload = """{"name":"$LOCAL_BACKUP_TASK_TYPE","data":{"destinationUri":"$escapedDestination","automatic":true}}""",
+ title = title,
+ description = description,
+ queueName = "lnreader-background-task:task:$LOCAL_BACKUP_TASK_TYPE",
+ state = BackgroundTaskState.QUEUED,
+ progress = null,
+ progressText = null,
+ checkpoint = null,
+ attempt = 0,
+ workId = null,
+ createdAt = now,
+ updatedAt = now,
+ )
+ dao.insert(task)
+ return enqueue(context, task.id)
+ }
+
+ private val NETWORK_TASKS = setOf(
+ LIBRARY_UPDATE_TASK_TYPE,
+ "DRIVE_BACKUP",
+ "DRIVE_RESTORE",
+ "SELF_HOST_BACKUP",
+ "SELF_HOST_RESTORE",
+ "MIGRATE_NOVEL",
+ "DOWNLOAD_CHAPTER",
+ )
+
+ private const val LIBRARY_UPDATE_TASK_TYPE = "UPDATE_LIBRARY"
+ private const val LOCAL_BACKUP_TASK_TYPE = "LOCAL_BACKUP"
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderHeadlessTaskService.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderHeadlessTaskService.kt
new file mode 100644
index 000000000..a80d88790
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderHeadlessTaskService.kt
@@ -0,0 +1,18 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Intent
+import com.facebook.react.HeadlessJsTaskService
+import com.facebook.react.bridge.Arguments
+import com.facebook.react.jstasks.HeadlessJsTaskConfig
+
+class LNReaderHeadlessTaskService : HeadlessJsTaskService() {
+ override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig? {
+ val extras = intent?.extras ?: return null
+ return HeadlessJsTaskConfig(
+ "LNReaderBackgroundTask",
+ Arguments.fromBundle(extras),
+ 0,
+ true,
+ )
+ }
+}
\ No newline at end of file
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderTaskWorker.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderTaskWorker.kt
new file mode 100644
index 000000000..5f5dd4898
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderTaskWorker.kt
@@ -0,0 +1,109 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ServiceInfo
+import android.os.Build
+import androidx.work.CoroutineWorker
+import androidx.work.ForegroundInfo
+import androidx.work.WorkerParameters
+import kotlinx.coroutines.CancellationException
+
+class LNReaderTaskWorker(
+ appContext: Context,
+ workerParams: WorkerParameters,
+) : CoroutineWorker(appContext, workerParams) {
+ override suspend fun doWork(): Result {
+ val taskId = inputData.getString(BackgroundTaskScheduler.TASK_ID) ?: return Result.failure()
+ val dao = BackgroundTaskDatabase.get(applicationContext).tasks()
+ val task = dao.get(taskId) ?: return Result.failure()
+ if (task.state == BackgroundTaskState.CANCELLED || task.state == BackgroundTaskState.PAUSED) {
+ return Result.success()
+ }
+
+ val claimed = dao.tryMarkRunning(
+ taskId,
+ BackgroundTaskState.RUNNING,
+ System.currentTimeMillis(),
+ MAX_CONCURRENT_DOWNLOADS,
+ )
+ if (claimed == 0) {
+ return if (dao.get(taskId)?.state == BackgroundTaskState.QUEUED) {
+ Result.retry()
+ } else {
+ Result.success()
+ }
+ }
+ val runningTask = dao.get(taskId) ?: return Result.failure()
+ setForeground(createForegroundInfo(runningTask))
+ val execution = TaskExecutionRegistry.register(taskId)
+
+ applicationContext.startService(
+ Intent(applicationContext, LNReaderHeadlessTaskService::class.java).apply {
+ putExtra("taskId", runningTask.id)
+ putExtra("type", runningTask.type)
+ putExtra("payload", runningTask.payload)
+ runningTask.checkpoint?.let { putExtra("checkpoint", it) }
+ },
+ )
+
+ return try {
+ when (val executionResult = execution.await()) {
+ TaskExecutionResult.Success -> {
+ val currentState = dao.get(taskId)?.state
+ if (currentState == BackgroundTaskState.CANCELLED) {
+ TaskNotificationFactory.dismiss(applicationContext, taskId)
+ return Result.success()
+ }
+ if (currentState == BackgroundTaskState.PAUSED) {
+ dao.get(taskId)?.let { TaskNotificationFactory.update(applicationContext, it) }
+ return Result.success()
+ }
+ dao.finishRunning(taskId, BackgroundTaskState.SUCCEEDED, System.currentTimeMillis())
+ dao.get(taskId)?.let {
+ TaskNotificationFactory.postTerminal(applicationContext, it)
+ }
+ Result.success()
+ }
+ is TaskExecutionResult.Failure -> {
+ val currentState = dao.get(taskId)?.state
+ if (currentState == BackgroundTaskState.PAUSED || currentState == BackgroundTaskState.CANCELLED) {
+ return Result.success()
+ }
+ if (executionResult.shouldRetry) {
+ dao.updateState(taskId, BackgroundTaskState.QUEUED, System.currentTimeMillis())
+ Result.retry()
+ } else {
+ dao.updateState(taskId, BackgroundTaskState.FAILED, System.currentTimeMillis())
+ dao.get(taskId)?.let {
+ TaskNotificationFactory.postTerminal(applicationContext, it)
+ }
+ Result.success()
+ }
+ }
+ }
+ } catch (error: CancellationException) {
+ val latestState = dao.get(taskId)?.state
+ if (latestState !in listOf(BackgroundTaskState.PAUSED, BackgroundTaskState.CANCELLED)) {
+ dao.updateState(taskId, BackgroundTaskState.QUEUED, System.currentTimeMillis())
+ }
+ throw error
+ } finally {
+ TaskExecutionRegistry.cancel(taskId)
+ }
+ }
+
+ private fun createForegroundInfo(task: BackgroundTaskEntity): ForegroundInfo {
+ val id = TaskNotificationFactory.notificationId(task.id)
+ val notification = TaskNotificationFactory.build(applicationContext, task)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ ForegroundInfo(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
+ } else {
+ ForegroundInfo(id, notification)
+ }
+ }
+
+ companion object {
+ private const val MAX_CONCURRENT_DOWNLOADS = 3
+ }
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduleWorker.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduleWorker.kt
new file mode 100644
index 000000000..b7f1c216b
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduleWorker.kt
@@ -0,0 +1,28 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+
+class LibraryUpdateScheduleWorker(
+ appContext: Context,
+ workerParams: WorkerParameters,
+) : CoroutineWorker(appContext, workerParams) {
+ override suspend fun doWork(): Result {
+ val title = inputData.getString(LibraryUpdateScheduler.TITLE)
+ ?: LibraryUpdateScheduler.DEFAULT_TITLE
+ val description = inputData.getString(LibraryUpdateScheduler.DESCRIPTION)
+ ?: LibraryUpdateScheduler.DEFAULT_DESCRIPTION
+
+ return try {
+ BackgroundTaskScheduler.enqueueLibraryUpdate(
+ applicationContext,
+ title,
+ description,
+ )
+ Result.success()
+ } catch (_: Exception) {
+ Result.retry()
+ }
+ }
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduler.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduler.kt
new file mode 100644
index 000000000..ed7a8a2cc
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduler.kt
@@ -0,0 +1,60 @@
+package expo.modules.nativebackgroundtasks
+
+import android.content.Context
+import androidx.work.Constraints
+import androidx.work.Data
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.NetworkType
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import java.util.concurrent.TimeUnit
+
+object LibraryUpdateScheduler {
+ const val TITLE = "title"
+ const val DESCRIPTION = "description"
+ const val DEFAULT_TITLE = "Updating Library"
+ const val DEFAULT_DESCRIPTION = "Preparing"
+
+ private const val WORK_NAME = "lnreader-automatic-library-update"
+ private val allowedIntervals = setOf(12L, 24L, 48L, 72L, 168L)
+
+ fun schedule(
+ context: Context,
+ intervalHours: Long,
+ title: String,
+ description: String,
+ ) {
+ require(intervalHours in allowedIntervals) {
+ "Unsupported automatic library update interval: $intervalHours"
+ }
+
+ val request =
+ PeriodicWorkRequestBuilder(
+ intervalHours,
+ TimeUnit.HOURS,
+ )
+ .setInitialDelay(intervalHours, TimeUnit.HOURS)
+ .setInputData(
+ Data.Builder()
+ .putString(TITLE, title)
+ .putString(DESCRIPTION, description)
+ .build(),
+ )
+ .setConstraints(
+ Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build(),
+ )
+ .build()
+
+ WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+ WORK_NAME,
+ ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ request,
+ )
+ }
+
+ fun cancel(context: Context) {
+ WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
+ }
+}
diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/NativeBackgroundTasksModule.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/NativeBackgroundTasksModule.kt
new file mode 100644
index 000000000..45d010f9b
--- /dev/null
+++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/NativeBackgroundTasksModule.kt
@@ -0,0 +1,205 @@
+package expo.modules.nativebackgroundtasks
+
+import com.facebook.react.bridge.Arguments
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.modules.core.DeviceEventManagerModule
+import expo.modules.kotlin.modules.Module
+import expo.modules.kotlin.modules.ModuleDefinition
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import java.lang.ref.WeakReference
+import java.util.UUID
+
+class NativeBackgroundTasksModule : Module() {
+ private val dao by lazy {
+ BackgroundTaskDatabase.get(appContext.reactContext!!).tasks()
+ }
+
+ override fun definition() = ModuleDefinition {
+ Name("NativeBackgroundTasks")
+
+ OnCreate {
+ appContext.reactContext?.let { reactContextRef = WeakReference(it as ReactApplicationContext) }
+ }
+
+ OnDestroy {
+ reactContextRef?.clear()
+ reactContextRef = null
+ }
+
+ AsyncFunction("enqueue") { type: String, payload: String, title: String, description: String, allowsDuplicates: Boolean, queueName: String ->
+ runBlocking(Dispatchers.IO) {
+ if (!allowsDuplicates) {
+ dao.getActiveByType(type)?.let { return@runBlocking it.id }
+ }
+ val now = System.currentTimeMillis()
+ val task = BackgroundTaskEntity(
+ id = UUID.randomUUID().toString(),
+ type = type,
+ payload = payload,
+ title = title,
+ description = description,
+ queueName = queueName,
+ state = BackgroundTaskState.QUEUED,
+ progress = null,
+ progressText = null,
+ checkpoint = null,
+ attempt = 0,
+ workId = null,
+ createdAt = now,
+ updatedAt = now,
+ )
+ dao.insert(task)
+ BackgroundTaskScheduler.enqueue(appContext.reactContext!!, task.id)
+ task.id
+ }
+ }
+
+ AsyncFunction("getTasks") {
+ runBlocking(Dispatchers.IO) {
+ val result = mutableListOf