mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 09:50:15 +01:00
Merge branch 'master' into wallrtd
This commit is contained in:
commit
a9c4aaaf63
137
.github/workflows/build-ios-release-pullrequest.yml
vendored
137
.github/workflows/build-ios-release-pullrequest.yml
vendored
@ -17,65 +17,88 @@ jobs:
|
||||
outputs:
|
||||
new_build_number: ${{ steps.generate_build_number.outputs.build_number }}
|
||||
project_version: ${{ steps.determine_marketing_version.outputs.project_version }}
|
||||
ipa_output_path: ${{ steps.build_app.outputs.ipa_output_path }}
|
||||
latest_commit_message: ${{ steps.get_latest_commit_message.outputs.commit_message }}
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
steps:
|
||||
- name: Checkout project
|
||||
- name: Checkout Project
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetches all history
|
||||
|
||||
|
||||
- name: Specify node version
|
||||
|
||||
- name: Specify Node.js Version
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 16.0
|
||||
|
||||
- name: Set up Ruby
|
||||
|
||||
- name: Set Up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3.1.6
|
||||
bundler-cache: true
|
||||
|
||||
- name: Install dependencies with Bundler
|
||||
|
||||
- name: Install Dependencies with Bundler
|
||||
run: |
|
||||
bundle config path vendor/bundle
|
||||
bundle install --jobs 4 --retry 3 --quiet
|
||||
|
||||
- name: Install node_modules
|
||||
- name: Install Node Modules
|
||||
run: npm install --omit=dev --yes
|
||||
|
||||
- name: Install CocoaPods Dependencies
|
||||
run: |
|
||||
bundle exec fastlane ios install_pods
|
||||
|
||||
- name: Cache CocoaPods Pods
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ios/Pods
|
||||
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
|
||||
- name: Display release-notes.txt
|
||||
run: cat release-notes.txt
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pods-
|
||||
|
||||
- name: Get Latest Commit Message
|
||||
id: get_latest_commit_message
|
||||
run: |
|
||||
LATEST_COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s")
|
||||
echo "LATEST_COMMIT_MESSAGE=${LATEST_COMMIT_MESSAGE}" >> $GITHUB_ENV
|
||||
echo "::set-output name=commit_message::$LATEST_COMMIT_MESSAGE"
|
||||
- name: Set up Git Authentication
|
||||
echo "commit_message=$LATEST_COMMIT_MESSAGE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate Build Number Based on Timestamp
|
||||
id: generate_build_number
|
||||
run: |
|
||||
NEW_BUILD_NUMBER=$(date +%s)
|
||||
echo "NEW_BUILD_NUMBER=$NEW_BUILD_NUMBER" >> $GITHUB_ENV
|
||||
echo "build_number=$NEW_BUILD_NUMBER" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set Build Number
|
||||
run: bundle exec fastlane ios increment_build_number_lane
|
||||
|
||||
- name: Determine Marketing Version
|
||||
id: determine_marketing_version
|
||||
run: |
|
||||
MARKETING_VERSION=$(grep MARKETING_VERSION BlueWallet.xcodeproj/project.pbxproj | awk -F '= ' '{print $2}' | tr -d ' ;' | head -1)
|
||||
echo "PROJECT_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV
|
||||
echo "project_version=$MARKETING_VERSION" >> $GITHUB_OUTPUT
|
||||
working-directory: ios
|
||||
|
||||
- name: Set Up Git Authentication
|
||||
env:
|
||||
ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
|
||||
run: |
|
||||
git config --global credential.helper 'cache --timeout=3600'
|
||||
git config --global http.https://github.com/.extraheader "AUTHORIZATION: basic $(echo -n x-access-token:${ACCESS_TOKEN} | base64)"
|
||||
|
||||
- name: Create Temporary Keychain
|
||||
run: bundle exec fastlane ios create_temp_keychain
|
||||
env:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
|
||||
- name: Setup Provisioning Profiles
|
||||
env:
|
||||
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
||||
@ -86,14 +109,16 @@ jobs:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: |
|
||||
bundle exec fastlane ios setup_provisioning_profiles
|
||||
|
||||
- name: Cache Provisioning Profiles
|
||||
id: cache_provisioning_profiles
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/Library/MobileDevice/Provisioning Profiles
|
||||
path: ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
key: ${{ runner.os }}-provisioning-profiles-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-provisioning-profiles-
|
||||
|
||||
- name: Check Cache Status for Provisioning Profiles
|
||||
run: |
|
||||
if [ -n "${{ steps.cache_provisioning_profiles.outputs.cache-hit }}" ]; then
|
||||
@ -101,41 +126,36 @@ jobs:
|
||||
else
|
||||
echo "No cache found for provisioning profiles. A new cache will be created."
|
||||
fi
|
||||
|
||||
- name: Verify Provisioning Profiles Exist
|
||||
run: |
|
||||
if [ -d "~/Library/MobileDevice/Provisioning Profiles" ]; then
|
||||
echo "Provisioning profiles are available in the cache."
|
||||
ls -la ~/Library/MobileDevice/Provisioning Profiles
|
||||
ls -la ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
else
|
||||
echo "Provisioning profiles directory does not exist."
|
||||
fi
|
||||
- name: Generate Build Number based on timestamp
|
||||
id: generate_build_number
|
||||
run: |
|
||||
NEW_BUILD_NUMBER=$(date +%s)
|
||||
echo "NEW_BUILD_NUMBER=$NEW_BUILD_NUMBER" >> $GITHUB_ENV
|
||||
echo "::set-output name=build_number::$NEW_BUILD_NUMBER"
|
||||
- name: Set Build Number
|
||||
run: bundle exec fastlane ios increment_build_number_lane
|
||||
- name: Determine Marketing Version
|
||||
id: determine_marketing_version
|
||||
run: |
|
||||
MARKETING_VERSION=$(grep MARKETING_VERSION BlueWallet.xcodeproj/project.pbxproj | awk -F '= ' '{print $2}' | tr -d ' ;' | head -1)
|
||||
echo "PROJECT_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV
|
||||
echo "::set-output name=project_version::$MARKETING_VERSION"
|
||||
working-directory: ios
|
||||
|
||||
- name: Expected IPA file name
|
||||
run: |
|
||||
echo "IPA file name: BlueWallet_${{env.PROJECT_VERSION}}(${{env.NEW_BUILD_NUMBER}}).ipa"
|
||||
|
||||
- name: Build App
|
||||
run: bundle exec fastlane ios build_app_lane
|
||||
id: build_app
|
||||
run: |
|
||||
bundle exec fastlane ios build_app_lane --verbose
|
||||
echo "ipa_output_path=$IPA_OUTPUT_PATH" >> $GITHUB_OUTPUT # Set the IPA output path for future jobs
|
||||
|
||||
- name: Upload Build Logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build_logs
|
||||
path: ./ios/build_logs/
|
||||
|
||||
- name: Upload IPA as Artifact
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: BlueWallet_${{env.PROJECT_VERSION}}_${{env.NEW_BUILD_NUMBER}}.ipa
|
||||
path: ./build/BlueWallet_${{env.PROJECT_VERSION}}_${{env.NEW_BUILD_NUMBER}}.ipa
|
||||
|
||||
path: ${{ env.IPA_OUTPUT_PATH }} # Directly from Fastfile `IPA_OUTPUT_PATH`
|
||||
|
||||
testflight-upload:
|
||||
needs: build
|
||||
runs-on: macos-latest
|
||||
@ -146,13 +166,15 @@ jobs:
|
||||
PROJECT_VERSION: ${{ needs.build.outputs.project_version }}
|
||||
LATEST_COMMIT_MESSAGE: ${{ needs.build.outputs.latest_commit_message }}
|
||||
steps:
|
||||
- name: Checkout project
|
||||
- name: Checkout Project
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Ruby
|
||||
|
||||
- name: Set Up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3.1.6
|
||||
bundler-cache: true
|
||||
|
||||
- name: Cache Ruby Gems
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@ -160,19 +182,43 @@ jobs:
|
||||
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gems-
|
||||
- name: Install dependencies with Bundler
|
||||
|
||||
- name: Install Dependencies with Bundler
|
||||
run: |
|
||||
bundle config path vendor/bundle
|
||||
bundle install --jobs 4 --retry 3 --quiet
|
||||
|
||||
- name: Download IPA from Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: BlueWallet_${{ needs.build.outputs.project_version }}_${{ needs.build.outputs.new_build_number }}.ipa
|
||||
path: ./
|
||||
path: ./ # Download the IPA file to the current working directory
|
||||
|
||||
- name: Create App Store Connect API Key JSON
|
||||
run: echo '${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}' > ./appstore_api_key.json
|
||||
|
||||
- name: Verify IPA File Download
|
||||
run: |
|
||||
echo "Current directory:"
|
||||
pwd
|
||||
echo "Files in current directory:"
|
||||
ls -la ./
|
||||
|
||||
- name: Set IPA Path Environment Variable
|
||||
run: echo "IPA_OUTPUT_PATH=$(pwd)/BlueWallet_${{ needs.build.outputs.project_version }}_${{ needs.build.outputs.new_build_number }}.ipa" >> $GITHUB_ENV
|
||||
|
||||
- name: Verify IPA Path Before Upload
|
||||
run: |
|
||||
if [ ! -f "$IPA_OUTPUT_PATH" ]; then
|
||||
echo "IPA file not found at path: $IPA_OUTPUT_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload to TestFlight
|
||||
env:
|
||||
run: |
|
||||
ls -la $IPA_OUTPUT_PATH
|
||||
bundle exec fastlane ios upload_to_testflight_lane
|
||||
env:
|
||||
APP_STORE_CONNECT_API_KEY_PATH: $(pwd)/appstore_api_key.p8
|
||||
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
||||
GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
|
||||
@ -182,7 +228,8 @@ jobs:
|
||||
APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}
|
||||
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: bundle exec fastlane ios upload_to_testflight_lane
|
||||
IPA_OUTPUT_PATH: ${{ env.IPA_OUTPUT_PATH }}
|
||||
|
||||
- name: Post PR Comment
|
||||
if: success() && github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v6
|
||||
@ -199,4 +246,4 @@ jobs:
|
||||
...repo,
|
||||
issue_number: prNumber,
|
||||
body: message,
|
||||
});
|
||||
});
|
39
Gemfile.lock
39
Gemfile.lock
@ -24,20 +24,20 @@ GEM
|
||||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.973.0)
|
||||
aws-sdk-core (3.204.0)
|
||||
aws-partitions (1.989.0)
|
||||
aws-sdk-core (3.209.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.90.0)
|
||||
aws-sdk-core (~> 3, >= 3.203.0)
|
||||
aws-sdk-kms (1.94.0)
|
||||
aws-sdk-core (~> 3, >= 3.207.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.161.0)
|
||||
aws-sdk-core (~> 3, >= 3.203.0)
|
||||
aws-sdk-s3 (1.167.0)
|
||||
aws-sdk-core (~> 3, >= 3.207.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.9.1)
|
||||
aws-sigv4 (1.10.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
base64 (0.2.0)
|
||||
@ -96,8 +96,8 @@ GEM
|
||||
escape (0.0.4)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
excon (0.111.0)
|
||||
faraday (1.10.3)
|
||||
excon (0.112.0)
|
||||
faraday (1.10.4)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
@ -123,10 +123,10 @@ GEM
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday_middleware (1.2.1)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.3.1)
|
||||
fastlane (2.222.0)
|
||||
fastlane (2.224.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@ -214,16 +214,17 @@ GEM
|
||||
http-cookie (1.0.7)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.5)
|
||||
i18n (1.14.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.2)
|
||||
json (2.7.2)
|
||||
jwt (2.8.2)
|
||||
jwt (2.9.3)
|
||||
base64
|
||||
logger (1.6.1)
|
||||
mime-types (3.5.2)
|
||||
mime-types (3.6.0)
|
||||
logger
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2024.0903)
|
||||
mime-types-data (3.2024.1001)
|
||||
mini_magick (4.13.2)
|
||||
mini_mime (1.1.5)
|
||||
minitest (5.25.1)
|
||||
@ -250,7 +251,7 @@ GEM
|
||||
mime-types (>= 1.16, < 4.0)
|
||||
netrc (~> 0.8)
|
||||
retriable (3.1.2)
|
||||
rexml (3.3.7)
|
||||
rexml (3.3.8)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (2.5.1)
|
||||
ruby2_keywords (0.0.5)
|
||||
@ -278,15 +279,15 @@ GEM
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
unicode-display_width (2.5.0)
|
||||
unicode-display_width (2.6.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.25.0)
|
||||
xcodeproj (1.25.1)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (>= 3.3.2, < 4.0)
|
||||
rexml (>= 3.3.6, < 4.0)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
|
@ -23,7 +23,7 @@
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.ACTION_SCREEN_OFF"/>
|
||||
<uses-permission android:name="android.permission.ACTION_SCREEN_ON"/>
|
||||
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
@ -52,12 +52,12 @@
|
||||
<meta-data
|
||||
android:name="com.dieam.reactnativepushnotification.notification_color"
|
||||
android:resource="@color/white" />
|
||||
<meta-data
|
||||
android:name="firebase_messaging_auto_init_enabled"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="firebase_analytics_collection_enabled"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="firebase_messaging_auto_init_enabled"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="firebase_analytics_collection_enabled"
|
||||
android:value="false" />
|
||||
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActions" />
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
|
||||
@ -95,11 +95,23 @@
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
|
||||
<!-- Main launcher intent filter -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Intent filter for opening the app only when .psbt files are selected -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="file" android:mimeType="application/octet-stream" android:pathPattern=".*\\.psbt" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Intent filter for other custom schemes (bitcoin, bluewallet, etc.) -->
|
||||
<intent-filter tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
@ -111,7 +123,16 @@
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Intent filter for importing other file types (but not launching the app) -->
|
||||
<intent-filter tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="file" android:mimeType="application/octet-stream" android:pathPattern=".*\\.*" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Intent filter for handling PSBT files -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.EDIT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@ -119,7 +140,7 @@
|
||||
android:mimeType="application/octet-stream"
|
||||
android:pathPattern=".*\\.psbt" />
|
||||
</intent-filter>
|
||||
<intent-filter tools:ignore="AppLinkUrlError">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.EDIT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@ -142,4 +163,4 @@
|
||||
<data android:scheme="http" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
</manifest>
|
@ -147,7 +147,7 @@ function Notifications(props) {
|
||||
ActionSheet.showActionSheetWithOptions(
|
||||
{
|
||||
title: loc.settings.notifications,
|
||||
message: loc.notifications.would_you_like_to_receive_notifications,
|
||||
message: `${loc.notifications.would_you_like_to_receive_notifications}\n${loc.settings.push_notifications_explanation}`,
|
||||
options,
|
||||
cancelButtonIndex: 0, // Assuming 'no and don't ask' is still treated as the cancel action
|
||||
anchor: anchor ? findNodeHandle(anchor.current) : undefined,
|
||||
|
@ -1,75 +1,75 @@
|
||||
# Define app identifiers once for reuse across lanes
|
||||
def app_identifiers
|
||||
["io.bluewallet.bluewallet", "io.bluewallet.bluewallet.watch", "io.bluewallet.bluewallet.watch.extension", "io.bluewallet.bluewallet.Stickers", "io.bluewallet.bluewallet.MarketWidget"]
|
||||
[
|
||||
"io.bluewallet.bluewallet",
|
||||
"io.bluewallet.bluewallet.watch",
|
||||
"io.bluewallet.bluewallet.watch.extension",
|
||||
"io.bluewallet.bluewallet.Stickers",
|
||||
"io.bluewallet.bluewallet.MarketWidget"
|
||||
]
|
||||
end
|
||||
|
||||
default_platform(:android)
|
||||
project_root = File.expand_path("..", __dir__)
|
||||
|
||||
# ===========================
|
||||
# Android Lanes
|
||||
# ===========================
|
||||
|
||||
platform :android do
|
||||
|
||||
desc "Prepare the keystore file"
|
||||
lane :prepare_keystore do
|
||||
Dir.chdir(project_root) do
|
||||
keystore_file_hex = ENV['KEYSTORE_FILE_HEX']
|
||||
UI.user_error!("KEYSTORE_FILE_HEX environment variable is missing") if keystore_file_hex.nil?
|
||||
keystore_file_hex = ENV['KEYSTORE_FILE_HEX']
|
||||
UI.user_error!("KEYSTORE_FILE_HEX environment variable is missing") if keystore_file_hex.nil?
|
||||
|
||||
Dir.chdir("android") do
|
||||
UI.message("Creating keystore hex file...")
|
||||
Dir.chdir("android") do
|
||||
UI.message("Creating keystore from HEX...")
|
||||
File.write("bluewallet-release-key.keystore.hex", keystore_file_hex)
|
||||
|
||||
File.write("bluewallet-release-key.keystore.hex", keystore_file_hex)
|
||||
|
||||
sh("xxd -plain -revert bluewallet-release-key.keystore.hex > bluewallet-release-key.keystore") do |status|
|
||||
UI.user_error!("Error reverting hex to keystore") unless status.success?
|
||||
end
|
||||
UI.message("Keystore created successfully.")
|
||||
|
||||
File.delete("bluewallet-release-key.keystore.hex")
|
||||
sh("xxd -plain -revert bluewallet-release-key.keystore.hex > bluewallet-release-key.keystore") do |status|
|
||||
UI.user_error!("Error reverting hex to keystore") unless status.success?
|
||||
end
|
||||
UI.message("Keystore created successfully.")
|
||||
|
||||
File.delete("bluewallet-release-key.keystore.hex")
|
||||
end
|
||||
end
|
||||
|
||||
desc "Update version, build number, and sign APK"
|
||||
lane :update_version_build_and_sign_apk do
|
||||
Dir.chdir(project_root) do
|
||||
build_number = ENV['BUILD_NUMBER']
|
||||
UI.user_error!("BUILD_NUMBER environment variable is missing") if build_number.nil?
|
||||
|
||||
# Get the version name from build.gradle
|
||||
|
||||
# Extract versionName from build.gradle
|
||||
version_name = sh("grep versionName android/app/build.gradle | awk '{print $2}' | tr -d '\"'").strip
|
||||
|
||||
# Manually update the versionCode in build.gradle
|
||||
|
||||
# Update versionCode in build.gradle
|
||||
UI.message("Updating versionCode in build.gradle to #{build_number}...")
|
||||
build_gradle_path = "android/app/build.gradle"
|
||||
build_gradle_contents = File.read(build_gradle_path)
|
||||
new_build_gradle_contents = build_gradle_contents.gsub(/versionCode\s+\d+/, "versionCode #{build_number}")
|
||||
File.write(build_gradle_path, new_build_gradle_contents)
|
||||
|
||||
# Get the branch name and default to 'master' if empty
|
||||
|
||||
# Determine branch name
|
||||
branch_name = ENV['GITHUB_HEAD_REF'] || `git rev-parse --abbrev-ref HEAD`.strip.gsub(/[\/\\:?*"<>|]/, '_')
|
||||
if branch_name.nil? || branch_name.empty?
|
||||
branch_name = 'master'
|
||||
end
|
||||
|
||||
# Append branch name only if it's not 'master'
|
||||
if branch_name != 'master'
|
||||
signed_apk_name = "BlueWallet-#{version_name}-#{build_number}-#{branch_name}.apk"
|
||||
else
|
||||
signed_apk_name = "BlueWallet-#{version_name}-#{build_number}.apk"
|
||||
end
|
||||
|
||||
# Continue with the build process
|
||||
branch_name = 'master' if branch_name.nil? || branch_name.empty?
|
||||
|
||||
# Define APK name based on branch
|
||||
signed_apk_name = branch_name != 'master' ? "BlueWallet-#{version_name}-#{build_number}-#{branch_name}.apk" : "BlueWallet-#{version_name}-#{build_number}.apk"
|
||||
|
||||
# Build APK
|
||||
Dir.chdir("android") do
|
||||
UI.message("Building APK...")
|
||||
gradle(
|
||||
task: "assembleRelease",
|
||||
project_dir: "android"
|
||||
)
|
||||
gradle(task: "assembleRelease", project_dir: "android")
|
||||
UI.message("APK build completed.")
|
||||
|
||||
# Define the output paths
|
||||
|
||||
# Define paths
|
||||
unsigned_apk_path = "app/build/outputs/apk/release/app-release-unsigned.apk"
|
||||
signed_apk_path = "app/build/outputs/apk/release/#{signed_apk_name}"
|
||||
|
||||
# Rename the unsigned APK to include the version and build number
|
||||
|
||||
# Rename APK
|
||||
if File.exist?(unsigned_apk_path)
|
||||
UI.message("Renaming APK to #{signed_apk_name}...")
|
||||
FileUtils.mv(unsigned_apk_path, signed_apk_path)
|
||||
@ -78,8 +78,8 @@ platform :android do
|
||||
UI.error("Unsigned APK not found at path: #{unsigned_apk_path}")
|
||||
next
|
||||
end
|
||||
|
||||
# Sign the APK using apksigner
|
||||
|
||||
# Sign APK
|
||||
UI.message("Signing APK with apksigner...")
|
||||
apksigner_path = "#{ENV['ANDROID_HOME']}/build-tools/34.0.0/apksigner"
|
||||
sh("#{apksigner_path} sign --ks ./bluewallet-release-key.keystore --ks-pass=pass:#{ENV['KEYSTORE_PASSWORD']} #{signed_apk_path}")
|
||||
@ -91,33 +91,32 @@ platform :android do
|
||||
desc "Upload APK to BrowserStack and post result as PR comment"
|
||||
lane :upload_to_browserstack_and_comment do
|
||||
Dir.chdir(project_root) do
|
||||
# Fetch the APK path from environment variables
|
||||
# Determine APK path
|
||||
apk_path = ENV['APK_PATH']
|
||||
|
||||
# Attempt to find the APK if not provided
|
||||
if apk_path.nil? || apk_path.empty?
|
||||
UI.message("No APK path provided, attempting to find the artifact...")
|
||||
UI.message("No APK path provided, searching for APK...")
|
||||
apk_path = `find ./ -name "*.apk"`.strip
|
||||
UI.user_error!("No APK file found") if apk_path.nil? || apk_path.empty?
|
||||
end
|
||||
|
||||
|
||||
# Upload to BrowserStack
|
||||
UI.message("Uploading APK to BrowserStack: #{apk_path}...")
|
||||
upload_to_browserstack_app_live(
|
||||
file_path: apk_path,
|
||||
browserstack_username: ENV['BROWSERSTACK_USERNAME'],
|
||||
browserstack_access_key: ENV['BROWSERSTACK_ACCESS_KEY']
|
||||
)
|
||||
|
||||
# Extract the BrowserStack URL from the output
|
||||
|
||||
# Extract BrowserStack URL
|
||||
app_url = ENV['BROWSERSTACK_LIVE_APP_ID']
|
||||
UI.user_error!("BrowserStack upload failed, no app URL returned") if app_url.nil? || app_url.empty?
|
||||
|
||||
# Prepare necessary values for the PR comment
|
||||
|
||||
# Prepare PR comment
|
||||
apk_filename = File.basename(apk_path)
|
||||
apk_download_url = ENV['APK_OUTPUT_PATH'] # Assuming this path is accessible to the PR
|
||||
apk_download_url = ENV['APK_OUTPUT_PATH'] # Ensure this path is accessible
|
||||
browserstack_hashed_id = app_url.gsub('bs://', '')
|
||||
pr_number = ENV['GITHUB_PR_NUMBER']
|
||||
|
||||
|
||||
comment = <<~COMMENT
|
||||
### APK Successfully Uploaded to BrowserStack
|
||||
|
||||
@ -127,16 +126,17 @@ platform :android do
|
||||
- [Google Pixel 7 (Android 13.0)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Google+Pixel+7&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
- [Google Pixel 8 (Android 14.0)](https://app-live.browserstack.com/dashboard#os=android&os_version=14.0&device=Google+Pixel+8&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
- [Google Pixel 3a (Android 9.0)](https://app-live.browserstack.com/dashboard#os=android&os_version=9.0&device=Google+Pixel+3a&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
|
||||
|
||||
- [Samsung Galaxy Z Fold 5 (Android 13.0)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Samsung+Galaxy+Z+Fold+5&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
- [Samsung Galaxy Z Fold 6 (Android 14.0)](https://app-live.browserstack.com/dashboard#os=android&os_version=14.0&device=Samsung+Galaxy+Z+Fold+6&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
- [Samsung Galaxy Tab S9 (Android 13.0)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Samsung+Galaxy+Tab+S9&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
- [Samsung Galaxy Note 9 (Android 8.1)](https://app-live.browserstack.com/dashboard#os=android&os_version=8.1&device=Samsung+Galaxy+Note+9&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true)
|
||||
|
||||
|
||||
**Filename**: [#{apk_filename}](#{apk_download_url})
|
||||
**BrowserStack App URL**: #{app_url}
|
||||
COMMENT
|
||||
|
||||
|
||||
# Post PR comment if PR number is available
|
||||
if pr_number
|
||||
begin
|
||||
sh("GH_TOKEN=#{ENV['GH_TOKEN']} gh pr comment #{pr_number} --body '#{comment}'")
|
||||
@ -149,36 +149,34 @@ platform :android do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# ===========================
|
||||
# iOS Lanes
|
||||
# ===========================
|
||||
|
||||
platform :ios do
|
||||
|
||||
before_all do |lane, options|
|
||||
UI.message("Setting up for all lanes...")
|
||||
UI.message("Discarding all untracked changes before running any lane...")
|
||||
sh("git clean -fd")
|
||||
sh("git checkout -- .")
|
||||
end
|
||||
|
||||
desc "Register new devices from a file"
|
||||
lane :register_devices_from_txt do
|
||||
UI.message("Registering new devices from file...")
|
||||
|
||||
csv_path = "../../devices.txt" # Update this with the actual path to your file
|
||||
|
||||
# Registering devices using the devices_file parameter
|
||||
# Register devices using the devices_file parameter
|
||||
register_devices(
|
||||
devices_file: csv_path
|
||||
)
|
||||
|
||||
UI.message("Devices registered successfully.")
|
||||
|
||||
# Update provisioning profiles for all app identifiers
|
||||
app_identifiers.each do |app_identifier|
|
||||
match(
|
||||
type: "development",
|
||||
app_identifier: app_identifier,
|
||||
readonly: false, # This will regenerate the provisioning profile if needed
|
||||
readonly: false, # Regenerate provisioning profile if needed
|
||||
force_for_new_devices: true,
|
||||
clone_branch_directly: true
|
||||
)
|
||||
@ -190,7 +188,7 @@ platform :ios do
|
||||
desc "Create a temporary keychain"
|
||||
lane :create_temp_keychain do
|
||||
UI.message("Creating a temporary keychain...")
|
||||
|
||||
|
||||
create_keychain(
|
||||
name: "temp_keychain",
|
||||
password: ENV["KEYCHAIN_PASSWORD"],
|
||||
@ -199,32 +197,26 @@ platform :ios do
|
||||
timeout: 3600,
|
||||
lock_when_sleeps: true
|
||||
)
|
||||
|
||||
|
||||
UI.message("Temporary keychain created successfully.")
|
||||
end
|
||||
|
||||
desc "Synchronize certificates and provisioning profiles"
|
||||
lane :setup_provisioning_profiles do |options|
|
||||
lane :setup_provisioning_profiles do
|
||||
UI.message("Setting up provisioning profiles...")
|
||||
target_to_app_identifier = {
|
||||
'BlueWallet' => 'io.bluewallet.bluewallet',
|
||||
'BlueWalletWatch' => 'io.bluewallet.bluewallet.watch',
|
||||
'BlueWalletWatchExtension' => 'io.bluewallet.bluewallet.watch.extension',
|
||||
'Stickers' => 'io.bluewallet.bluewallet.Stickers',
|
||||
'MarketWidget' => 'io.bluewallet.bluewallet.MarketWidget'
|
||||
}
|
||||
|
||||
platform = options[:platform] || "ios" # Default to iOS if not specified
|
||||
|
||||
# Remove local master branch if it exists (Exit status: 128 - 'fatal: a branch named 'master' already exists')
|
||||
platform = "ios"
|
||||
|
||||
# Remove local master branch if it exists to avoid conflicts
|
||||
sh("git branch -D master || true")
|
||||
|
||||
target_to_app_identifier.each do |target, app_identifier|
|
||||
|
||||
# Iterate over app identifiers to fetch provisioning profiles
|
||||
app_identifiers.each do |app_identifier|
|
||||
match(
|
||||
git_basic_authorization: ENV["GIT_ACCESS_TOKEN"],
|
||||
git_url: ENV["GIT_URL"],
|
||||
type: "appstore",
|
||||
clone_branch_directly: true, # Skip if the branch already exists (Exit 128 error)
|
||||
clone_branch_directly: true, # Skip if the branch already exists
|
||||
platform: platform,
|
||||
app_identifier: app_identifier,
|
||||
team_id: ENV["ITC_TEAM_ID"],
|
||||
@ -255,7 +247,6 @@ platform :ios do
|
||||
app_identifier: app_identifiers,
|
||||
readonly: true,
|
||||
clone_branch_directly: true
|
||||
|
||||
)
|
||||
end
|
||||
|
||||
@ -306,94 +297,116 @@ platform :ios do
|
||||
cocoapods(podfile: "ios/Podfile")
|
||||
end
|
||||
|
||||
desc "Build the application"
|
||||
lane :build_app_lane do
|
||||
UI.message("Building the application...")
|
||||
build_app(
|
||||
scheme: "BlueWallet",
|
||||
workspace: "ios/BlueWallet.xcworkspace",
|
||||
export_method: "app-store",
|
||||
include_bitcode: false,
|
||||
configuration: "Release",
|
||||
skip_profile_detection: true,
|
||||
include_symbols: true,
|
||||
export_team_id: ENV["ITC_TEAM_ID"],
|
||||
export_options: {
|
||||
signingStyle: "manual",
|
||||
provisioningProfiles: {
|
||||
'io.bluewallet.bluewallet' => 'match AppStore io.bluewallet.bluewallet',
|
||||
'io.bluewallet.bluewallet.watch' => 'match AppStore io.bluewallet.bluewallet.watch',
|
||||
'io.bluewallet.bluewallet.watch.extension' => 'match AppStore io.bluewallet.bluewallet.watch.extension',
|
||||
'io.bluewallet.bluewallet.Stickers' => 'match AppStore io.bluewallet.bluewallet.Stickers',
|
||||
'io.bluewallet.bluewallet.MarketWidget' => 'match AppStore io.bluewallet.bluewallet.MarketWidget'
|
||||
}
|
||||
},
|
||||
xcargs: "GCC_PREPROCESSOR_DEFINITIONS='$(inherited) VERBOSE_LOGGING=1'",
|
||||
output_directory: "./build", # Directory where the IPA file will be stored
|
||||
desc "Upload IPA to TestFlight"
|
||||
lane :upload_to_testflight_lane do
|
||||
ipa_path = ENV['IPA_OUTPUT_PATH']
|
||||
changelog = ENV["LATEST_COMMIT_MESSAGE"]
|
||||
|
||||
output_name: "BlueWallet_#{ENV['PROJECT_VERSION']}_#{ENV['NEW_BUILD_NUMBER']}.ipa",
|
||||
buildlog_path: "./build_logs"
|
||||
)
|
||||
# Check if IPA exists before proceeding
|
||||
if ipa_path.nil? || ipa_path.empty? || !File.exist?(ipa_path)
|
||||
UI.user_error!("IPA file not found at path: #{ipa_path}")
|
||||
end
|
||||
|
||||
desc "Upload to TestFlight without Processing Wait"
|
||||
lane :upload_to_testflight_lane do
|
||||
attempts = 0
|
||||
max_attempts = 3
|
||||
UI.message("Uploading IPA to TestFlight from path: #{ipa_path}")
|
||||
|
||||
upload_to_testflight(
|
||||
api_key_path: "./appstore_api_key.json",
|
||||
ipa: ipa_path,
|
||||
skip_waiting_for_build_processing: true, # Do not wait for processing
|
||||
changelog: changelog
|
||||
)
|
||||
|
||||
UI.success("Successfully uploaded IPA to TestFlight!")
|
||||
end
|
||||
|
||||
desc "Build the iOS app"
|
||||
lane :build_app_lane do
|
||||
Dir.chdir(project_root) do
|
||||
UI.message("Building the application from: #{Dir.pwd}")
|
||||
|
||||
workspace_path = File.join(project_root, "ios", "BlueWallet.xcworkspace")
|
||||
export_options_path = File.join(project_root, "ios", "export_options.plist")
|
||||
|
||||
begin
|
||||
UI.message("Uploading to TestFlight without processing wait...")
|
||||
changelog = ENV["LATEST_COMMIT_MESSAGE"]
|
||||
ipa_path = "./BlueWallet_#{ENV['PROJECT_VERSION']}_#{ENV['NEW_BUILD_NUMBER']}.ipa"
|
||||
|
||||
upload_to_testflight(
|
||||
api_key_path: "./appstore_api_key.json",
|
||||
ipa: ipa_path,
|
||||
skip_waiting_for_build_processing: true, # Do not wait for processing
|
||||
changelog: changelog
|
||||
build_ios_app(
|
||||
scheme: "BlueWallet",
|
||||
workspace: workspace_path,
|
||||
export_method: "app-store",
|
||||
include_bitcode: false,
|
||||
configuration: "Release",
|
||||
skip_profile_detection: false,
|
||||
include_symbols: true,
|
||||
export_team_id: ENV["ITC_TEAM_ID"],
|
||||
export_options: export_options_path,
|
||||
output_directory: File.join(project_root, "ios", "build"),
|
||||
output_name: "BlueWallet_#{ENV['PROJECT_VERSION']}_#{ENV['NEW_BUILD_NUMBER']}.ipa",
|
||||
buildlog_path: File.join(project_root, "ios", "build_logs"),
|
||||
silent: false,
|
||||
clean: true
|
||||
)
|
||||
rescue => exception
|
||||
attempts += 1
|
||||
if attempts <= max_attempts
|
||||
wait_time = 180 # 3 minutes in seconds
|
||||
UI.message("Attempt ##{attempts} failed with error: #{exception.message}. Waiting #{wait_time} seconds before trying again...")
|
||||
sleep(wait_time)
|
||||
retry
|
||||
else
|
||||
UI.error("Failed after #{max_attempts} attempts. Error: #{exception.message}")
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "Deploy to TestFlight"
|
||||
lane :deploy do |options|
|
||||
UI.message("Starting build process...")
|
||||
|
||||
# Update the WWDR certificate
|
||||
update_wwdr_certificate
|
||||
|
||||
setup_app_store_connect_api_key
|
||||
setup_provisioning_profiles
|
||||
clear_derived_data_lane
|
||||
increment_build_number_lane
|
||||
|
||||
unless File.directory?("Pods")
|
||||
install_pods
|
||||
rescue => e
|
||||
UI.user_error!("build_ios_app failed: #{e.message}")
|
||||
end
|
||||
|
||||
build_app_lane
|
||||
upload_to_testflight_lane
|
||||
# Use File.join to construct paths without extra slashes
|
||||
ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH]
|
||||
|
||||
# Clean up and delete the temporary keychain
|
||||
delete_keychain(name: "temp_keychain")
|
||||
if ipa_path && File.exist?(ipa_path)
|
||||
UI.message("IPA successfully found at: #{ipa_path}")
|
||||
ENV['IPA_OUTPUT_PATH'] = ipa_path
|
||||
sh("echo 'IPA_OUTPUT_PATH=#{ipa_path}' >> $GITHUB_ENV") # Export for GitHub Actions
|
||||
else
|
||||
UI.user_error!("IPA not found after build_ios_app.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Mark deployment as completed for the current commit
|
||||
last_commit = last_git_commit
|
||||
already_built_flag = ".already_built_#{last_commit[:sha]}"
|
||||
File.write(already_built_flag, Time.now.to_s)
|
||||
# ===========================
|
||||
# Global Lanes
|
||||
# ===========================
|
||||
|
||||
|
||||
|
||||
desc "Deploy to TestFlight"
|
||||
lane :deploy do |options|
|
||||
UI.message("Starting deployment process...")
|
||||
|
||||
# Update WWDR Certificate
|
||||
update_wwdr_certificate
|
||||
|
||||
# Setup App Store Connect API Key
|
||||
setup_app_store_connect_api_key
|
||||
|
||||
# Setup Provisioning Profiles
|
||||
setup_provisioning_profiles
|
||||
|
||||
# Clear Derived Data
|
||||
clear_derived_data_lane
|
||||
|
||||
# Increment Build Number
|
||||
increment_build_number_lane
|
||||
|
||||
# Install CocoaPods if not already installed
|
||||
unless File.directory?("Pods")
|
||||
install_pods
|
||||
end
|
||||
|
||||
desc "Update 'What's New' section in App Store Connect for the 'Prepare for Submission' version"
|
||||
# Build the iOS App
|
||||
build_app_lane
|
||||
|
||||
# Upload IPA to TestFlight
|
||||
upload_to_testflight_lane
|
||||
|
||||
# Clean up and delete the temporary keychain
|
||||
delete_keychain(name: "temp_keychain")
|
||||
|
||||
# Mark deployment as completed for the current commit
|
||||
last_commit = last_git_commit
|
||||
already_built_flag = ".already_built_#{last_commit[:sha]}"
|
||||
File.write(already_built_flag, Time.now.to_s)
|
||||
end
|
||||
|
||||
desc "Update 'What's New' section in App Store Connect for the 'Prepare for Submission' version"
|
||||
lane :update_release_notes do |options|
|
||||
require 'spaceship'
|
||||
|
||||
@ -402,16 +415,13 @@ lane :update_release_notes do |options|
|
||||
|
||||
app = Spaceship::ConnectAPI::App.find(app_identifiers.first)
|
||||
|
||||
unless app
|
||||
UI.user_error!("Could not find the app with identifier: #{app_identifiers.first}")
|
||||
end
|
||||
UI.user_error!("Could not find the app with identifier: #{app_identifiers.first}") unless app
|
||||
|
||||
# Retry logic for fetching or creating the edit version
|
||||
retries = 5
|
||||
begin
|
||||
prepare_version = app.get_edit_app_store_version(platform: Spaceship::ConnectAPI::Platform::IOS)
|
||||
|
||||
# If no "Prepare for Submission" version is found, create a new one
|
||||
if prepare_version.nil?
|
||||
UI.message("No version in 'Prepare for Submission' found. Creating a new version...")
|
||||
latest_version = app.get_latest_version(platform: Spaceship::ConnectAPI::Platform::IOS)
|
||||
@ -436,12 +446,11 @@ lane :update_release_notes do |options|
|
||||
# Extract existing metadata
|
||||
localized_metadata = prepare_version.get_app_store_version_localizations
|
||||
|
||||
# Get all the enabled locales for the app version
|
||||
# Get enabled locales
|
||||
enabled_locales = localized_metadata.map(&:locale)
|
||||
|
||||
# Define valid language codes and filter them based on enabled locales
|
||||
# Define release notes
|
||||
release_notes_text = options[:release_notes]
|
||||
|
||||
if release_notes_text.nil? || release_notes_text.strip.empty?
|
||||
release_notes_path = "../../release-notes.txt"
|
||||
unless File.exist?(release_notes_path)
|
||||
@ -451,6 +460,7 @@ lane :update_release_notes do |options|
|
||||
release_notes_text = File.read(release_notes_path)
|
||||
end
|
||||
|
||||
# Define localized release notes
|
||||
localized_release_notes = {
|
||||
'en-US' => release_notes_text, # English (U.S.) - Primary
|
||||
'ar-SA' => release_notes_text, # Arabic
|
||||
@ -479,7 +489,7 @@ lane :update_release_notes do |options|
|
||||
'th' => release_notes_text, # Thai
|
||||
}.select { |locale, _| enabled_locales.include?(locale) } # Only include enabled locales
|
||||
|
||||
# Review what's going to be updated
|
||||
# Review release notes updates
|
||||
UI.message("Review the following release notes updates:")
|
||||
localized_release_notes.each do |locale, notes|
|
||||
UI.message("Locale: #{locale} - Notes: #{notes}")
|
||||
@ -487,21 +497,17 @@ lane :update_release_notes do |options|
|
||||
|
||||
unless options[:force_yes]
|
||||
confirm = UI.confirm("Do you want to proceed with these release notes updates?")
|
||||
unless confirm
|
||||
UI.user_error!("User aborted the lane.")
|
||||
end
|
||||
UI.user_error!("User aborted the lane.") unless confirm
|
||||
end
|
||||
|
||||
# Update release notes in App Store Connect and skip all other metadata
|
||||
# Update release notes in App Store Connect
|
||||
localized_release_notes.each do |locale, notes|
|
||||
app_store_version_localization = localized_metadata.find { |loc| loc.locale == locale }
|
||||
|
||||
if app_store_version_localization
|
||||
app_store_version_localization.update(attributes: { "whats_new" => notes })
|
||||
else
|
||||
UI.error("No localization found for locale #{locale}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -2,369 +2,322 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11</string>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bluewallet.fetchTxsForWallet</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>BlueWallet</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>PSBT</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>TXN</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ELECTRUMBACKUP</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.backup</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>bitcoin</string>
|
||||
<string>lightning</string>
|
||||
<string>bluewallet</string>
|
||||
<string>lapp</string>
|
||||
<string>blue</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.finance</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
<string>http</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>onion</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>tailscale.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>ts.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>In order to use FaceID please confirm your permission.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Your authorization is required to save this image.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>In order to import an image for scanning, we need your permission to access your photo library.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bluewallet.receiveonchain</string>
|
||||
<string>io.bluewallet.bluewallet.xpub</string>
|
||||
</array>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Entypo.ttf</string>
|
||||
<string>FontAwesome.ttf</string>
|
||||
<string>FontAwesome5_Brands.ttf</string>
|
||||
<string>FontAwesome5_Regular.ttf</string>
|
||||
<string>FontAwesome5_Solid.ttf</string>
|
||||
<string>Ionicons.ttf</string>
|
||||
<string>MaterialIcons.ttf</string>
|
||||
<string>Octicons.ttf</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Partially Signed Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>JSON File</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>json</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/json</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Partially Signed Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Bitcoin Transaction</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Electrum Backup</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.backup</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>backup</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>bugsnag</key>
|
||||
<dict>
|
||||
<key>apiKey</key>
|
||||
<string>17ba9059f676f1cc4f45d98182388b01</string>
|
||||
</dict>
|
||||
<key>FIREBASE_ANALYTICS_COLLECTION_ENABLED</key>
|
||||
<false/>
|
||||
<key>FIREBASE_MESSAGING_AUTO_INIT_ENABLED</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11</string>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bluewallet.fetchTxsForWallet</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>BlueWallet</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<!-- PSBT file type -->
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>PSBT</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- TXN file type -->
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>TXN</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- Electrum Backup file type -->
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>ELECTRUMBACKUP</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.backup</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- BW COSIGNER file type -->
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>bitcoin</string>
|
||||
<string>lightning</string>
|
||||
<string>bluewallet</string>
|
||||
<string>lapp</string>
|
||||
<string>blue</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.finance</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
<string>http</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>onion</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>tailscale.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>ts.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>In order to use FaceID please confirm your permission.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Your authorization is required to save this image.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>In order to import an image for scanning, we need your permission to access your photo library.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>io.bluewallet.bluewallet.receiveonchain</string>
|
||||
<string>io.bluewallet.bluewallet.xpub</string>
|
||||
</array>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Entypo.ttf</string>
|
||||
<string>FontAwesome.ttf</string>
|
||||
<string>FontAwesome5_Brands.ttf</string>
|
||||
<string>FontAwesome5_Regular.ttf</string>
|
||||
<string>FontAwesome5_Solid.ttf</string>
|
||||
<string>Ionicons.ttf</string>
|
||||
<string>MaterialIcons.ttf</string>
|
||||
<string>Octicons.ttf</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
|
||||
<!-- Define exported types (UTIs) for file types -->
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<!-- PSBT -->
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Partially Signed Bitcoin Transaction</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>psbt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<!-- BW Cosigner -->
|
||||
<dict>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>BW COSIGNER</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.bwcosigner</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>bwcosigner</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<!-- TXN -->
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Bitcoin Transaction</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.psbt.txn</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>txn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<!-- Electrum Backup -->
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Electrum Backup</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>io.bluewallet.backup</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>backup</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<!-- Define imported types for other files -->
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>JSON File</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>public.json</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>json</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/json</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>bugsnag</key>
|
||||
<dict>
|
||||
<key>apiKey</key>
|
||||
<string>17ba9059f676f1cc4f45d98182388b01</string>
|
||||
</dict>
|
||||
|
||||
<key>FIREBASE_ANALYTICS_COLLECTION_ENABLED</key>
|
||||
<false/>
|
||||
<key>FIREBASE_MESSAGING_AUTO_INIT_ENABLED</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
33
ios/export_options.plist
Normal file
33
ios/export_options.plist
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>app-store</string>
|
||||
<key>signingStyle</key>
|
||||
<string>manual</string>
|
||||
<key>teamID</key>
|
||||
<string>A7W54YZ4WU</string>
|
||||
<key>uploadSymbols</key>
|
||||
<true/>
|
||||
<key>compileBitcode</key>
|
||||
<false/>
|
||||
<key>thinning</key>
|
||||
<string>none</string>
|
||||
<key>destination</key>
|
||||
<string>export</string>
|
||||
<key>provisioningProfiles</key>
|
||||
<dict>
|
||||
<key>io.bluewallet.bluewallet</key>
|
||||
<string>match AppStore io.bluewallet.bluewallet</string>
|
||||
<key>io.bluewallet.bluewallet.watch</key>
|
||||
<string>match AppStore io.bluewallet.bluewallet.watch</string>
|
||||
<key>io.bluewallet.bluewallet.watch.extension</key>
|
||||
<string>match AppStore io.bluewallet.bluewallet.watch.extension</string>
|
||||
<key>io.bluewallet.bluewallet.Stickers</key>
|
||||
<string>match AppStore io.bluewallet.bluewallet.Stickers</string>
|
||||
<key>io.bluewallet.bluewallet.MarketWidget</key>
|
||||
<string>match AppStore io.bluewallet.bluewallet.MarketWidget</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
@ -10,6 +10,7 @@
|
||||
"never": "nie",
|
||||
"of": "{number} von {total}",
|
||||
"ok": "OK",
|
||||
"enter_url": "URL eingeben",
|
||||
"storage_is_encrypted": "Zum Entschlüsseln des Speichers das Passwort eingeben.",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
@ -25,7 +26,8 @@
|
||||
"pick_file": "Datei auswählen",
|
||||
"enter_amount": "Betrag eingeben",
|
||||
"qr_custom_input_button": "10x antippen für individuelle Eingabe",
|
||||
"unlock": "Entsperren"
|
||||
"unlock": "Entsperren",
|
||||
"suggested": "Vorgeschlagen"
|
||||
},
|
||||
"azteco": {
|
||||
"codeIs": "Dein Gutscheincode lautet",
|
||||
@ -206,8 +208,10 @@
|
||||
"performance_score": "Leistungskennzahl: {num}",
|
||||
"run_performance_test": "Leistung testen",
|
||||
"about_selftest": "Selbsttest ausführen",
|
||||
"block_explorer_invalid_custom_url": "Ungültige URL. Geben Sie eine gültige URL ein, die mit http:// oder https:// beginnt.",
|
||||
"about_selftest_electrum_disabled": "Deaktiviere den Electrum Offline-Modus, um den Selbsttest durchführen zu können.",
|
||||
"about_selftest_ok": "Alle internen Tests verliefen erfolgreich. Das Wallet funktioniert gut.",
|
||||
|
||||
"about_sm_github": "GitHub",
|
||||
"about_sm_discord": "Discord Server",
|
||||
"about_sm_telegram": "Telegram-Channel",
|
||||
@ -259,6 +263,9 @@
|
||||
"encrypt_storage_explanation_description_line1": "Die Aktivierung der Speicherverschlüsselung fügt Ihrer App ein zusätzlicher Schutz hinzu. Die Art und Weise, wie die Daten auf Ihrem Gerät gespeichert werden, macht es anderen damit schwieriger, ohne Erlaubnis darauf zuzugreifen.",
|
||||
"encrypt_storage_explanation_description_line2": "Diese Verschlüsselung betrifft den Zugriff auf die im Gerät gespeicherten Schlüssel, schützt also die Wallets. Die Wallet selbst werden dabei nicht mit einem Passwort oder einem anderen zusätzlichen Schutz versehen.",
|
||||
"i_understand": "Ich habe verstanden",
|
||||
"block_explorer": "Block-Explorer",
|
||||
"block_explorer_preferred": "Bevorzugten Block-Explorer verwenden",
|
||||
"block_explorer_error_saving_custom": "Fehler beim Speichern des bevorzugten Block-Explorers.",
|
||||
"encrypt_title": "Sicherheit",
|
||||
"encrypt_tstorage": "Speicher",
|
||||
"encrypt_use": "Benutze {type}",
|
||||
|
@ -302,8 +302,8 @@
|
||||
"privacy_clipboard_explanation": "Provide shortcuts if an address or invoice is found in your clipboard.",
|
||||
"privacy_do_not_track": "Disable Analytics",
|
||||
"privacy_do_not_track_explanation": "Performance and reliability information will not be submitted for analysis.",
|
||||
"push_notifications": "Push Notifications",
|
||||
"rate": "Rate",
|
||||
"push_notifications_explanation": "By enabling notifications, your device token will be sent to the server, along with wallet addresses and transaction IDs for all wallets and transactions made after enabling notifications. The device token is used to send notifications, and the wallet information allows us to notify you about incoming Bitcoin or transaction confirmations.\n\nOnly information from after you enable notifications is transmitted—nothing from before is collected.\n\nDisabling notifications will remove all of this information from the server. Additionally, deleting a wallet from the app will also remove its associated information from the server.",
|
||||
"selfTest": "Self-Test",
|
||||
"save": "Save",
|
||||
"saved": "Saved",
|
||||
@ -315,6 +315,7 @@
|
||||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "Would you like to receive notifications when you get incoming payments?",
|
||||
"notifications_subtitle": "Incoming payments and transaction confirmations",
|
||||
"no_and_dont_ask": "No, and do not ask me again.",
|
||||
"ask_me_later": "Ask me later."
|
||||
},
|
||||
|
@ -14,7 +14,7 @@ const DefaultView = lazy(() => import('../screen/settings/DefaultView'));
|
||||
const ElectrumSettings = lazy(() => import('../screen/settings/electrumSettings'));
|
||||
const EncryptStorage = lazy(() => import('../screen/settings/EncryptStorage'));
|
||||
const LightningSettings = lazy(() => import('../screen/settings/LightningSettings'));
|
||||
const NotificationSettings = lazy(() => import('../screen/settings/notificationSettings'));
|
||||
const NotificationSettings = lazy(() => import('../screen/settings/NotificationSettings'));
|
||||
const SelfTest = lazy(() => import('../screen/settings/SelfTest'));
|
||||
const ReleaseNotes = lazy(() => import('../screen/settings/ReleaseNotes'));
|
||||
const Tools = lazy(() => import('../screen/settings/tools'));
|
||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -20,7 +20,7 @@
|
||||
"@react-native-async-storage/async-storage": "1.24.0",
|
||||
"@react-native-clipboard/clipboard": "1.14.2",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#8c6004b",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#a33379d",
|
||||
"@react-native/gradle-plugin": "^0.75.4",
|
||||
"@react-native/metro-config": "0.75.4",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
@ -89,7 +89,7 @@
|
||||
"react-native-quick-actions": "0.3.13",
|
||||
"react-native-randombytes": "3.6.1",
|
||||
"react-native-rate": "1.2.12",
|
||||
"react-native-reanimated": "3.15.4",
|
||||
"react-native-reanimated": "3.15.5",
|
||||
"react-native-safe-area-context": "4.11.0",
|
||||
"react-native-screens": "3.34.0",
|
||||
"react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc",
|
||||
@ -5601,8 +5601,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-menu/menu": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/menu.git#8c6004bae317e00ea8f163612af96dddb9cdf7e9",
|
||||
"version": "1.1.3",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/menu.git#a33379d2bf2349066488ab9726c7d52bf76c49ac",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
@ -20882,9 +20882,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated": {
|
||||
"version": "3.15.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.15.4.tgz",
|
||||
"integrity": "sha512-jcpHE+MnsvSbClhHgAFoken7SnaHrUJ5gVA8BUw8S1j6rkrw2VzRpht6cxn14NlqYx5ytjfG9IXJDOzq8tFvfw==",
|
||||
"version": "3.15.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.15.5.tgz",
|
||||
"integrity": "sha512-admqeZ0w235vQqYPy+IUgmHu5gwKi9+b7AQRV1yIK3MbAMLYx+RY+tTUtx1CNE5X+rNZ6eSQssW5z77yTwIusg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
|
||||
|
@ -84,7 +84,7 @@
|
||||
"@react-native-async-storage/async-storage": "1.24.0",
|
||||
"@react-native-clipboard/clipboard": "1.14.2",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#8c6004b",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#a33379d",
|
||||
"@react-native/gradle-plugin": "^0.75.4",
|
||||
"@react-native/metro-config": "0.75.4",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
@ -153,7 +153,7 @@
|
||||
"react-native-quick-actions": "0.3.13",
|
||||
"react-native-randombytes": "3.6.1",
|
||||
"react-native-rate": "1.2.12",
|
||||
"react-native-reanimated": "3.15.4",
|
||||
"react-native-reanimated": "3.15.5",
|
||||
"react-native-safe-area-context": "4.11.0",
|
||||
"react-native-screens": "3.34.0",
|
||||
"react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc",
|
||||
|
231
screen/settings/NotificationSettings.tsx
Normal file
231
screen/settings/NotificationSettings.tsx
Normal file
@ -0,0 +1,231 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, View, Pressable } from 'react-native';
|
||||
import { Button as ButtonRNElements } from '@rneui/themed';
|
||||
// @ts-ignore: no declaration file
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueCard, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { Button } from '../../components/Button';
|
||||
import CopyToClipboardButton from '../../components/CopyToClipboardButton';
|
||||
import ListItem, { PressableWrapper } from '../../components/ListItem';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import loc from '../../loc';
|
||||
import { Divider } from '@rneui/base';
|
||||
|
||||
const NotificationSettings: React.FC = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isNotificationsEnabled, setNotificationsEnabled] = useState(false);
|
||||
const [tokenInfo, setTokenInfo] = useState('<empty>');
|
||||
const [URI, setURI] = useState<string | undefined>();
|
||||
const [tapCount, setTapCount] = useState(0);
|
||||
const { colors } = useTheme();
|
||||
const stylesWithThemeHook = {
|
||||
root: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
scroll: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
scrollBody: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
uri: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
};
|
||||
|
||||
const handleTap = () => {
|
||||
setTapCount(prevCount => prevCount + 1);
|
||||
};
|
||||
|
||||
const onNotificationsSwitch = async (value: boolean) => {
|
||||
try {
|
||||
setNotificationsEnabled(value);
|
||||
if (value) {
|
||||
// User is enabling notifications
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.cleanUserOptOutFlag();
|
||||
// @ts-ignore: refactor later
|
||||
if (await Notifications.getPushToken()) {
|
||||
// we already have a token, so we just need to reenable ALL level on groundcontrol:
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.setLevels(true);
|
||||
} else {
|
||||
// ok, we dont have a token. we need to try to obtain permissions, configure callbacks and save token locally:
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.tryToObtainPermissions();
|
||||
}
|
||||
} else {
|
||||
// User is disabling notifications
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.setLevels(false);
|
||||
}
|
||||
|
||||
// @ts-ignore: refactor later
|
||||
setNotificationsEnabled(await Notifications.isNotificationsEnabled());
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
presentAlert({ message: (error as Error).message });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
// @ts-ignore: refactor later
|
||||
setNotificationsEnabled(await Notifications.isNotificationsEnabled());
|
||||
// @ts-ignore: refactor later
|
||||
setURI(await Notifications.getSavedUri());
|
||||
// @ts-ignore: refactor later
|
||||
setTokenInfo(
|
||||
'token: ' +
|
||||
// @ts-ignore: refactor later
|
||||
JSON.stringify(await Notifications.getPushToken()) +
|
||||
' permissions: ' +
|
||||
// @ts-ignore: refactor later
|
||||
JSON.stringify(await Notifications.checkPermissions()) +
|
||||
' stored notifications: ' +
|
||||
// @ts-ignore: refactor later
|
||||
JSON.stringify(await Notifications.getStoredNotifications()),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
presentAlert({ message: (e as Error).message });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const save = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (URI) {
|
||||
// validating only if its not empty. empty means use default
|
||||
// @ts-ignore: refactor later
|
||||
if (await Notifications.isGroundControlUriValid(URI)) {
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.saveUri(URI);
|
||||
presentAlert({ message: loc.settings.saved });
|
||||
} else {
|
||||
presentAlert({ message: loc.settings.not_a_valid_uri });
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.saveUri('');
|
||||
presentAlert({ message: loc.settings.saved });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [URI]);
|
||||
|
||||
return (
|
||||
<ScrollView style={stylesWithThemeHook.scroll} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
|
||||
<ListItem
|
||||
Component={PressableWrapper}
|
||||
title={loc.settings.notifications}
|
||||
subtitle={loc.notifications.notifications_subtitle}
|
||||
disabled={isLoading}
|
||||
switch={{ onValueChange: onNotificationsSwitch, value: isNotificationsEnabled, testID: 'NotificationsSwitch' }}
|
||||
/>
|
||||
|
||||
<Pressable onPress={handleTap}>
|
||||
<BlueCard>
|
||||
<BlueText style={styles.multilineText}>{loc.settings.push_notifications_explanation}</BlueText>
|
||||
</BlueCard>
|
||||
</Pressable>
|
||||
|
||||
{tapCount >= 10 && (
|
||||
<>
|
||||
<Divider />
|
||||
<BlueCard>
|
||||
<BlueText>{loc.settings.groundcontrol_explanation}</BlueText>
|
||||
</BlueCard>
|
||||
|
||||
<ButtonRNElements
|
||||
icon={{
|
||||
name: 'github',
|
||||
type: 'font-awesome',
|
||||
color: colors.foregroundColor,
|
||||
}}
|
||||
onPress={() => Linking.openURL('https://github.com/BlueWallet/GroundControl')}
|
||||
titleStyle={{ color: colors.buttonAlternativeTextColor }}
|
||||
title="github.com/BlueWallet/GroundControl"
|
||||
color={colors.buttonTextColor}
|
||||
buttonStyle={styles.buttonStyle}
|
||||
/>
|
||||
|
||||
<BlueCard>
|
||||
<View style={[styles.uri, stylesWithThemeHook.uri]}>
|
||||
<TextInput
|
||||
// @ts-ignore: refactor later
|
||||
placeholder={Notifications.getDefaultUri()}
|
||||
value={URI}
|
||||
onChangeText={setURI}
|
||||
numberOfLines={1}
|
||||
style={styles.uriText}
|
||||
placeholderTextColor="#81868e"
|
||||
editable={!isLoading}
|
||||
textContentType="URL"
|
||||
autoCapitalize="none"
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<BlueSpacing20 />
|
||||
<BlueText style={styles.centered} onPress={() => setTapCount(tapCount + 1)}>
|
||||
♪ Ground Control to Major Tom ♪
|
||||
</BlueText>
|
||||
<BlueText style={styles.centered} onPress={() => setTapCount(tapCount + 1)}>
|
||||
♪ Commencing countdown, engines on ♪
|
||||
</BlueText>
|
||||
|
||||
<View>
|
||||
<CopyToClipboardButton stringToCopy={tokenInfo} displayText={tokenInfo} />
|
||||
</View>
|
||||
|
||||
<BlueSpacing20 />
|
||||
<Button onPress={save} title={loc.settings.save} />
|
||||
</BlueCard>
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
uri: {
|
||||
flexDirection: 'row',
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
minHeight: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
},
|
||||
centered: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
uriText: {
|
||||
flex: 1,
|
||||
color: '#81868e',
|
||||
marginHorizontal: 8,
|
||||
minHeight: 36,
|
||||
height: 36,
|
||||
},
|
||||
buttonStyle: {
|
||||
backgroundColor: 'transparent',
|
||||
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
||||
},
|
||||
multilineText: {
|
||||
textAlign: 'left',
|
||||
lineHeight: 20,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default NotificationSettings;
|
@ -1,195 +0,0 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, TouchableWithoutFeedback, View } from 'react-native';
|
||||
import { Button as ButtonRNElements } from '@rneui/themed';
|
||||
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueCard, BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { Button } from '../../components/Button';
|
||||
import CopyToClipboardButton from '../../components/CopyToClipboardButton';
|
||||
import ListItem from '../../components/ListItem';
|
||||
import { BlueCurrentTheme, useTheme } from '../../components/themes';
|
||||
import loc from '../../loc';
|
||||
|
||||
const NotificationSettings = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isNotificationsEnabled, setNotificationsEnabled] = useState(false);
|
||||
const [isShowTokenInfo, setShowTokenInfo] = useState(0);
|
||||
const [tokenInfo, setTokenInfo] = useState('<empty>');
|
||||
const [URI, setURI] = useState();
|
||||
|
||||
const { colors } = useTheme();
|
||||
|
||||
const onNotificationsSwitch = async value => {
|
||||
setNotificationsEnabled(value); // so the slider is not 'jumpy'
|
||||
if (value) {
|
||||
// user is ENABLING notifications
|
||||
await Notifications.cleanUserOptOutFlag();
|
||||
if (await Notifications.getPushToken()) {
|
||||
// we already have a token, so we just need to reenable ALL level on groundcontrol:
|
||||
await Notifications.setLevels(true);
|
||||
} else {
|
||||
// ok, we dont have a token. we need to try to obtain permissions, configure callbacks and save token locally:
|
||||
await Notifications.tryToObtainPermissions();
|
||||
}
|
||||
} else {
|
||||
// user is DISABLING notifications
|
||||
await Notifications.setLevels(false);
|
||||
}
|
||||
|
||||
setNotificationsEnabled(await Notifications.isNotificationsEnabled());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
setNotificationsEnabled(await Notifications.isNotificationsEnabled());
|
||||
setURI(await Notifications.getSavedUri());
|
||||
setTokenInfo(
|
||||
'token: ' +
|
||||
JSON.stringify(await Notifications.getPushToken()) +
|
||||
' permissions: ' +
|
||||
JSON.stringify(await Notifications.checkPermissions()) +
|
||||
' stored notifications: ' +
|
||||
JSON.stringify(await Notifications.getStoredNotifications()),
|
||||
);
|
||||
} catch (e) {
|
||||
console.debug(e);
|
||||
presentAlert({ message: e.message });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const stylesWithThemeHook = {
|
||||
root: {
|
||||
...styles.root,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
scroll: {
|
||||
...styles.scroll,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
scrollBody: {
|
||||
...styles.scrollBody,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
};
|
||||
|
||||
const save = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (URI) {
|
||||
// validating only if its not empty. empty means use default
|
||||
if (await Notifications.isGroundControlUriValid(URI)) {
|
||||
await Notifications.saveUri(URI);
|
||||
presentAlert({ message: loc.settings.saved });
|
||||
} else {
|
||||
presentAlert({ message: loc.settings.not_a_valid_uri });
|
||||
}
|
||||
} else {
|
||||
await Notifications.saveUri('');
|
||||
presentAlert({ message: loc.settings.saved });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [URI]);
|
||||
|
||||
return isLoading ? (
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<ScrollView style={stylesWithThemeHook.scroll} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
|
||||
<ListItem
|
||||
Component={TouchableWithoutFeedback}
|
||||
title={loc.settings.push_notifications}
|
||||
subtitle={loc.settings.groundcontrol_explanation}
|
||||
switch={{ onValueChange: onNotificationsSwitch, value: isNotificationsEnabled, testID: 'NotificationsSwitch' }}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<ButtonRNElements
|
||||
icon={{
|
||||
name: 'github',
|
||||
type: 'font-awesome',
|
||||
color: colors.foregroundColor,
|
||||
}}
|
||||
onPress={() => Linking.openURL('https://github.com/BlueWallet/GroundControl')}
|
||||
titleStyle={{ color: colors.buttonAlternativeTextColor }}
|
||||
title="github.com/BlueWallet/GroundControl"
|
||||
color={colors.buttonTextColor}
|
||||
buttonStyle={styles.buttonStyle}
|
||||
/>
|
||||
|
||||
<BlueCard>
|
||||
<View style={styles.uri}>
|
||||
<TextInput
|
||||
placeholder={Notifications.getDefaultUri()}
|
||||
value={URI}
|
||||
onChangeText={setURI}
|
||||
numberOfLines={1}
|
||||
style={styles.uriText}
|
||||
placeholderTextColor="#81868e"
|
||||
editable={!isLoading}
|
||||
textContentType="URL"
|
||||
autoCapitalize="none"
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<BlueSpacing20 />
|
||||
<BlueText style={styles.centered} onPress={() => setShowTokenInfo(isShowTokenInfo + 1)}>
|
||||
♪ Ground Control to Major Tom ♪
|
||||
</BlueText>
|
||||
<BlueText style={styles.centered} onPress={() => setShowTokenInfo(isShowTokenInfo + 1)}>
|
||||
♪ Commencing countdown, engines on ♪
|
||||
</BlueText>
|
||||
|
||||
{isShowTokenInfo >= 9 && (
|
||||
<View>
|
||||
<CopyToClipboardButton stringToCopy={tokenInfo} displayText={tokenInfo} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
<BlueSpacing20 />
|
||||
<Button onPress={save} title={loc.settings.save} />
|
||||
</BlueCard>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
uri: {
|
||||
flexDirection: 'row',
|
||||
borderColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
|
||||
minHeight: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
},
|
||||
centered: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
uriText: {
|
||||
flex: 1,
|
||||
color: '#81868e',
|
||||
marginHorizontal: 8,
|
||||
minHeight: 36,
|
||||
height: 36,
|
||||
},
|
||||
buttonStyle: {
|
||||
backgroundColor: 'transparent',
|
||||
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
||||
},
|
||||
});
|
||||
|
||||
export default NotificationSettings;
|
Loading…
Reference in New Issue
Block a user