Preparing for CI test

This commit is contained in:
Zhiyuan Zheng 2021-02-02 22:50:38 +01:00
parent b8aa402c99
commit 1a5b21d56a
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
54 changed files with 641 additions and 448 deletions

View File

@ -1,31 +0,0 @@
name: Publish development
on:
push:
branches:
- '*-development'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: -- Step 1 -- Checkout code
uses: actions/checkout@v2
- name: -- Step 2 -- Setup node
uses: actions/setup-node@v2
with:
node-version: 14.x
- name: -- Step 3 -- Use Expo action
uses: expo/expo-github-action@v5
with:
expo-version: 4.x
expo-username: ${{ secrets.EXPO_USERNAME }}
expo-token: ${{ secrets.EXPO_TOKEN }}
- name: -- Step 4 -- Install dependencies
run: yarn install
- name: -- Step 5 -- Publish
env:
SENTRY_ORGANIZATION: ${{ secrets.SENTRY_ORGANIZATION }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_DEPLOY_ENV: development
run: expo publish --release-channel=${GITHUB_REF#refs/heads/}

View File

@ -2,7 +2,7 @@ name: Publish production
on:
push:
branches:
- '*-production'
- production
jobs:
publish:
runs-on: ubuntu-latest

View File

@ -1,11 +1,11 @@
name: Publish staging
name: Build staging
on:
push:
branches:
- '*-staging'
- staging
jobs:
publish:
runs-on: ubuntu-latest
build-ios:
runs-on: macos-latest
steps:
- name: -- Step 1 -- Checkout code
uses: actions/checkout@v2
@ -13,19 +13,26 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: 14.x
- name: -- Step 3 -- Use Expo action
uses: expo/expo-github-action@v5
with:
expo-version: 4.x
expo-username: ${{ secrets.EXPO_USERNAME }}
expo-token: ${{ secrets.EXPO_TOKEN }}
- name: -- Step 3 -- Setup ruby
uses: actions/setup-ruby@v1
- name: -- Step 4 -- Install dependencies
run: yarn install
- name: -- Step 5 -- Publish
- name: -- Step 5 -- Install native dependencies
run: npx pod-install
- name: -- Step 6 -- Run fastlane
env:
TOOOT_ENVIRONMENT: staging
SENTRY_ORGANIZATION: ${{ secrets.SENTRY_ORGANIZATION }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_DEPLOY_ENV: staging
run: expo publish --release-channel=${GITHUB_REF#refs/heads/}
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }}
MATCH_GIT_PRIVATE_KEY: ${{ MATCH_GIT_PRIVATE_KEY }}
APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ APP_STORE_CONNECT_API_KEY_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_KEY: ${{ APP_STORE_CONNECT_API_KEY_KEY }}
run: yarn ios:build

View File

@ -1,3 +1,5 @@
source "https://rubygems.org"
gem "fastlane"
gem "fastlane"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

View File

@ -85,6 +85,9 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-json (1.0.0)
fastlane-plugin-versioning (0.4.4)
fastlane-plugin-yarn (1.2)
gh_inspector (1.1.3)
google-api-client (0.38.0)
addressable (~> 2.5, >= 2.5.1)
@ -196,6 +199,9 @@ PLATFORMS
DEPENDENCIES
fastlane
fastlane-plugin-json
fastlane-plugin-versioning
fastlane-plugin-yarn
BUNDLED WITH
1.17.2

30
VERSIONING.md Normal file
View File

@ -0,0 +1,30 @@
## Major releases - App Store
"Major releases" are artifacts published as `x.?.?`:
* An artifact must be released as `x.?.?` if native modules have been changed or updated, including upgrading Expo SDK version.
* A new app store version has to be submitted.
* Outdated versions in principle do not receive further OTA updates.
## Minor releases - App Store and OTA
"Minor releases" are artifacts published as `?.y.?`:
* An artifact can be released as `?.y.?` when there is no change nor update made to the native modules.
* A new app store version can be submitted for better first launch experience.
* All these versions that are not part of above mentioned outdates versions receive also OTA updates.
## Patch releases - OTA
"Patch releases" are artifacts published as `?.?.z`:
* An artifact must be release as `?.?.z` when there is no major change to the functionalities.
* No new app store version will be submitted.
* All these versions that are not part of above mentioned outdates versions receive also OTA updates.
## OTA release channels
* `MAJOR-environment`. Environments include `production`, `staging` and `development`.
## Major versions mapping to native module versions
| Major version | Native module version |
| :-----------: | :-------------------: |
| `0` | `210201` |

View File

@ -21,13 +21,28 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="true"
android:theme="@style/AppTheme"
>
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@xmflsct/tooot"/>
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="40.0.0"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.App.SplashScreen"
android:screenOrientation="portrait"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -42,6 +57,6 @@
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application android:requestLegacyExternalStorage="true"/>
</manifest>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually -->
<color name="splashscreen_background">#191919</color>
</resources>

View File

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml
version="1.0"
encoding="UTF-8"
standalone="yes"
?>
<resources>
<!-- Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually -->
<color name="iconBackground">#FFFFFF</color>
<color name="splashscreen_background">#FAFAFA</color>
<color name="colorPrimary">#023c69</color>
</resources>
</resources>

View File

@ -11,7 +11,8 @@
</style>
<style name="Theme.App.SplashScreen" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually -->
<!-- Customize your splash screen theme here -->
<item name="android:windowBackground">@drawable/splashscreen</item>
<item name="android:windowFullscreen">true</item>
<!-- Customize your splash screen theme here -->
</style>
</resources>

View File

@ -1,26 +1,20 @@
import { ExpoConfig } from '@expo/config'
import { versions } from './package.json'
import 'dotenv/config'
const toootVersion = `${versions.major}.${versions.minor}.${versions.patch}`
export default (): ExpoConfig => ({
name: 'tooot',
description: 'tooot for Mastodon',
slug: 'tooot',
version: toootVersion,
sdkVersion: versions.expo,
privacy: 'hidden',
sdkVersion: '40.0.0',
version: '0.8',
platforms: ['ios', 'android'],
orientation: 'portrait',
userInterfaceStyle: 'automatic',
icon: './assets/icon.png',
splash: {
backgroundColor: '#FAFAFA',
image: './assets/splash.png'
},
scheme: 'tooot',
assetBundlePatterns: ['assets/*'],
extra: {
sentryDSN: process.env.SENTRY_DSN,
sentryEnv: process.env.SENTRY_DEPLOY_ENV
toootEnvironment: process.env.TOOOT_ENVIRONMENT,
sentryDSN: process.env.SENTRY_DSN
},
hooks: {
postPublish: [
@ -31,7 +25,7 @@ export default (): ExpoConfig => ({
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
setCommits: process.env.GITHUB_SHA || undefined,
deployEnv: process.env.SENTRY_DEPLOY_ENV
deployEnv: process.env.TOOOT_ENVIRONMENT
}
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,79 +1,110 @@
$ExpoSDK = '40.0.0'
$NativeVersion = '210201' # Update when there is native module change
fastlane_version "2.172.0"
fastlane_version '2.172.0'
ensure_env_vars(
env_vars: ["TOOOT_ENVIRONMENT"]
)
VERSIONS = read_json( json_path: "./package.json" )[:versions]
ENVIRONMENT = ENV["TOOOT_ENVIRONMENT"]
VERSION = "#{VERSIONS[:major]}.#{VERSIONS[:minor]}"
RELEASE_CHANNEL = "#{VERSIONS[:major]}-#{ENVIRONMENT}"
BUILD_NUMBER = Time.now.strftime("%y%m%d")
platform :ios do
desc 'Build and deploy'
private_lane :build do |options|
branch = 'NATIVEVERSION-TYPE'.gsub('NATIVEVERSION', $NativeVersion).gsub('TYPE', options[:type])
set_info_plist_value(
path: './ios/tooot/Supporting/Expo.plist',
key: 'EXUpdatesSDKVersion',
value: $ExpoSDK
)
set_info_plist_value(
path: './ios/tooot/Supporting/Expo.plist',
key: 'EXUpdatesReleaseChannel',
value: branch
)
XCODEPROJ = "./ios/tooot.xcodeproj"
INFO_PLIST = "./ios/tooot/Info.plist"
EXPO_PLIST = "./ios/tooot/Supporting/Expo.plist"
case options[:type]
when 'staging', 'production'
ensure_git_branch(
branch: options[:type]
)
ensure_git_status_clean
increment_build_number(
build_number: $NativeVersion
)
app_store_connect_api_key(
key_filepath: "appstore.p8"
)
end
match(
type: options[:type],
readonly: true
)
case options[:type]
when 'development'
build_ios_app(
scheme: 'tooot',
silent: true,
include_bitcode: true,
workspace: './ios/tooot.xcworkspace',
export_method: 'development'
)
install_on_device(
skip_wifi: true
)
when 'staging'
build_ios_app(
scheme: 'tooot',
workspace: './ios/tooot.xcworkspace'
)
upload_to_testflight(
skip_submission: true,
notify_external_testers: false
)
desc "Prepare app store"
private_lane :prepare_appstore do
case ENVIRONMENT
when "staging", "production"
increment_build_number( xcodeproj: XCODEPROJ, build_number: BUILD_NUMBER )
app_store_connect_api_key
end
end
desc 'Build development to phone'
lane :development do
build(type: 'development')
desc "Expo release"
private_lane :expo_release do
yarn( package_path: "./package.json", flags: "release", command: RELEASE_CHANNEL )
end
desc 'Build staging to TestFlight'
lane :staging do
build(type: 'staging')
desc "Get certificates"
private_lane :get_certificates do |options|
if ENV['CI'] == true
match( type: options[:type], readonly: true, keychain_name: KEYCHAIN_NAME, keychain_password: KEYCHAIN_PASS )
else
match( type: options[:type], readonly: true )
end
end
desc 'Build product to App Store'
lane :production do
build(type: 'production')
desc "Build and deploy"
lane :build do
BUILD_DIRECTORY = "./ios/build"
SHOULD_BUILD_NATIVE = false
case ENVIRONMENT
when "staging", "production"
PREVIOUS_VERSION = get_info_plist_value( path: INFO_PLIST, key: "CFBundleShortVersionString" )
if VERSION.to_f > PREVIOUS_VERSION.to_f
SHOULD_BUILD_NATIVE = true
set_info_plist_value( path: INFO_PLIST, key: "CFBundleShortVersionString", value: VERSION )
set_info_plist_value( path: EXPO_PLIST, key: "EXUpdatesSDKVersion", value: VERSIONS[:expo] )
set_info_plist_value( path: EXPO_PLIST, key: "EXUpdatesReleaseChannel", value: RELEASE_CHANNEL )
end
when "development"
SHOULD_BUILD_NATIVE = true
end
if SHOULD_BUILD_NATIVE == true
prepare_appstore
KEYCHAIN_NAME = "tooot"
KEYCHAIN_PASS = SecureRandom.base64
if ENV['CI'] == true
create_keychain(
name: KEYCHAIN_NAME,
password: KEYCHAIN_PASS,
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: true
)
end
case ENVIRONMENT
when "development"
get_certificates( type: "development" )
build_ios_app(
export_method: "development",
output_directory: BUILD_DIRECTORY,
output_name: VERSION + "-" + BUILD_NUMBER
)
install_on_device( skip_wifi: true )
when "staging"
get_certificates( type: "appstore" )
build_ios_app(
export_method: "app-store",
output_directory: BUILD_DIRECTORY,
output_name: VERSION + "-" + BUILD_NUMBER
)
upload_to_testflight(
demo_account_required: true,
distribute_external: true,
groups: "内测用户",
changelog: "Ready for testing"
)
when "production"
get_certificates( type: "appstore" )
build_ios_app(
export_method: "app-store",
output_directory: BUILD_DIRECTORY,
output_name: VERSION + "-" + BUILD_NUMBER
)
end
end
expo_release
end
end

3
fastlane/Gymfile Normal file
View File

@ -0,0 +1,3 @@
scheme "tooot"
workspace "./ios/tooot.xcworkspace"
clean true

View File

@ -1,3 +1,2 @@
git_user_email("me@xmflsct.com")
git_private_key("./github.key")
storage_mode("git")

6
fastlane/Pluginfile Normal file
View File

@ -0,0 +1,6 @@
# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-yarn'
gem 'fastlane-plugin-json'

View File

@ -16,21 +16,11 @@ or alternatively using `brew install fastlane`
# Available Actions
## iOS
### ios development
### ios build
```
fastlane ios development
fastlane ios build
```
Build development to phone
### ios staging
```
fastlane ios staging
```
Build staging to TestFlight
### ios production
```
fastlane ios production
```
Build product to App Store
Build and deploy
----

View File

@ -12,6 +12,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
5E36538325C9B8BD009F93EE /* RootViewColor.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */; };
6CB3B7B773184F6EB8040C3E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C2DAF0391E246238BE2A4B4 /* InfoPlist.strings */; };
8BA74ECC129842FEA0CC08AF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F78D778B9BBC48D584012340 /* InfoPlist.strings */; };
96905EF65AED1B983A6B3ABC /* libPods-tooot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */; };
@ -30,6 +31,7 @@
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = tooot/main.m; sourceTree = "<group>"; };
4C2DAF0391E246238BE2A4B4 /* InfoPlist.strings */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = /Users/zhzhe/Documents/GitHub/tooot/app/ios/tooot/Supporting/en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-tooot.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = RootViewColor.xcassets; path = tooot/RootViewColor.xcassets; sourceTree = "<group>"; };
6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-tooot.debug.xcconfig"; path = "Target Support Files/Pods-tooot/Pods-tooot.debug.xcconfig"; sourceTree = "<group>"; };
7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-tooot.release.xcconfig"; path = "Target Support Files/Pods-tooot/Pods-tooot.release.xcconfig"; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = "<group>"; };
@ -72,6 +74,7 @@
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */,
B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */,
);
name = tooot;
@ -179,6 +182,7 @@
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = 8EGBLQ2MA6;
LastSwiftMigration = 1120;
ProvisioningStyle = Automatic;
};
};
};
@ -205,6 +209,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5E36538325C9B8BD009F93EE /* RootViewColor.xcassets in Resources */,
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
@ -325,7 +330,9 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
CURRENT_PROJECT_VERSION = 1;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2102022230;
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@ -343,6 +350,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
PRODUCT_NAME = tooot;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
@ -357,7 +365,9 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
CURRENT_PROJECT_VERSION = 1;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2102022230;
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
INFOPLIST_FILE = tooot/Info.plist;
@ -370,6 +380,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
PRODUCT_NAME = tooot;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";

