# PostizMobile React Native (Expo) mobile app to control a self-hosted [Postiz](https://postiz.com) instance from Android. Build is fully local — no expo.dev account or EAS cloud required. --- ## Features | Screen | Description | |--------|-------------| | **Calendar** | Monthly view with color dots per day (indigo = scheduled, green = published, red = error). Tap a day post to copy or edit it. | | **Posts** | Filtered list (All / Queue / Published / Draft / Error) with post counts, sort toggle (newest/oldest, persisted), pull-to-refresh, swipe left to delete, swipe right to reschedule. | | **Compose** | Text editor with per-network character limit, channel picker, date/time picker, gallery image pick + upload, publish now or schedule. Local draft save/restore. | | **Settings** | API key and base URL, connection test, secure storage. 401 auto-redirect to Settings. | | **Notifications** | Local alerts when a post transitions to PUBLISHED or ERROR (polling every 15 min). | **Theme**: forced dark. **Auth**: API key in `expo-secure-store`, never hardcoded. --- ## Prerequisites | Tool | Version | |------|---------| | Node.js | 20 LTS | | pnpm | 10+ | | Java (JDK) | 17–24 (Java 25+ not yet supported by Gradle 8) | | Android SDK | see below | No expo.dev account needed for builds. --- ## Development ### Install dependencies ```bash git clone ssh://gitea@homegit.gyozamancave.fr:2222/billisdead/Postiz-android.git cd Postiz-android pnpm install ``` ### Start dev server (Expo Go) ```bash pnpm --filter @workspace/postiz-mobile run dev ``` Scan the QR code with Expo Go on Android to preview the app live. --- ## Building an APK (local, no EAS) ### First-time setup **1. Java 21 LTS** Gradle 8 requires Java ≤ 24. If the system Java is 25+ (Fedora 44), install Temurin 21 locally: ```bash wget -O /tmp/jdk21.tar.gz \ "https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.7%2B6/OpenJDK21U-jdk_x64_linux_hotspot_21.0.7_6.tar.gz" mkdir -p ~/jdk21 && tar -xzf /tmp/jdk21.tar.gz -C ~/jdk21 --strip-components=1 ``` `build-apk.sh` will use `~/jdk21` automatically if the system Java is ≥ 25. **2. Android SDK** ```bash cd artifacts/postiz-mobile ./install-android-sdk.sh ``` Add to `~/.bashrc` or `~/.zshrc`: ```bash export ANDROID_HOME="$HOME/android-sdk" export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/35.0.0" ``` **2. Signing keystore** The release keystore is stored at `~/.config/postiz-mobile/postiz-mobile.jks` (not in the repo). To export it from EAS (one-time): ```bash cd artifacts/postiz-mobile eas credentials --platform android # → Keystore: Manage everything → Download existing keystore # Note the key alias and passwords shown during export ``` **3. Signing credentials** ```bash cp ~/.config/postiz-mobile/signing.env.example ~/.config/postiz-mobile/signing.env $EDITOR ~/.config/postiz-mobile/signing.env ``` Fill in: ```bash KEYSTORE_PATH="$HOME/.config/postiz-mobile/postiz-mobile.jks" KEYSTORE_ALIAS="" KEYSTORE_STORE_PASSWORD="" KEYSTORE_KEY_PASSWORD="" ``` ### Build ```bash cd artifacts/postiz-mobile ./build-apk.sh # → dist/postiz-mobile-YYYYMMDD-HHMM.apk ./build-apk.sh --aab # → dist/postiz-mobile-YYYYMMDD-HHMM.aab (Play Store) ``` The script runs `expo prebuild`, patches `android/app/build.gradle` for release signing, runs Gradle, copies the artifact to `dist/`, then wipes the credentials from `gradle.properties`. ### Install on device ```bash adb install dist/postiz-mobile-*.apk ``` --- ## How the build works ``` build-apk.sh ├── source ~/.config/postiz-mobile/signing.env ├── expo prebuild --platform android --clean │ └── generates android/ from app.json + plugins ├── python3 patch: injects release signingConfig into build.gradle ├── append MYAPP_UPLOAD_* to gradle.properties ├── ./gradlew assembleRelease (or bundleRelease) ├── wipe signing block from gradle.properties └── copy APK → dist/ ``` The `android/` directory is not committed (gitignored). It is regenerated on each build. --- ## App configuration On first launch, go to **Settings**: 1. **Base URL**: `https://your-postiz-instance/api/public/v1` 2. **API Key**: generated in Postiz → Settings → API Keys 3. Tap **Test Connection**, then **Save Settings** The key is encrypted locally via `expo-secure-store` and never sent to third parties. --- ## Architecture ``` artifacts/postiz-mobile/ ├── app/ │ ├── _layout.tsx # Root layout: providers, fonts, 401 handler │ └── (tabs)/ │ ├── _layout.tsx # Tab bar │ ├── index.tsx # Calendar screen │ ├── posts.tsx # Post list screen │ ├── compose.tsx # Compose screen │ └── settings.tsx # Settings screen ├── components/ │ ├── ChannelChip.tsx # Channel selector chip │ ├── ErrorBoundary.tsx │ ├── PostCard.tsx # Swipe-to-delete / swipe-to-reschedule │ └── StatusBadge.tsx ├── context/ │ └── PostizContext.tsx # axios client + SecureStore + 401 interceptor ├── hooks/ │ ├── useColors.ts │ └── useNotifications.ts # Permission + polling + local notifications ├── lib/ │ └── extractError.ts # Shared axios/fetch error formatter ├── build-apk.sh # Local build script └── install-android-sdk.sh # One-time Android SDK bootstrap ``` ### Key dependencies | Package | Role | |---------|------| | `expo-router` | File-based navigation | | `axios` | Postiz API HTTP client | | `expo-secure-store` | Encrypted key storage | | `react-native-calendars` | Calendar view | | `@react-native-community/datetimepicker` | Date/time picker | | `expo-image-picker` | Gallery access | | `expo-notifications` | Local status notifications | | `@tanstack/react-query` | API cache + refetch | --- ## Postiz API | Method | Endpoint | Usage | |--------|----------|-------| | `GET` | `/integrations` | List channels | | `GET` | `/posts?startDate=&endDate=` | Posts over a date range | | `POST` | `/posts` | Create / schedule a post | | `DELETE` | `/posts/:id` | Delete a post | | `POST` | `/upload` | Upload an image (multipart) | --- ## Troubleshooting **"Not Configured" on all screens** → Settings tab → enter API key and URL → Test Connection. **"Connection failed"** → URL must end with `/api/public/v1` — check Postiz is reachable. **No notifications** → Accept permissions on first launch. Polling runs every 15 min. **Build fails at Gradle** → Make sure `ANDROID_HOME` is set and `./gradlew` is executable (`chmod +x android/gradlew`). **`expo prebuild` fails** → Run `pnpm install` from the repo root first.