diff --git a/.github/workflows/build-aab-android.yml b/.github/workflows/build-android.yml similarity index 69% rename from .github/workflows/build-aab-android.yml rename to .github/workflows/build-android.yml index 72b3e220..e3f76056 100644 --- a/.github/workflows/build-aab-android.yml +++ b/.github/workflows/build-android.yml @@ -1,10 +1,14 @@ -name: Build AAB for Play Store +name: Build Android on: - workflow_dispatch: + push: + branches: + - main jobs: build: + name: Build & Upload to Play Store + if: github.repository == 'PapillonApp/Papillon' runs-on: ubuntu-latest steps: @@ -15,23 +19,44 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20 + cache: npm - name: Setup Java uses: actions/setup-java@v4 with: - distribution: 'temurin' - java-version: '17' + distribution: temurin + java-version: 17 - - name: Install deps - run: npm install --legacy-peer-deps || true + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Install dependencies + run: npm install --legacy-peer-deps + + - name: Cache Expo prebuild + id: prebuild-cache + uses: actions/cache@v4 + with: + path: android/ + key: ${{ runner.os }}-expo-prebuild-${{ hashFiles('app.json', 'package-lock.json') }} + restore-keys: ${{ runner.os }}-expo-prebuild- + + - name: Create secrets.json + run: | + echo '{ + "APP_KEY": "${{ secrets.COUNTLY_APP_KEY }}", + "SALT": "${{ secrets.COUNTLY_SALT }}", + "SERVER_URL": "${{ secrets.COUNTLY_SERVER_URL }}" + }' > secrets.json - name: Prebuild Android + if: steps.prebuild-cache.outputs.cache-hit != 'true' run: npx expo prebuild --platform android --clean - name: Decode keystore run: echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/release.keystore - - name: Calculate Android Version Code + - name: Calculate version code run: | VERSION_CODE=$(node -e " const version = require('./package.json').version; @@ -40,42 +65,32 @@ jobs: console.log(cleanVersion * 100000 + runNumber); ") echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV - echo "Computed VERSION_CODE: $VERSION_CODE" - name: Configure signing run: | sed -i '/signingConfigs {/a\ release {\n storeFile file('\''release.keystore'\'')\n storePassword '\''${{ secrets.KEYSTORE_PASSWORD }}'\''\n keyAlias '\''${{ secrets.KEY_ALIAS }}'\''\n keyPassword '\''${{ secrets.KEY_PASSWORD }}'\''\n }' android/app/build.gradle sed -i 's/signingConfig signingConfigs.debug/signingConfig signingConfigs.release/' android/app/build.gradle sed -i "s/versionCode .*/versionCode ${{ env.VERSION_CODE }}/" android/app/build.gradle - echo "Signing config and version code configured in build.gradle" - name: Configure Gradle run: | chmod +x android/gradlew echo "org.gradle.jvmargs=-Xmx6144m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError" >> android/gradle.properties + echo "org.gradle.parallel=true" >> android/gradle.properties + echo "org.gradle.caching=true" >> android/gradle.properties + echo "org.gradle.configuration-cache=true" >> android/gradle.properties echo "android.enable16KbPageSizes=true" >> android/gradle.properties - - name: Create secrets.json - run: | - echo '{ - "APP_KEY": "${{ secrets.COUNTLY_APP_KEY }}", - "SALT": "${{ secrets.COUNTLY_SALT }}", - "SERVER_URL": "${{ secrets.COUNTLY_SERVER_URL }}" - }' > secrets.json - - name: Build AAB - run: | - cd android - ./gradlew bundleRelease --no-daemon + run: cd android && ./gradlew bundleRelease --no-daemon - - name: Upload AAB + - name: Upload AAB artifact uses: actions/upload-artifact@v4 with: name: app-release.aab path: android/app/build/outputs/bundle/release/*.aab - - name: Upload on Play Store on beta track - if: success() + - name: Upload to Play Store uses: r0adkll/upload-google-play@v1 with: serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_KEY }} diff --git a/.github/workflows/build-beta.yml b/.github/workflows/build-beta.yml deleted file mode 100644 index 0ebe35fb..00000000 --- a/.github/workflows/build-beta.yml +++ /dev/null @@ -1,186 +0,0 @@ -name: Build a beta - -on: - workflow_dispatch: - -permissions: - contents: write - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Notify start on Telegram - run: | - MESSAGE="🦋 *Starting a build for a beta version* - - 📦 ${{ github.repository }} (${{ github.ref_name }}) - 👤 ${{ github.actor }} | Run #${{ github.run_number }} - 📅 $(date -u +'%Y-%m-%d %H:%M UTC') - 🔗 [Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" - - curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ - -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \ - -d parse_mode=Markdown \ - -d text="$MESSAGE" - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - - - name: Create and Push Tag - run: | - VERSION=$(node -p "require('./package.json').version") - DATE=$(date +'%Y%m%d-%H%M') - TAG="v$VERSION-$DATE" - - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - git tag $TAG - git push origin $TAG - - - name: Install deps - run: npm install --legacy-peer-deps || true - - - name: Prebuild Android - run: npx expo prebuild --platform android --clean - - - name: Decode keystore - run: echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/release.keystore - - - name: Calculate Android Version Code - run: | - VERSION_CODE=$(node -e " - const version = require('./package.json').version; - const cleanVersion = parseInt(version.replace(/\./g, '')); - const runNumber = parseInt(process.env.GITHUB_RUN_NUMBER || '0'); - console.log(cleanVersion * 100000 + runNumber); - ") - echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV - echo "Computed VERSION_CODE: $VERSION_CODE" - - - name: Configure signing - run: | - sed -i '/signingConfigs {/a\ release {\n storeFile file('\''release.keystore'\'')\n storePassword '\''${{ secrets.KEYSTORE_PASSWORD }}'\''\n keyAlias '\''${{ secrets.KEY_ALIAS }}'\''\n keyPassword '\''${{ secrets.KEY_PASSWORD }}'\''\n }' android/app/build.gradle - sed -i 's/signingConfig signingConfigs.debug/signingConfig signingConfigs.release/' android/app/build.gradle - sed -i "s/versionCode .*/versionCode ${{ env.VERSION_CODE }}/" android/app/build.gradle - echo "Signing config and version code configured in build.gradle" - - - name: Configure Gradle - run: | - chmod +x android/gradlew - echo "org.gradle.jvmargs=-Xmx6144m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError" >> android/gradle.properties - echo "android.enable16KbPageSizes=true" >> android/gradle.properties - - - name: Create secrets.json - run: | - echo '{ - "APP_KEY": "${{ secrets.COUNTLY_APP_KEY }}", - "SALT": "${{ secrets.COUNTLY_SALT }}", - "SERVER_URL": "${{ secrets.COUNTLY_SERVER_URL }}" - }' > secrets.json - - - name: Build APK - run: | - cd android - ./gradlew assembleRelease --no-daemon - - - name: Build AAB - run: | - cd android - ./gradlew bundleRelease --no-daemon - - - name: Sign APK - run: | - echo "Aligning and signing APK..." - ZIPALIGN=$(find $ANDROID_HOME/build-tools -name zipalign | sort -V | tail -n 1) - APKSIGNER=$(find $ANDROID_HOME/build-tools -name apksigner | sort -V | tail -n 1) - UNALIGNED_APK=android/app/build/outputs/apk/release/app-release.apk - ALIGNED_APK=android/app/build/outputs/apk/release/app-release-aligned.apk - FINAL_APK=android/app/build/outputs/apk/release/app-release-final.apk - - echo "Using zipalign at: $ZIPALIGN" - echo "Using apksigner at: $APKSIGNER" - - # Align APK - $ZIPALIGN -v -p 4 $UNALIGNED_APK $ALIGNED_APK - - # Sign aligned APK - $APKSIGNER sign --ks android/app/release.keystore \ - --ks-pass pass:${{ secrets.KEYSTORE_PASSWORD }} \ - --key-pass pass:${{ secrets.KEY_PASSWORD }} \ - --out $FINAL_APK \ - $ALIGNED_APK - - # Verify signature - $APKSIGNER verify --verbose --print-certs $FINAL_APK - - # Replace unaligned APK by final signed & aligned APK - mv $FINAL_APK $UNALIGNED_APK - - # Clean intermediate aligned APK - rm -f $ALIGNED_APK - - - name: Upload APK - uses: actions/upload-artifact@v4 - with: - name: app-release.apk - path: android/app/build/outputs/apk/release/*.apk - - - name: Upload AAB - uses: actions/upload-artifact@v4 - with: - name: app-release.aab - path: android/app/build/outputs/bundle/release/*.aab - - - name: Upload on Play Store on beta track - if: success() - uses: r0adkll/upload-google-play@v1 - with: - serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_KEY }} - packageName: xyz.getpapillon.app - releaseFiles: android/app/build/outputs/bundle/release/*.aab - track: beta - inAppUpdatePriority: 3 - status: completed - - - name: Notify success on Telegram - if: success() - run: | - DURATION=$((SECONDS - 0)) - MESSAGE="✅ *The build for the beta version was successful* - - 📦 ${{ github.repository }} (${{ github.ref_name }}) - 👤 ${{ github.actor }} | Run #${{ github.run_number }} - 🔗 [Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" - - curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ - -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \ - -d parse_mode=Markdown \ - -d text="$MESSAGE" - - - name: Notify failure on Telegram - if: failure() - run: | - MESSAGE="❌ *The build for the beta version failed* - - 📦 ${{ github.repository }} (${{ github.ref_name }}) - 👤 ${{ github.actor }} | Run #${{ github.run_number }} - 🔗 [Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" - - curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ - -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \ - -d parse_mode=Markdown \ - -d text="$MESSAGE" diff --git a/.github/workflows/issue-check.yml b/.github/workflows/issue-check.yml deleted file mode 100644 index 6a18538c..00000000 --- a/.github/workflows/issue-check.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Issue Check - -on: - issues: - types: [opened] - -jobs: - check-issue: - runs-on: ubuntu-latest - permissions: - issues: write - contents: read - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Install Dependencies - working-directory: .github/bot - run: npm ci - - - name: Build Bot - working-directory: .github/bot - run: npm run build - - - name: Run Issue Check - working-directory: .github/bot - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: node dist/issue/index.js diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 00000000..e455973c --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,35 @@ +name: Merge dev into main + +on: + workflow_dispatch: + +jobs: + merge: + name: Merge + if: github.repository == 'PapillonApp/Papillon' + runs-on: ubuntu-latest + + steps: + - name: Generate app token + id: app-token + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - name: Configure git + run: | + git config user.name "papillon-release[bot]" + git config user.email "papillon-release[bot]@users.noreply.github.com" + + - name: Merge dev into main + run: | + git checkout main + git merge origin/dev --no-edit + git push origin main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..ba272363 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,60 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: "Numéro de version (x.x.x)" + required: true + type: string + +jobs: + release: + name: Merge & Release + if: github.repository == 'PapillonApp/Papillon' + runs-on: ubuntu-latest + + steps: + - name: Validate version format + run: | + if ! echo "${{ inputs.version }}" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Version invalide '${{ inputs.version }}'. Format attendu : x.x.x (ex: 7.2.0)" + exit 1 + fi + + - name: Generate app token + id: app-token + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - name: Configure git + run: | + git config user.name "papillon-release[bot]" + git config user.email "papillon-release[bot]@users.noreply.github.com" + + - name: Merge dev into main + run: | + git checkout main + git merge origin/dev --no-edit + git push origin main + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + token: ${{ steps.app-token.outputs.token }} + tag_name: v${{ inputs.version }} + name: v${{ inputs.version }} + target_commitish: main + make_latest: true + body: | + Papillon + + **Notes de mise à jour :** https://papillon.bzh/release-notes/${{ inputs.version }} diff --git a/.gitignore b/.gitignore index c7a6c9da..f4e4fba5 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,6 @@ android/* bun.lock yarn.lock -secrets.json \ No newline at end of file +secrets.json +.vscode/ +.idea/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 813312b4..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. - // Pointez pour afficher la description des attributs existants. - // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "expo", - "request": "attach", - "name": "Debug Expo app", - "projectRoot": "${workspaceFolder}", - "bundlerPort": "8081", - "bundlerHost": "127.0.0.1" - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 44a34c50..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "editor.formatOnSave": false, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.codeActionsOnSave": [ - "source.fixAll.eslint" - ], - "[typescriptreact]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - } -} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6cfe508d..b73f19ce 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,15 +2,15 @@ + + - - - + - + @@ -18,11 +18,16 @@ - + - + + + + + + diff --git a/app.config.ts b/app.config.ts index cb299768..5a90bd5c 100644 --- a/app.config.ts +++ b/app.config.ts @@ -96,6 +96,7 @@ export default { backgroundColor: "#003A21", }, supportsTablet: true, + predictiveBackGestureEnabled: true }, web: { bundler: "metro", @@ -108,6 +109,8 @@ export default { "expo-video", "expo-audio", "expo-localization", + "expo-asset", + "@react-native-community/datetimepicker", [ "expo-image-picker", { diff --git a/app/(features)/(cards)/cards.tsx b/app/(features)/(cards)/cards.tsx index 89fb45b7..893ce339 100644 --- a/app/(features)/(cards)/cards.tsx +++ b/app/(features)/(cards)/cards.tsx @@ -2,7 +2,7 @@ import { getManager } from "@/services/shared"; import { Balance } from "@/services/shared/balance"; import { useAccountStore } from "@/stores/account"; import { Services } from "@/stores/account/types"; -import Button from "@/ui/components/Button"; +import Button from "@/ui/new/Button"; import ChipButton from "@/ui/components/ChipButton"; import { Dynamic } from "@/ui/components/Dynamic"; import { EmptyItem } from "@/ui/components/EmptyItem"; @@ -19,7 +19,7 @@ import { LinearGradient } from "expo-linear-gradient"; import { router, useFocusEffect } from "expo-router"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Image, Pressable, View } from "react-native"; +import { Image, Platform, Pressable, View } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; export default function QRCodeAndCardsPage() { @@ -51,6 +51,7 @@ export default function QRCodeAndCardsPage() { return ( <> } - trailing={ + trailing={Platform.OS === "ios" && ( { router.dismiss(); }} /> - } + )} />