View File

@ -85,7 +85,7 @@ static void InitializeFlipper(UIApplication *application) {
{
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
rootView.backgroundColor = [UIColor colorNamed:@"Background"];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;

View File

@ -5,11 +5,42 @@
"filename": "splashscreen.png",
"scale": "1x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"filename": "dark_splashscreen.png",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -5,11 +5,42 @@
"filename": "background.png",
"scale": "1x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"filename": "dark_background.png",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
},
{
"appearances": [
{
"appearance": "luminosity",
"value": "dark"
}
],
"idiom": "universal",
"scale": "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

@ -1,95 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<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>0.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>UIUserInterfaceStyle</key>
<string>Automatic</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>tooot</string>
<string>com.xmflsct.app.tooot</string>
</array>
</dict>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<key>CFBundleDisplayName</key>
<string>tooot</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your microphone</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to save photos</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to save photos</string>
<key>NSCameraUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to access your camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to use your microphone</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>gizmos</string>
<key>CFBundleURLSchemes</key>
<array>
<string>tooot</string>
</array>
</dict>
</array>
</dict>
</plist>
<dict>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>tooot</string>
<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>0.8</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>gizmos</string>
<key>CFBundleURLSchemes</key>
<array>
<string>tooot</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>2102022230</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCameraUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to access your camera</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>NSMicrophoneUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to use your microphone</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to save photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to save photos</string>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Automatic</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "250",
"green" : "250",
"red" : "250"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "25",
"green" : "25",
"red" : "25"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -9,7 +9,7 @@
<key>EXUpdatesLaunchWaitMs</key>
<integer>0</integer>
<key>EXUpdatesReleaseChannel</key>
<string>210201-development</string>
<string>0-staging</string>
<key>EXUpdatesSDKVersion</key>
<string>40.0.0</string>
<key>EXUpdatesURL</key>

View File

@ -3,7 +3,7 @@
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios",
"ios:development": "bundle exec fastlane ios development",
"ios:build": "bundle exec fastlane ios build",
"test": "jest --watchAll",
"release": "scripts/release.sh"
},
@ -104,6 +104,12 @@
"typescript": "~4.1.3"
},
"private": true,
"name": "app",
"version": "1.0.0"
}
"name": "tooot",
"versions": {
"native": "210201",
"major": 0,
"minor": 8,
"patch": 0,
"expo": "40.0.0"
}
}

BIN
splashes/dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -114,14 +114,18 @@ const Index: React.FC<Props> = ({ localCorrupt }) => {
return (
<>
<StatusBar barStyle={barStyle[mode]} backgroundColor={theme.background} />
<StatusBar barStyle={barStyle[mode]} />
<NavigationContainer
ref={navigationRef}
theme={themes[mode]}
onReady={navigationContainerOnReady}
onStateChange={navigationContainerOnStateChange}
>
<Stack.Navigator mode='modal' initialRouteName='Screen-Tabs'>
<Stack.Navigator
mode='modal'
initialRouteName='Screen-Tabs'
screenOptions={{ cardStyle: { backgroundColor: theme.background } }}
>
<Stack.Screen
name='Screen-Tabs'
component={ScreenTabs}

View File

@ -1,14 +1,8 @@
import { StyleConstants } from '@utils/styles/constants'
import React, { useCallback, useState } from 'react'
import {
ImageStyle,
Pressable,
StyleProp,
StyleSheet,
ViewStyle
} from 'react-native'
import React, { useCallback, useMemo, useState } from 'react'
import { Pressable, StyleProp, StyleSheet, ViewStyle } from 'react-native'
import { Blurhash } from 'react-native-blurhash'
import FastImage from 'react-native-fast-image'
import FastImage, { ImageStyle } from 'react-native-fast-image'
import { SharedElement } from 'react-navigation-shared-element'
import { useTheme } from '@utils/styles/ThemeManager'
@ -23,70 +17,120 @@ export interface Props {
imageStyle?: StyleProp<ImageStyle>
}
const GracefullyImage: React.FC<Props> = ({
sharedElement,
hidden = false,
uri,
blurhash,
dimension,
onPress,
style,
imageStyle
}) => {
const { mode, theme } = useTheme()
const [imageLoaded, setImageLoaded] = useState(false)
const children = useCallback(() => {
return (
<>
{sharedElement ? (
<SharedElement id={`image.${sharedElement}`} style={[styles.image]}>
<FastImage
source={{ uri: uri.preview || uri.original || uri.remote }}
style={[styles.image, imageStyle]}
onLoad={() => setImageLoaded(true)}
/>
</SharedElement>
) : (
<FastImage
source={{ uri: uri.preview || uri.original || uri.remote }}
style={[styles.image, imageStyle]}
onLoad={() => setImageLoaded(true)}
/>
)}
{blurhash && (hidden || !imageLoaded) ? (
<Blurhash
decodeAsync
blurhash={blurhash}
style={{
width: '100%',
height: '100%',
position: 'absolute',
top: StyleConstants.Spacing.XS / 2,
left: StyleConstants.Spacing.XS / 2
}}
/>
) : null}
</>
const GracefullyImage = React.memo(
({
sharedElement,
hidden = false,
uri,
blurhash,
dimension,
onPress,
style,
imageStyle
}: Props) => {
const { mode, theme } = useTheme()
const [previewLoaded, setPreviewLoaded] = useState(
uri.preview ? false : true
)
}, [hidden, imageLoaded, mode, uri])
const [originalLoaded, setOriginalLoaded] = useState(false)
const [originalFailed, setOriginalFailed] = useState(false)
const [remoteLoaded, setRemoteLoaded] = useState(uri.remote ? false : true)
return (
<Pressable
children={children}
style={[
style,
{ backgroundColor: theme.shimmerDefault },
dimension && { ...dimension }
]}
{...(onPress
? hidden
? { disabled: true }
: { onPress }
: { disabled: true })}
/>
)
}
const sourceUri = useMemo(() => {
if (previewLoaded) {
if (originalFailed) {
return uri.remote
} else {
return uri.original
}
} else {
return uri.preview
}
}, [previewLoaded, originalLoaded, originalFailed, remoteLoaded])
const onLoad = useCallback(() => {
if (previewLoaded) {
if (originalFailed) {
return setRemoteLoaded(true)
} else {
return setOriginalLoaded(true)
}
} else {
return setPreviewLoaded(true)
}
}, [previewLoaded, originalLoaded, originalFailed, remoteLoaded])
const onError = useCallback(() => {
if (previewLoaded) {
if (originalFailed) {
return
} else {
return setOriginalFailed(true)
}
} else {
return
}
}, [previewLoaded, originalLoaded, originalFailed, remoteLoaded])
const children = useCallback(() => {
return (
<>
{sharedElement ? (
<SharedElement id={`image.${sharedElement}`} style={[styles.image]}>
<FastImage
source={{ uri: sourceUri }}
style={[styles.image, imageStyle]}
onLoad={onLoad}
onError={onError}
/>
</SharedElement>
) : (
<FastImage
source={{ uri: sourceUri }}
style={[styles.image, imageStyle]}
onLoad={onLoad}
onError={onError}
/>
)}
{blurhash &&
(hidden || !(previewLoaded || originalLoaded || remoteLoaded)) ? (
<Blurhash
decodeAsync
blurhash={blurhash}
style={{
width: '100%',
height: '100%',
position: 'absolute',
top: StyleConstants.Spacing.XS / 2,
left: StyleConstants.Spacing.XS / 2
}}
/>
) : null}
</>
)
}, [hidden, previewLoaded, originalLoaded, remoteLoaded, mode, uri])
return (
<Pressable
children={children}
style={[
style,
{ backgroundColor: theme.shimmerDefault },
dimension && { ...dimension }
]}
{...(onPress
? hidden
? { disabled: true }
: { onPress }
: { disabled: true })}
/>
)
},
(prev, next) => {
let skipUpdate = true
skipUpdate = prev.hidden === next.hidden
skipUpdate = prev.uri.original === next.uri.original
return false
}
)
const styles = StyleSheet.create({
image: {

View File

@ -202,6 +202,7 @@ const TimelineActions: React.FC<Props> = ({
: iconColorAction(status.reblogged)
}
size={StyleConstants.Font.Size.L}
strokeWidth={status.reblogged ? 3 : undefined}
/>
{status.reblogs_count > 0 && (
<Text
@ -225,6 +226,7 @@ const TimelineActions: React.FC<Props> = ({
name='Heart'
color={iconColorAction(status.favourited)}
size={StyleConstants.Font.Size.L}
strokeWidth={status.favourited ? 3 : undefined}
/>
{status.favourites_count > 0 && (
<Text
@ -248,6 +250,7 @@ const TimelineActions: React.FC<Props> = ({
name='Bookmark'
color={iconColorAction(status.bookmarked)}
size={StyleConstants.Font.Size.L}
strokeWidth={status.bookmarked ? 3 : undefined}
/>
),
[status.bookmarked]

View File

@ -114,7 +114,7 @@ const TimelineAttachment: React.FC<Props> = ({ status }) => {
return (
<View>
<View style={styles.container}>{attachments}</View>
<View style={styles.container} children={attachments} />
{status.sensitive &&
(sensitiveShown ? (

View File

@ -1,3 +1,4 @@
import analytics from '@components/analytics'
import Button from '@components/Button'
import GracefullyImage from '@components/GracefullyImage'
import { Slider } from '@sharcoux/slider'
@ -8,7 +9,6 @@ import React, { useCallback, useState } from 'react'
import { StyleSheet, View } from 'react-native'
import { Blurhash } from 'react-native-blurhash'
import attachmentAspectRatio from './aspectRatio'
import analytics from '@components/analytics'
export interface Props {
total: number

View File

@ -27,6 +27,7 @@ const TimelineCard: React.FC<Props> = ({ card }) => {
uri={{ original: card.image }}
blurhash={card.blurhash}
style={styles.left}
imageStyle={styles.image}
/>
)}
<View style={styles.right}>
@ -64,15 +65,13 @@ const styles = StyleSheet.create({
height: StyleConstants.Font.LineHeight.M * 5,
marginTop: StyleConstants.Spacing.M,
borderWidth: StyleSheet.hairlineWidth,
borderRadius: 6
borderRadius: 6,
overflow: 'hidden'
},
left: {
width: StyleConstants.Font.LineHeight.M * 5,
height: StyleConstants.Font.LineHeight.M * 5
flexBasis: StyleConstants.Font.LineHeight.M * 5
},
image: {
width: '100%',
height: '100%',
borderTopLeftRadius: 6,
borderBottomLeftRadius: 6
},

View File

@ -1,8 +1,11 @@
import analytics from '@components/analytics'
import Button from '@components/Button'
import { StackScreenProps } from '@react-navigation/stack'
import { getLocalAccount, getLocalUrl } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Dimensions, StyleSheet, View } from 'react-native'
import {
PanGestureHandler,
@ -32,6 +35,8 @@ export type ScreenAccountProp = StackScreenProps<
const ScreenActions = React.memo(
({ route: { params }, navigation }: ScreenAccountProp) => {
const { t } = useTranslation()
const localAccount = useSelector(getLocalAccount)
let sameAccount = false
switch (params.type) {
@ -174,6 +179,15 @@ const ScreenActions = React.memo(
]}
/>
{actions}
<Button
type='text'
content={t('common:buttons.cancel')}
onPress={() => {
analytics('bottomsheet_cancel')
// dismiss()
}}
style={styles.button}
/>
</Animated.View>
</PanGestureHandler>
</Animated.View>

View File

@ -229,7 +229,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
return (
<KeyboardAvoidingView
style={[styles.base, {backgroundColor: 'red'}]}
style={styles.base}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<SafeAreaView

View File

@ -2,7 +2,8 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { MutableRefObject, useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { Dimensions, Image, StyleSheet, Text, View } from 'react-native'
import { Dimensions, StyleSheet, Text, View } from 'react-native'
import FastImage from 'react-native-fast-image'
import { PanGestureHandler } from 'react-native-gesture-handler'
import Animated, {
Extrapolate,
@ -110,7 +111,7 @@ const ComposeEditAttachmentImage: React.FC<Props> = ({ index, focus }) => {
return (
<>
<View style={{ overflow: 'hidden', flex: 1, alignItems: 'center' }}>
<Image
<FastImage
style={{
width: imageDimensionis.width,
height: imageDimensionis.height

View File

@ -53,14 +53,6 @@ const ComposeRoot: React.FC = () => {
})
}
}, [emojisData])
useEffect(() => {
if (emojisData && emojisData.length) {
// Prefetch first batch of emojis for faster loading experience
emojisData.slice(0, 40).forEach(emoji => {
Image.prefetch(emoji.url)
})
}
}, [emojisData])
const listEmpty = useMemo(() => {
if (isFetching) {

View File

@ -15,15 +15,9 @@ import React, {
useRef
} from 'react'
import { useTranslation } from 'react-i18next'
import {
FlatList,
Image,
Pressable,
StyleSheet,
Text,
View
} from 'react-native'
import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native'
import { Chase } from 'react-native-animated-spinkit'
import FastImage from 'react-native-fast-image'
import ComposeContext from '../../utils/createContext'
import { ExtendedAttachment } from '../../utils/types'
import addAttachment from './addAttachment'
@ -120,7 +114,7 @@ const ComposeAttachments: React.FC = () => {
key={index}
style={[styles.container, { width: calculateWidth(item) }]}
>
<Image
<FastImage
style={styles.image}
source={{
uri: item.local?.local_thumbnail || item.remote?.preview_url

View File

@ -1,18 +1,12 @@
import analytics from '@components/analytics'
import haptics from '@components/haptics'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useContext, useMemo } from 'react'
import {
Image,
Pressable,
SectionList,
StyleSheet,
Text,
View
} from 'react-native'
import { Pressable, SectionList, StyleSheet, Text, View } from 'react-native'
import FastImage from 'react-native-fast-image'
import ComposeContext from '../../utils/createContext'
import updateText from '../../updateText'
import analytics from '@components/analytics'
const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => {
const { composeState, composeDispatch } = useContext(ComposeContext)
@ -31,7 +25,7 @@ const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => {
haptics('Success')
}, [])
const children = useMemo(
() => <Image source={{ uri: emoji.url }} style={styles.emoji} />,
() => <FastImage source={{ uri: emoji.url }} style={styles.emoji} />,
[]
)
return (

View File

@ -10,11 +10,10 @@ import { useTimelineQuery } from '@utils/queryHooks/timeline'
import {
getLocalAccount,
getLocalActiveIndex,
getLocalNotification,
localUpdateNotification
getLocalNotification
} from '@utils/slices/instancesSlice'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useEffect, useMemo } from 'react'
import React, { useCallback, useMemo } from 'react'
import { Platform } from 'react-native'
import FastImage from 'react-native-fast-image'
import { useDispatch, useSelector } from 'react-redux'

View File

@ -1,4 +1,4 @@
import Constants from 'expo-constants'
import * as Updates from 'expo-updates'
import React from 'react'
import { ScrollView } from 'react-native-gesture-handler'
import SettingsAnalytics from './Settings/Analytics'
@ -15,7 +15,7 @@ const ScreenMeSettings: React.FC = () => {
{__DEV__ ||
['development'].some(channel =>
Constants.manifest.releaseChannel?.includes(channel)
Updates.releaseChannel.includes(channel)
) ? (
<SettingsDev />
) : null}

View File

@ -6,6 +6,7 @@ import {
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import Constants from 'expo-constants'
import * as Updates from 'expo-updates'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, Text } from 'react-native'
@ -31,7 +32,7 @@ const SettingsAnalytics: React.FC = () => {
<Text style={[styles.version, { color: theme.secondary }]}>
{t('content.version', {
version: Constants.manifest.version,
releaseChannel: Constants.manifest.releaseChannel || 'dev'
releaseChannel: Updates.releaseChannel
})}
</Text>
</MenuContainer>

View File

@ -6,7 +6,7 @@ import { useSearchQuery } from '@utils/queryHooks/search'
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import Constants from 'expo-constants'
import * as Updates from 'expo-updates'
import * as Linking from 'expo-linking'
import * as StoreReview from 'expo-store-review'
import * as WebBrowser from 'expo-web-browser'
@ -44,7 +44,7 @@ const SettingsTooot: React.FC = () => {
/>
{__DEV__ ||
['production', 'development'].some(channel =>
Constants.manifest.releaseChannel?.includes(channel)
Updates.releaseChannel?.includes(channel)
) ? (
<MenuRow
title={t('content.review.heading')}

View File

@ -7,7 +7,6 @@ import {
getLocalActiveIndex,
getLocalInstances,
InstanceLocal,
InstancesState,
localUpdateActiveIndex
} from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
@ -26,16 +25,11 @@ import { useQueryClient } from 'react-query'
import { useDispatch, useSelector } from 'react-redux'
interface Props {
index: NonNullable<InstancesState['local']['activeIndex']>
instance: InstanceLocal
disabled?: boolean
}
const AccountButton: React.FC<Props> = ({
index,
instance,
disabled = false
}) => {
const AccountButton: React.FC<Props> = ({ instance, disabled = false }) => {
const queryClient = useQueryClient()
const navigation = useNavigation()
const dispatch = useDispatch()
@ -51,7 +45,7 @@ const AccountButton: React.FC<Props> = ({
onPress={() => {
haptics('Light')
analytics('switch_existing_press')
dispatch(localUpdateActiveIndex(index))
dispatch(localUpdateActiveIndex(instance))
queryClient.clear()
navigation.goBack()
}}
@ -86,14 +80,20 @@ const ScreenMeSwitchRoot: React.FC = () => {
`${b.uri}${b.account.acct}`
)
)
.map((instance, index) => (
<AccountButton
key={index}
index={index}
instance={instance}
disabled={localActiveIndex === index}
/>
))
.map((instance, index) => {
const localAccount = localInstances[localActiveIndex!]
return (
<AccountButton
key={index}
instance={instance}
disabled={
instance.url === localAccount.url &&
instance.token === localAccount.token &&
instance.account.id === localAccount.account.id
}
/>
)
})
: null}
</View>
</View>

View File

@ -1,11 +1,7 @@
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useContext, useEffect } from 'react'
import { Dimensions, Image } from 'react-native'
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming
} from 'react-native-reanimated'
import React, { useContext } from 'react'
import { Dimensions } from 'react-native'
import FastImage from 'react-native-fast-image'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import AccountContext from './utils/createContext'
@ -15,39 +11,18 @@ export interface Props {
}
const AccountHeader: React.FC<Props> = ({ account, limitHeight = false }) => {
const { accountState, accountDispatch } = useContext(AccountContext)
const { accountState } = useContext(AccountContext)
const { theme } = useTheme()
const topInset = useSafeAreaInsets().top
const height = useSharedValue(
Dimensions.get('screen').width * accountState.headerRatio + topInset
)
const styleHeight = useAnimatedStyle(() => {
return {
height: withTiming(height.value)
}
})
useEffect(() => {
if (
account?.header &&
!account.header.includes('/headers/original/missing.png')
) {
Image.getSize(account.header, (width, height) => {
// if (!limitHeight) {
// accountDispatch({
// type: 'headerRatio',
// payload: height / width
// })
// }
})
}
}, [account])
return (
<Animated.Image
<FastImage
source={{ uri: account?.header }}
style={[styleHeight, { backgroundColor: theme.disabled }]}
style={{
height:
Dimensions.get('screen').width * accountState.headerRatio + topInset,
backgroundColor: theme.disabled
}}
/>
)
}

View File

@ -88,7 +88,9 @@ const styles = StyleSheet.create({
}
})
export default React.memo(
AccountInformation,
(_, next) => next.account === undefined
)
export default React.memo(AccountInformation, (prev, next) => {
let skipUpdate = true
skipUpdate = prev.account?.id === next.account?.id
skipUpdate = prev.account?.acct === next.account?.acct
return skipUpdate
})

View File

@ -3,7 +3,7 @@ import GracefullyImage from '@components/GracefullyImage'
import { useNavigation } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import { StyleConstants } from '@utils/styles/constants'
import React, { useMemo } from 'react'
import React from 'react'
import { Pressable, StyleSheet } from 'react-native'
export interface Props {
@ -15,35 +15,33 @@ const AccountInformationAvatar: React.FC<Props> = ({ account, myInfo }) => {
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const dimension = useMemo(
() => ({
width: StyleConstants.Avatar.L,
height: StyleConstants.Avatar.L
}),
[]
)
return (
<Pressable
disabled={!myInfo}
onPress={() => {
analytics('account_avatar_press')
myInfo &&
account &&
navigation.push('Tab-Shared-Account', { account })
myInfo && account && navigation.push('Tab-Shared-Account', { account })
}}
style={styles.base}
>
<GracefullyImage
style={styles.base}
key={account?.avatar}
style={styles.image}
uri={{ original: account?.avatar || '' }}
dimension={dimension}
/>
</Pressable>
)
}
const styles = StyleSheet.create({
base: { borderRadius: 8, overflow: 'hidden' }
base: {
borderRadius: 8,
overflow: 'hidden',
width: StyleConstants.Avatar.L,
height: StyleConstants.Avatar.L
},
image: { flex: 1 }
})
export default AccountInformationAvatar

View File

@ -1,18 +1,17 @@
import Constants from 'expo-constants'
import * as Updates from 'expo-updates'
import * as Sentry from 'sentry-expo'
import log from './log'
const sentry = () => {
log('log', 'Sentry', 'initializing')
Sentry.init({
environment: Constants.manifest.extra.sentryEnv,
dsn: Constants.manifest.extra.sentryDSN,
environment: Constants.manifest.extra.toootEnvironment,
enableInExpoDevelopment: false,
debug:
__DEV__ ||
['development'].some(channel =>
Constants.manifest.releaseChannel?.includes(channel)
)
['development'].some(channel => Updates.releaseChannel.includes(channel))
})
}

View File

@ -1,6 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { RootState } from '@root/store'
import Constants from 'expo-constants'
import * as Updates from 'expo-updates'
import * as StoreReview from 'expo-store-review'
export const supportedLngs = ['zh-Hans', 'en']
@ -38,7 +38,7 @@ const contextsSlice = createSlice({
initialState: contextsInitialState as ContextsState,
reducers: {
updateStoreReview: (state, action: PayloadAction<1>) => {
if (Constants.manifest.releaseChannel?.includes('production')) {
if (Updates.releaseChannel.includes('production')) {
state.storeReview.current = state.storeReview.current + action.payload
if (state.storeReview.current === state.storeReview.context) {
StoreReview.isAvailableAsync().then(() => StoreReview.requestReview())

View File

@ -175,15 +175,13 @@ const instancesSlice = createSlice({
name: 'instances',
initialState: instancesInitialState,
reducers: {
localUpdateActiveIndex: (
state,
action: PayloadAction<NonNullable<InstancesState['local']['activeIndex']>>
) => {
if (action.payload < state.local.instances.length) {
state.local.activeIndex = action.payload
} else {
throw new Error('Set index cannot be found')
}
localUpdateActiveIndex: (state, action: PayloadAction<InstanceLocal>) => {
state.local.activeIndex = state.local.instances.findIndex(
instance =>
instance.url === action.payload.url &&
instance.token === action.payload.token &&
instance.account.id === action.payload.account.id
)
},
localUpdateAccount: (
state,