Merge branch 'main' into release

This commit is contained in:
xmflsct 2023-07-16 23:10:35 +02:00
commit 24c099b427
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
63 changed files with 4403 additions and 3693 deletions

View File

@ -20,7 +20,7 @@ jobs:
- run: bundle install
- run: yarn app:build ios
env:
DEVELOPER_DIR: /Applications/Xcode_14.3.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_14.3.1.app/Contents/Developer
ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
SENTRY_ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
LC_ALL: en_US.UTF-8

View File

@ -0,0 +1,50 @@
diff --git a/src/android.js b/src/android.js
index fbf09855771f985c5edfc53c22cf6cfe828f45f9..7751d456e08e2dc4c78601fc9430fdbf1373e0d4 100644
--- a/src/android.js
+++ b/src/android.js
@@ -16,7 +16,7 @@ import TextAncestor from 'react-native/Libraries/Text/TextAncestor';
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';
import invariant from 'invariant';
import nullthrows from 'nullthrows';
-import setAndForwardRef from 'react-native/Libraries/Utilities/setAndForwardRef';
+import setAndForwardRef from './setAndForwardRef';
import usePressability from 'react-native/Libraries/Pressability/usePressability';
diff --git a/src/ios.tsx b/src/ios.tsx
index b9ed28bbf9fca6fb44c27096e771d8d2b65b858f..588a75c82b2ee1123d3e48acb984bcbc8b293cc8 100644
--- a/src/ios.tsx
+++ b/src/ios.tsx
@@ -11,7 +11,7 @@ import {
} from 'react-native';
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';
import TextAncestor from 'react-native/Libraries/Text/TextAncestor';
-import setAndForwardRef from 'react-native/Libraries/Utilities/setAndForwardRef';
+import setAndForwardRef from './setAndForwardRef';
import { getTextInputExtraProps } from './extra_props';
import type {
diff --git a/src/setAndForwardRef.js b/src/setAndForwardRef.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad7777e271b945c7123953f7578a8d1208ca9e48
--- /dev/null
+++ b/src/setAndForwardRef.js
@@ -0,0 +1,17 @@
+function setAndForwardRef({ getForwardedRef, setLocalRef }) {
+ return function forwardRef(ref) {
+ const forwardedRef = getForwardedRef();
+
+ setLocalRef(ref);
+
+ // Forward to user ref prop (if one has been specified)
+ if (typeof forwardedRef === 'function') {
+ // Handle function-based refs. String-based refs are handled as functions.
+ forwardedRef(ref);
+ } else if (typeof forwardedRef === 'object' && forwardedRef != null) {
+ // Handle createRef-based refs
+ forwardedRef.current = ref;
+ }
+ };
+}
+export default setAndForwardRef;
\ No newline at end of file

View File

@ -0,0 +1,74 @@
diff --git a/Libraries/Utilities/setAndForwardRef.js b/Libraries/Utilities/setAndForwardRef.js
new file mode 100644
index 0000000000000000000000000000000000000000..e67b530b9a933b68e219e2b8cec2a66dc8f51323
--- /dev/null
+++ b/Libraries/Utilities/setAndForwardRef.js
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @flow
+ */
+
+'use strict'
+
+import type { ElementRef, Ref } from 'react'
+
+type Args = $ReadOnly<{|
+ getForwardedRef: () => ?Ref<any>,
+ setLocalRef: (ref: ElementRef<any>) => mixed
+|}>
+
+/**
+ * This is a helper function for when a component needs to be able to forward a ref
+ * to a child component, but still needs to have access to that component as part of
+ * its implementation.
+ *
+ * Its main use case is in wrappers for native components.
+ *
+ * Usage:
+ *
+ * class MyView extends React.Component {
+ * _nativeRef = null;
+ *
+ * _setNativeRef = setAndForwardRef({
+ * getForwardedRef: () => this.props.forwardedRef,
+ * setLocalRef: ref => {
+ * this._nativeRef = ref;
+ * },
+ * });
+ *
+ * render() {
+ * return <View ref={this._setNativeRef} />;
+ * }
+ * }
+ *
+ * const MyViewWithRef = React.forwardRef((props, ref) => (
+ * <MyView {...props} forwardedRef={ref} />
+ * ));
+ *
+ * module.exports = MyViewWithRef;
+ */
+
+function setAndForwardRef({ getForwardedRef, setLocalRef }: Args): (ref: ElementRef<any>) => void {
+ return function forwardRef(ref: ElementRef<any>) {
+ const forwardedRef = getForwardedRef()
+
+ setLocalRef(ref)
+
+ // Forward to user ref prop (if one has been specified)
+ if (typeof forwardedRef === 'function') {
+ // Handle function-based refs. String-based refs are handled as functions.
+ forwardedRef(ref)
+ } else if (typeof forwardedRef === 'object' && forwardedRef != null) {
+ // Handle createRef-based refs
+ forwardedRef.current = ref
+ }
+ }
+}
+
+module.exports = setAndForwardRef

View File

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

View File

@ -8,7 +8,7 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.1)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
@ -16,20 +16,20 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.722.0)
aws-sdk-core (3.170.0)
aws-partitions (1.785.0)
aws-sdk-core (3.178.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.63.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (1.71.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.119.1)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-s3 (1.129.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-sigv4 (~> 1.6)
aws-sigv4 (1.6.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
@ -76,7 +76,7 @@ GEM
highline (~> 2.0.0)
concurrent-ruby (1.2.2)
declarative (0.0.20)
digest-crc (0.6.4)
digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
@ -85,7 +85,7 @@ GEM
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
excon (0.99.0)
excon (0.100.0)
faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
@ -114,8 +114,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.212.1)
fastimage (2.2.7)
fastlane (2.213.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@ -139,7 +139,7 @@ GEM
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
@ -163,7 +163,7 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.35.0)
google-apis-androidpublisher_v3 (0.45.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.0)
addressable (~> 2.5, >= 2.5.1)
@ -194,7 +194,7 @@ GEM
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.3.0)
googleauth (1.6.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@ -209,14 +209,14 @@ GEM
concurrent-ruby (~> 1.0)
jmespath (1.6.2)
json (2.6.3)
jwt (2.7.0)
jwt (2.7.1)
memoist (0.16.2)
mini_magick (4.12.0)
mini_mime (1.1.2)
minitest (5.18.0)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.0.0)
multipart-post (2.3.0)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
@ -280,7 +280,7 @@ PLATFORMS
arm64-darwin-22
DEPENDENCIES
cocoapods
cocoapods (~> 1.12)
fastlane
fastlane-plugin-json
fastlane-plugin-sentry

View File

@ -2,8 +2,6 @@ apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
apply plugin: 'com.google.gms.google-services'
import com.android.build.OutputFile
/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
@ -15,8 +13,8 @@ react {
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
// codegenDir = file("../node_modules/react-native-codegen")
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
// codegenDir = file("../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js")
/* Variants */
@ -51,14 +49,6 @@ react {
// hermesFlags = ["-O", "-output-source-map"]
}
/**
* Set this to true to create four separate APKs instead of one,
* one for each native architecture. This is useful if you don't
* use App Bundles (https://developer.android.com/guide/app-bundle/)
* and want to have separate APKs to upload to the Play Store.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
@ -77,16 +67,6 @@ def enableProguardInReleaseBuilds = false
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Private function to get the list of Native Architectures you want to build.
* This reads the value from reactNativeArchitectures in your gradle.properties
* file and works together with the --active-arch-only flag of react-native run-android.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
android {
ndkVersion rootProject.ext.ndkVersion
@ -105,14 +85,6 @@ android {
versionCode 50
versionName "0.2"
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include (*reactNativeArchitectures())
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
@ -133,28 +105,9 @@ android {
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
}
}
dependencies {
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0") {
force = true
}
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
@ -182,7 +135,6 @@ dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.squareup.okhttp3', module:'okhttp'

View File

@ -1,4 +1,5 @@
package com.xmflsct.app.tooot;
import expo.modules.ReactActivityDelegateWrapper;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
@ -30,13 +31,10 @@ public class MainActivity extends ReactActivity {
*/
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new DefaultReactActivityDelegate(
return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, new DefaultReactActivityDelegate(
this,
getMainComponentName(),
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
);
DefaultNewArchitectureEntryPoint.getFabricEnabled()));
}
}

View File

@ -20,7 +20,7 @@ import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper(
this,
new DefaultReactNativeHost(this) {
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
@ -48,7 +48,7 @@ public class MainApplication extends Application implements ReactApplication {
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
});
}));
@Override
public ReactNativeHost getReactNativeHost() {
@ -65,5 +65,12 @@ public class MainApplication extends Application implements ReactApplication {
DefaultNewArchitectureEntryPoint.load();
}
// ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
}
}

View File

@ -7,6 +7,7 @@ buildscript {
compileSdkVersion = 33
targetSdkVersion = 33
kotlinVersion = '1.8.21'
frescoVersion = '2.5.0'
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
@ -17,7 +18,7 @@ buildscript {
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:7.4.1")
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath 'com.google.gms:google-services:4.3.14'
}

View File

@ -26,7 +26,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.176.1
FLIPPER_VERSION=0.182.0
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using

Binary file not shown.

View File

@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

64
android/gradlew vendored
View File

@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -17,14 +17,56 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
@ -37,6 +79,7 @@ do
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
@ -73,6 +116,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -122,7 +166,9 @@ fi
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
@ -152,11 +198,19 @@ fi
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
@ -183,4 +237,4 @@ eval "set -- $(
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
exec "$JAVACMD" "$@"

17
android/gradlew.bat vendored
View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -69,20 +69,23 @@ goto fail
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:omega

View File

@ -1,11 +1,10 @@
rootProject.name = 'tooot'
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute().text.trim(), "../scripts/autolinking.gradle");
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
useExpoModules()
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute().text.trim(), "../native_modules.gradle");
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile())

View File

@ -1,26 +1,27 @@
module.exports = function (api) {
api.cache(false)
const plugins = [
'@babel/plugin-proposal-optional-chaining',
[
'module-resolver',
{
root: ['./'],
alias: {
'@components': './src/components',
'@i18n': './src/i18n',
'@screens': './src/screens',
'@utils': './src/utils'
return {
presets: ['babel-preset-expo'],
plugins: [
'@babel/plugin-proposal-optional-chaining',
[
'module-resolver',
{
root: ['./'],
alias: {
'@components': './src/components',
'@i18n': './src/i18n',
'@screens': './src/screens',
'@utils': './src/utils'
}
}
}
],
'react-native-reanimated/plugin'
]
if (process.env.NODE_ENV === 'production' || process.env.BABEL_ENV === 'production') {
plugins.push('transform-remove-console')
]
].concat(
process.env.NODE_ENV === 'production' || process.env.BABEL_ENV === 'production'
? ['transform-remove-console']
: [],
['react-native-reanimated/plugin']
)
}
return { presets: ['babel-preset-expo'], plugins }
}

View File

@ -1,2 +1,4 @@
Enjoy toooting! This version includes following improvements and fixes:
- Fixed functionality version check (e.g. cannot edit own toots)
- Supports mute duration
- Long press to copy toot
- Button to fetch latest on load

View File

@ -1,2 +1,5 @@
tooot-ing愉快此版本包括以下改进和修复
- 修复版本功能检查(如无法编辑嘟文)
- 新增neodb.social演出卡片
- 支持选择隐藏用户时限
- 长按复制嘟文
- 新增获取最新嘟文按钮

View File

@ -1,6 +1,11 @@
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
platform :ios, '13.0'
prepare_react_native_project!
@ -33,8 +38,7 @@ target 'tooot' do
post_install do |installer|
react_native_post_install(
installer,
# Set `mac_catalyst_enabled` to `true` in order to apply patches
# necessary for Mac Catalyst builds
config[:reactNativePath],
:mac_catalyst_enabled => false
)
__apply_Xcode_12_5_M1_post_install_workaround(installer)

File diff suppressed because it is too large Load Diff

View File

@ -225,6 +225,7 @@
buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
395686AEA3960C8699AE1CAD /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
@ -385,6 +386,25 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
395686AEA3960C8699AE1CAD /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-tooot/expo-configure-project.sh\"\n";
};
49D30A53634620EF2A5C6692 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -697,6 +717,8 @@
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
@ -753,6 +775,8 @@
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"\"";
MTL_ENABLE_DEBUG_INFO = NO;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;

View File

@ -22,16 +22,6 @@
#endif
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
return true;
}
// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
NSString *urlString = url.absoluteString;

View File

@ -1,3 +1,11 @@
module.exports = {
transformer: { inlineRequires: true }
}
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {};
module.exports = mergeConfig(getDefaultConfig(__dirname), config)

View File

@ -1,6 +1,6 @@
{
"name": "tooot",
"version": "4.9.6",
"version": "4.10.0",
"description": "tooot for Mastodon",
"author": "xmflsct <me@xmflsct.com>",
"license": "GPL-3.0-or-later",
@ -19,95 +19,96 @@
},
"dependencies": {
"@expo/react-native-action-sheet": "^4.0.1",
"@formatjs/intl-datetimeformat": "^6.8.0",
"@formatjs/intl-getcanonicallocales": "^2.2.0",
"@formatjs/intl-locale": "^3.3.0",
"@formatjs/intl-numberformat": "^8.5.0",
"@formatjs/intl-pluralrules": "^5.2.2",
"@formatjs/intl-relativetimeformat": "^11.2.2",
"@formatjs/intl-datetimeformat": "^6.10.0",
"@formatjs/intl-getcanonicallocales": "^2.2.1",
"@formatjs/intl-locale": "^3.3.2",
"@formatjs/intl-numberformat": "^8.7.0",
"@formatjs/intl-pluralrules": "^5.2.4",
"@formatjs/intl-relativetimeformat": "^11.2.4",
"@mattermost/react-native-paste-input": "^0.6.2",
"@neverdull-agency/expo-unlimited-secure-store": "^1.0.10",
"@react-native-async-storage/async-storage": "~1.17.11",
"@react-native-camera-roll/camera-roll": "^5.4.0",
"@react-native-camera-roll/camera-roll": "^5.7.2",
"@react-native-clipboard/clipboard": "^1.11.2",
"@react-native-community/blur": "^4.3.2",
"@react-native-community/netinfo": "9.3.10",
"@react-native-firebase/app": "^17.5.0",
"@react-native-menu/menu": "^0.7.3",
"@react-native-segmented-control/segmented-control": "^2.4.1",
"@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"@react-navigation/stack": "^6.3.16",
"@sentry/react-native": "5.5.0",
"@sharcoux/slider": "^6.1.2",
"@tanstack/react-query": "^4.29.7",
"@react-native-community/netinfo": "^9.4.1",
"@react-native-firebase/app": "^18.1.0",
"@react-native-menu/menu": "^0.8.0",
"@react-native-segmented-control/segmented-control": "^2.4.2",
"@react-navigation/bottom-tabs": "^6.5.8",
"@react-navigation/native": "^6.1.7",
"@react-navigation/native-stack": "^6.9.13",
"@react-navigation/stack": "^6.3.17",
"@sentry/react-native": "^5.7.1",
"@sharcoux/slider": "^7.0.1",
"@tanstack/react-query": "^4.29.19",
"axios": "^1.4.0",
"diff": "^5.1.0",
"expo": "48.0.17",
"expo-auth-session": "^4.1.0",
"expo-av": "^13.3.0",
"expo-constants": "^14.3.0",
"expo-crypto": "^12.3.0",
"expo-file-system": "^15.3.0",
"expo-haptics": "^12.3.0",
"expo-image": "^1.2.3",
"expo-linking": "^4.1.0",
"expo-localization": "^14.2.0",
"expo-notifications": "^0.18.1",
"expo-screen-capture": "5.1.1",
"expo-screen-orientation": "^5.2.0",
"expo-secure-store": "^12.1.1",
"expo-splash-screen": "^0.18.2",
"expo-store-review": "^6.3.0",
"expo-video-thumbnails": "^7.3.0",
"expo-web-browser": "~12.1.1",
"expo": "^49.0.3",
"expo-auth-session": "^5.0.2",
"expo-av": "^13.4.1",
"expo-constants": "^14.4.2",
"expo-crypto": "^12.4.1",
"expo-file-system": "^15.4.2",
"expo-haptics": "^12.4.0",
"expo-image": "^1.3.2",
"expo-linking": "^5.0.2",
"expo-localization": "^14.3.0",
"expo-notifications": "^0.20.1",
"expo-screen-capture": "^5.3.0",
"expo-screen-orientation": "^6.0.3",
"expo-secure-store": "^12.3.1",
"expo-splash-screen": "^0.20.4",
"expo-store-review": "^6.4.0",
"expo-video-thumbnails": "^7.4.0",
"expo-web-browser": "^12.3.2",
"htmlparser2": "^9.0.0",
"i18next": "^22.5.0",
"i18next": "^23.2.11",
"linkify-it": "^4.0.1",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^12.3.1",
"react-intl": "^6.4.2",
"react-native": "^0.71.8",
"react-i18next": "^13.0.2",
"react-intl": "^6.4.4",
"react-native": "^0.72.3",
"react-native-flash-message": "^0.4.1",
"react-native-gesture-handler": "~2.10.1",
"react-native-image-picker": "^5.3.1",
"react-native-gesture-handler": "~2.12.0",
"react-native-image-picker": "^5.6.0",
"react-native-ios-context-menu": "^1.15.3",
"react-native-language-detection": "^0.2.2",
"react-native-mmkv": "~2.8.0",
"react-native-mmkv": "^2.10.1",
"react-native-pager-view": "^6.2.0",
"react-native-quick-base64": "^2.0.6",
"react-native-reanimated": "^3.1.0",
"react-native-reanimated": "^3.3.0",
"react-native-reanimated-zoom": "^0.3.3",
"react-native-safe-area-context": "^4.5.3",
"react-native-screens": "^3.20.0",
"react-native-safe-area-context": "^4.7.1",
"react-native-screens": "^3.22.1",
"react-native-share-menu": "^6.0.0",
"react-native-svg": "^13.9.0",
"react-native-svg": "^13.10.0",
"react-native-swipe-list-view": "^3.2.9",
"react-native-tab-view": "^3.5.1",
"react-native-tab-view": "^3.5.2",
"rn-placeholder": "^3.0.3",
"zeego": "^1.6.1"
"zeego": "^1.6.2"
},
"devDependencies": {
"@babel/core": "^7.21.8",
"@babel/core": "^7.22.8",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/preset-typescript": "^7.21.5",
"@expo/config": "^8.0.4",
"@babel/preset-typescript": "^7.22.5",
"@expo/config": "^8.1.2",
"@react-native/metro-config": "^0.72.9",
"@types/diff": "^5.0.3",
"@types/linkify-it": "^3.0.2",
"@types/lodash": "^4.14.194",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
"@types/lodash": "^4.14.195",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.7",
"@types/react-native-share-menu": "^5.0.2",
"babel-plugin-module-resolver": "^5.0.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"chalk": "^4.1.2",
"deprecated-react-native-prop-types": "^4.1.0",
"dotenv": "^16.0.3",
"dotenv": "^16.3.1",
"react-native-clean-project": "^4.0.1",
"typescript": "^5.0.4"
"typescript": "^5.1.6"
},
"packageManager": "yarn@3.3.1",
"resolutions": {
@ -115,6 +116,8 @@
"react-native-share-menu@^6.0.0": "patch:react-native-share-menu@npm%3A6.0.0#./.yarn/patches/react-native-share-menu-npm-6.0.0-f1094c3204.patch",
"@types/react-native-share-menu@^5.0.2": "patch:@types/react-native-share-menu@npm%3A5.0.2#./.yarn/patches/@types-react-native-share-menu-npm-5.0.2-373df17ecc.patch",
"react-native-ios-context-menu@^1.15.1": "patch:react-native-ios-context-menu@npm%3A1.15.1#./.yarn/patches/react-native-ios-context-menu-npm-1.15.1-0034bfa5ba.patch",
"react-native-reanimated-zoom@^0.3.3": "patch:react-native-reanimated-zoom@npm%3A0.3.3#./.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch"
"react-native-reanimated-zoom@^0.3.3": "patch:react-native-reanimated-zoom@npm%3A0.3.3#./.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch",
"react-native@^0.72.0": "patch:react-native@npm%3A0.72.0#./.yarn/patches/react-native-npm-0.72.0-66f5fd62b3.patch",
"@mattermost/react-native-paste-input@^0.6.2": "patch:@mattermost/react-native-paste-input@npm%3A0.6.2#./.yarn/patches/@mattermost-react-native-paste-input-npm-0.6.2-e109419dfb.patch"
}
}

View File

@ -17,6 +17,7 @@ export type Props = {
loading?: boolean
disabled?: boolean
destructive?: boolean
destructiveColor?: string
onPress: () => void
} & ({ type?: undefined; content: IconName } | { type: 'text'; content: string })
@ -34,6 +35,7 @@ const HeaderRight: React.FC<Props> = ({
loading,
disabled,
destructive = false,
destructiveColor,
onPress
}) => {
const { colors } = useTheme()
@ -57,7 +59,7 @@ const HeaderRight: React.FC<Props> = ({
color: disabled
? colors.secondary
: destructive
? colors.red
? destructiveColor || colors.red
: colors.primaryDefault,
opacity: loading ? 0 : 1
}}

View File

@ -16,18 +16,19 @@ export type Props = {
export const CardNeodb: React.FC<Props> = ({ card }) => {
const { colors } = useTheme()
const segments = Linking.parse(card.url).path?.split('/')
if (!segments || !['movie', 'book', 'tv', 'game', 'album', 'podcast'].includes(segments[0]))
const path = Linking.parse(card.url).path
if (!path) return null
const segments = path?.split('/')
if (
!segments ||
!['movie', 'book', 'tv', 'game', 'album', 'podcast', 'performance'].includes(segments[0])
)
return null
const [headingLines, setHeadingLines] = useState(3)
const { data } = useNeodbQuery({
path:
segments[0] === 'tv' && segments[1] === 'season'
? `${segments[0]}${segments[1]}/${segments[2]}`
: `${segments[0]}/${segments[1]}`
})
const { data } = useNeodbQuery({ path })
if (!data) return null
@ -44,7 +45,13 @@ export const CardNeodb: React.FC<Props> = ({ card }) => {
>
{data.cover_image_url ? (
<GracefullyImage
sources={{ default: { uri: data.cover_image_url } }}
sources={{
default: {
uri: data.cover_image_url.startsWith('/')
? `https://neodb.social${data.cover_image_url}`
: data.cover_image_url
}
}}
dimension={{
width: StyleConstants.Font.LineHeight.M * 4,
height: StyleConstants.Font.LineHeight.M * 5
@ -162,6 +169,27 @@ export const CardNeodb: React.FC<Props> = ({ card }) => {
return (
<Content heading={[data.title]} details={[data.hosts.join(' '), data.genre.join(' ')]} />
)
case 'performance':
if (segments[1] === 'production') {
return (
<Content
heading={[data.display_title]}
details={[
data.opening_date,
data.director.join(' '),
data.playwright.join(' '),
data.composer.join(' ')
]}
/>
)
} else {
return (
<Content
heading={[data.title, data.orig_title]}
details={[data.genre.join(' '), data.playwright.join(' '), data.director.join(' ')]}
/>
)
}
default:
return null
}

View File

@ -36,6 +36,7 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
adaptiveSize
numberOfLines={999}
color={suppressSpoiler ? colors.disabled : undefined}
selectable
/>
{inThread ? (
<CustomText
@ -62,6 +63,7 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
}
expandHint={t('shared.content.expandHint')}
setSpoilerExpanded={setSpoilerExpanded}
selectable
/>
</>
) : (
@ -70,6 +72,7 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
size={highlighted ? 'L' : 'M'}
adaptiveSize
numberOfLines={highlighted || inThread ? 999 : notificationOwnToot ? 2 : undefined}
selectable
/>
)}
</View>

View File

@ -1,3 +1,4 @@
import Icon from '@components/Icon'
import ComponentSeparator from '@components/Separator'
import CustomText from '@components/Text'
import TimelineDefault from '@components/Timeline/Default'
@ -10,12 +11,20 @@ import {
setAccountStorage,
useGlobalStorageListener
} from '@utils/storage/actions'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import { StyleConstants } from '@utils/styles/constants'
import { throttle } from 'lodash'
import React, { RefObject, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, FlatListProps, Platform, RefreshControl } from 'react-native'
import {
FlatList,
FlatListProps,
Platform,
Pressable,
RefreshControl,
StyleProp,
ViewStyle
} from 'react-native'
import Animated, {
Easing,
runOnJS,
@ -127,6 +136,27 @@ const Timeline: React.FC<Props> = ({
transform: [{ translateY: fetchedNoticeTop.value }]
}))
const refetchedNoticeBottom = useDerivedValue(() => {
if (firstLoad.value) {
return withSequence(
withTiming(0),
withDelay(
3000,
withTiming(fetchedNoticeHeight.value + 32, { easing: Easing.out(Easing.ease) })
)
)
} else {
return fetchedNoticeHeight.value + 32
}
}, [])
const refetchedNoticeAnimate = useAnimatedStyle(() => ({
transform: [
{
translateY: refetchedNoticeBottom.value
}
]
}))
const scrollY = useSharedValue(0)
const fetchingType = useSharedValue<0 | 1 | 2>(0)
@ -169,10 +199,9 @@ const Timeline: React.FC<Props> = ({
throttle(() => {
if (readMarker) {
const currentMarker = getAccountStorage.string(readMarker) || '0'
// setAccountStorage([{ key: readMarker, value: '108425743226508521' }])
if (latestMarker.current > currentMarker) {
setAccountStorage([{ key: readMarker, value: latestMarker.current }])
} else {
// setAccountStorage([{ key: readMarker, value: '105250709762254246' }])
}
}
}, 1000 * 15),
@ -242,6 +271,18 @@ const Timeline: React.FC<Props> = ({
flRef.current?.scrollToOffset({ offset: 0, animated: false })
)
const noticeDefaults: StyleProp<Animated.AnimateStyle<StyleProp<ViewStyle>>> = {
position: 'absolute',
alignSelf: 'center',
borderRadius: 99,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: colors.backgroundDefault,
shadowColor: colors.primaryDefault,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: theme === 'light' ? 0.16 : 0.24
}
return (
<>
<TimelineRefresh
@ -286,42 +327,78 @@ const Timeline: React.FC<Props> = ({
{...customProps}
/>
{!disableRefresh ? (
<Animated.View
style={[
{
position: 'absolute',
alignSelf: 'center',
top: -fetchedNoticeHeight.value - 16,
paddingVertical: StyleConstants.Spacing.S,
paddingHorizontal: StyleConstants.Spacing.M,
backgroundColor: colors.backgroundDefault,
shadowColor: colors.primaryDefault,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: theme === 'light' ? 0.16 : 0.24,
borderRadius: 99,
justifyContent: 'center',
alignItems: 'center'
},
fetchedNoticeAnimate
]}
onLayout={({
nativeEvent: {
layout: { height }
}
}) => (fetchedNoticeHeight.value = height)}
>
<CustomText
fontStyle='S'
style={{ color: colors.primaryDefault }}
children={
fetchedCount !== null
? fetchedCount > 0
? t('refresh.fetched.found', { count: fetchedCount })
: t('refresh.fetched.none')
: t('refresh.fetching')
}
/>
</Animated.View>
<>
<Animated.View
style={[
{
top: -fetchedNoticeHeight.value - 16,
paddingVertical: StyleConstants.Spacing.S,
paddingHorizontal: StyleConstants.Spacing.M,
...noticeDefaults
},
fetchedNoticeAnimate
]}
onLayout={({
nativeEvent: {
layout: { height }
}
}) => (fetchedNoticeHeight.value = height)}
>
<CustomText
fontStyle='S'
style={{ color: colors.primaryDefault }}
children={
fetchedCount !== null
? fetchedCount > 0
? t('refresh.fetched.found', { count: fetchedCount })
: t('refresh.fetched.none')
: t('refresh.fetching')
}
/>
</Animated.View>
{readMarker ? (
<Animated.View
style={[
{
bottom: 16,
borderColor: colors.primaryDefault,
borderWidth: 0.5,
...noticeDefaults
},
refetchedNoticeAnimate
]}
>
<Pressable
style={{
flexDirection: 'row',
alignItems: 'center',
gap: StyleConstants.Spacing.S,
paddingVertical: StyleConstants.Spacing.S,
paddingHorizontal: StyleConstants.Spacing.M
}}
onPress={async () => {
if (readMarker) {
setAccountStorage([{ key: readMarker, value: undefined }])
}
flRef.current?.scrollToOffset({ offset: 0 })
await refetch()
}}
>
<CustomText
fontStyle='M'
style={{ color: colors.primaryDefault }}
children={t('refresh.refetch')}
/>
<Icon
name='log-in'
color={colors.primaryDefault}
size={StyleConstants.Font.Size.M}
style={{ transform: [{ rotate: '-90deg' }] }}
/>
</Pressable>
</Animated.View>
) : null}
</>
) : null}
</>
)

View File

@ -4,6 +4,7 @@ import { useNavigation } from '@react-navigation/native'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { useQueryClient } from '@tanstack/react-query'
import apiInstance from '@utils/api/instance'
import { featureCheck } from '@utils/helpers/featureCheck'
import { checkIsMyAccount } from '@utils/helpers/isMyAccount'
import { TabSharedStackParamList, useNavState } from '@utils/navigation/navigators'
import { useAccountQuery } from '@utils/queryHooks/account'
@ -203,13 +204,22 @@ const menuAccount = ({
type: 'item',
key: 'account-mute',
props: {
onSelect: () =>
actualAccount &&
timelineMutation.mutate({
type: 'updateAccountProperty',
id: actualAccount.id,
payload: { property: 'mute', currentValue: data?.muting }
}),
onSelect: () => {
if (actualAccount) {
if (data?.muting !== true) {
if (featureCheck('mute_duration')) {
navigation.navigate('Tab-Shared-Mute', { account: actualAccount })
return
}
}
timelineMutation.mutate({
type: 'updateAccountProperty',
id: actualAccount.id,
payload: { property: 'mute', currentValue: data?.muting }
})
}
},
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
destructive: false,
hidden: false

View File

@ -390,8 +390,8 @@
"accessibilityHint": "Вы можаце ігнараваць, блакіраваць або абагуліць гэтага карыстальніка"
},
"followed_by": " падпісаны на вас",
"privateNote": "",
"moved": "",
"privateNote": "Задаць прыватную нататку",
"moved": "Карыстальнік перанесены",
"created_at": "Далучыўся: {{date}}",
"summary": {
"statuses_count": "{{count}} допісаў"
@ -412,11 +412,25 @@
},
"filter": {
"name": "Дадаць у фільтр",
"existed": ""
"existed": "Існаваў у гэтых фільтрах"
},
"history": {
"name": "Гісторыя рэдагавання"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "Бестэрмінова",
"1800": "30 хвілін",
"3600": "1 гадзіна",
"86400": "1 дзень",
"604800": "1 тыдзень"
}
},
"report": {
"name": "Паскардзіцца на {{acct}}",
"report": "Скарга",
@ -468,7 +482,7 @@
"name": "Абмеркаванні",
"remoteFetch": {
"title": "Змяшчае аддаленае змесціва",
"message": ""
"message": "Федэратыўны кантэнт не заўсёды даступны на лакальным серверы. Гэты кантэнт атрымліваецца з аддаленага сервера і мае пазнаку. Вы можаце ўзаемадзейнічаць з гэтым кантэнтам як звычайна."
}
},
"users": {

View File

@ -417,6 +417,20 @@
"history": {
"name": "Edita l'historial"
},
"mute": {
"name": "Silencia {{acct}}",
"mute": "Silencia",
"description": "Amaga publicacions i mencions d'aquest usuari, però encara podrà veure les teves publicacions i seguir-te.",
"notification": "També amaga notificacions d'aquest usuari",
"duration": {
"heading": "D'una durada",
"0": "Indefinida",
"1800": "30 minuts",
"3600": "1 hora",
"86400": "1 dia",
"604800": "1 setmana"
}
},
"report": {
"name": "Denúncia a {{acct}}",
"report": "Denúncia",

View File

@ -417,6 +417,20 @@
"history": {
"name": ""
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "",
"report": "",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Bearbeitungsverlauf"
},
"mute": {
"name": "@{{acct}} stummschalten",
"mute": "Stummschalten",
"description": "Verstecke Tröts des Users und solche, in denen das Konto erwähnt wird. Die Person wird weiterhin deine Beiträge lesen und dir folgen können.",
"notification": "Benachrichtigungen dieses Profils ebenfalls ausblenden",
"duration": {
"heading": "Für die Dauer",
"0": "Dauerhaft",
"1800": "30 Minuten",
"3600": "1 Stunde",
"86400": "1 Tag",
"604800": "1 Woche"
}
},
"report": {
"name": "{{acct}} melden",
"report": "Melden",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Ιστορικό επεξεργασίας"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "Αναφορά {{acct}}",
"report": "Αναφορά",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Edit History"
},
"mute": {
"name": "Mute {{acct}}",
"mute": "Mute",
"description": "Hide posts from this user and posts mentioning them, but it will still allow them to see your posts and follow you.",
"notification": "Also hide notifications from this user",
"duration": {
"heading": "For duration",
"0": "Indefinitely",
"1800": "30 minutes",
"3600": "1 hour",
"86400": "1 day",
"604800": "1 week"
}
},
"report": {
"name": "Report {{acct}}",
"report": "Report",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Historial de ediciones"
},
"mute": {
"name": "Silenciar a {{acct}}",
"mute": "Silenciar",
"description": "Oculta publicaciones y menciones a este usuario, pero podrá ver tus publicaciones y seguirte.",
"notification": "También oculta notificaciones de este usuario",
"duration": {
"heading": "Durante",
"0": "Indefinidamente",
"1800": "30 minutos",
"3600": "1 hora",
"86400": "1 día",
"604800": "1 semana"
}
},
"report": {
"name": "Denuncia {{acct}}",
"report": "Denuncia",

View File

@ -417,6 +417,20 @@
"history": {
"name": ""
},
"mute": {
"name": "Mututu {{acct}}",
"mute": "Mututu",
"description": "Ezkutatu erabiltzaile honen argitalpenak berari aipatzen dieten argitalpenak, hala ere, berak zure argitalpenak irakurri ahal izango ditu eta zuri jarraitu ere.",
"notification": "Baita ere, ezkutatu erabiltzaile honen jakinarazpenak",
"duration": {
"heading": "Iraupena",
"0": "Mugagabe",
"1800": "30 minutu",
"3600": "Ordu 1",
"86400": "Egun 1",
"604800": "Aste 1"
}
},
"report": {
"name": "",
"report": "",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Modifier l'historique"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "",
"report": "",

View File

@ -5,11 +5,11 @@
"cancel": "Annulla",
"discard": "Scarta",
"continue": "Continua",
"create": "",
"delete": "",
"done": "",
"create": "Crea",
"delete": "Elimina",
"done": "Fatto",
"confirm": "Ho capito",
"add": ""
"add": "Aggiungi"
},
"customEmoji": {
"accessibilityLabel": "Emoji personalizzata {{emoji}}"

View File

@ -1,13 +1,13 @@
{
"server": {
"textInput": {
"placeholder": ""
"placeholder": "Dominio dell'istanza"
},
"whitelisted": "",
"whitelisted": "Questa potrebbe essere un'istanza nella whitelist nella quale tooot non può accedere ai dati di essa prima di fare il log-in.",
"button": "Accedi",
"information": {
"name": "Nome",
"description": ""
"description": "Descrizione"
},
"disclaimer": {
"base": "Per accedere, verrà aperta una pagina del browser di sistema. I dati di accesso del tuo account sono protetti."

View File

@ -17,10 +17,10 @@
"refresh": {
"fetchPreviousPage": "Più recenti da qui",
"refetch": "Al più recente",
"fetching": "",
"fetching": "Recupero nuovi toot ...",
"fetched": {
"none": "",
"found": ""
"none": "Nessun nuovo toot",
"found": "{{count}} toot recuperati"
}
},
"shared": {

View File

@ -417,6 +417,20 @@
"history": {
"name": "Cronologia delle modifiche"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "",
"report": "",

View File

@ -417,6 +417,20 @@
"history": {
"name": "編集履歴"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "{{acct}} の違反報告",
"report": "報告",

View File

@ -417,6 +417,20 @@
"history": {
"name": "수정 이력"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "@{{acct}} 신고",
"report": "신고",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Geschiedenis bewerken"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "Voor duur",
"0": "Onbepaalde tijd",
"1800": "30 minuten",
"3600": "1 uur",
"86400": "1 dag",
"604800": "1 week"
}
},
"report": {
"name": "Rapporteer {{acct}}",
"report": "Rapporteer",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Rediger historikk"
},
"mute": {
"name": "Demp {{acct}}",
"mute": "Demp",
"description": "Skjul innlegg fra denne brukeren og innleggene som nevner brukeren, men det vil fortsatt la brukeren se dine innlegg og følge deg.",
"notification": "Skjul varsler også for denne brukeren",
"duration": {
"heading": "For varighet",
"0": "På ubestemt tid",
"1800": "30 minutter",
"3600": "Én time",
"86400": "Én dag",
"604800": "én uke"
}
},
"report": {
"name": "Rapporter {{acct}}",
"report": "Rapporter",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Historia edycji"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "Zgłoś {{acct}}",
"report": "Zgłoś",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Histórico de Edição"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "Denuncia {{acct}}",
"report": "Denunciar",

View File

@ -417,6 +417,20 @@
"history": {
"name": ""
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "",
"report": "",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Redigeringshistorik"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "Rapportera {{acct}}",
"report": "Rapport",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Редагувати історію"
},
"mute": {
"name": "Ігнорувати {{acct}}",
"mute": "Ігнорувати",
"description": "Сховає дописи від цього користувача і дописи зі згадками про них, проте вони все одно матимуть змогу бачити ваші дописи та слідкувати за вами.",
"notification": "Також сховати сповіщення цього користувача",
"duration": {
"heading": "На час",
"0": "Безтерміново",
"1800": "30 хвилин",
"3600": "1 годину",
"86400": "1 день",
"604800": "1 тиждень"
}
},
"report": {
"name": "Скарга на {{acct}}",
"report": "Скарга",

View File

@ -417,6 +417,20 @@
"history": {
"name": "Lịch sử chỉnh sửa"
},
"mute": {
"name": "",
"mute": "",
"description": "",
"notification": "",
"duration": {
"heading": "",
"0": "",
"1800": "",
"3600": "",
"86400": "",
"604800": ""
}
},
"report": {
"name": "Báo cáo @{{acct}}",
"report": "Báo cáo",

View File

@ -417,6 +417,20 @@
"history": {
"name": "编辑历史"
},
"mute": {
"name": "隐藏{{acct}}",
"mute": "隐藏",
"description": "此用户的嘟文及提到此用户的嘟文都会隐藏,但他们仍可以看到你的嘟文,也可以关注你。",
"notification": "同时隐藏来自此用户的通知",
"duration": {
"heading": "时限",
"0": "无限期",
"1800": "30分钟",
"3600": "一小时",
"86400": "一天",
"604800": "一周"
}
},
"report": {
"name": "举报 {{acct}}",
"report": "举报",

View File

@ -417,6 +417,20 @@
"history": {
"name": "編輯歷史"
},
"mute": {
"name": "禁音{{acct}}",
"mute": "禁音",
"description": "該使用者的嘟文及提到該使用者的嘟文都會被隱藏,但他們仍然可以看到你的嘟文,也可以關注你。",
"notification": "同時隱藏來自該使用者的通知",
"duration": {
"heading": "時限",
"0": "無限期",
"1800": "30分鐘",
"3600": "一小時",
"86400": "一天",
"604800": "一週"
}
},
"report": {
"name": "檢舉 {{acct}}",
"report": "檢舉",

View File

@ -374,7 +374,10 @@ const Explore = ({ route: { key: page } }: { route: { key: 'Explore' } }) => {
<DropdownMenu.ItemTitle children={item.title} />
<DropdownMenu.ItemSubtitle children={item.domain} />
{index === remotes?.findIndex(r => r.domain === remoteActive) ? (
<DropdownMenu.ItemIcon ios={{ name: 'trash' }} />
<DropdownMenu.ItemIcon
ios={{ name: 'trash' }}
androidIconName='ic_menu_delete'
/>
) : null}
</DropdownMenu.CheckboxItem>
))}
@ -391,7 +394,7 @@ const Explore = ({ route: { key: page } }: { route: { key: 'Explore' } }) => {
<DropdownMenu.ItemTitle
children={t('screenTabs:tabs.public.exploring.followRemote')}
/>
<DropdownMenu.ItemIcon ios={{ name: 'plus' }} />
<DropdownMenu.ItemIcon ios={{ name: 'plus' }} androidIconName='ic_menu_add' />
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>

View File

@ -71,7 +71,7 @@ const AccountInformationActions: React.FC = () => {
round
type='icon'
content='at-sign'
style={{ flex: 1, marginRight: StyleConstants.Spacing.S }}
style={{ marginRight: StyleConstants.Spacing.S }}
onPress={() => {}}
/>
</DropdownMenu.Trigger>
@ -129,7 +129,7 @@ const styles = StyleSheet.create({
base: {
alignSelf: 'flex-end',
flexDirection: 'row',
alignItems: 'stretch'
alignItems: 'center'
}
})

View File

@ -0,0 +1,130 @@
import ComponentAccount from '@components/Account'
import { HeaderLeft, HeaderRight } from '@components/Header'
import Icon from '@components/Icon'
import { displayMessage } from '@components/Message'
import { ModalScrollView } from '@components/ModalScrollView'
import Selections from '@components/Selections'
import CustomText from '@components/Text'
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
import { useTimelineMutation } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Pressable, View } from 'react-native'
const TabSharedMute: React.FC<TabSharedStackScreenProps<'Tab-Shared-Report'>> = ({
navigation,
route: {
params: { account }
}
}) => {
const { colors, theme } = useTheme()
const { t } = useTranslation(['common', 'screenTabs'])
const { mutateAsync, isLoading } = useTimelineMutation({
onSuccess: () =>
displayMessage({
type: 'success',
message: t('common:message.success.message', {
function: t('componentContextMenu:account.mute.action', {
defaultValue: 'false',
context: 'false'
})
})
})
})
const [durations, setDurations] = useState<{ selected: boolean; content: string }[]>(
(['0', '1800', '3600', '86400', '604800'] as ['0', '1800', '3600', '86400', '604800']).map(
duration => ({
selected: duration === '0',
content: t(`screenTabs:shared.mute.duration.${duration}`)
})
)
)
const [notification, setNotification] = useState(false)
useEffect(() => {
navigation.setOptions({
title: t('screenTabs:shared.mute.name', { acct: `@${account.acct}` }),
headerLeft: () => (
<HeaderLeft
type='text'
content={t('common:buttons.cancel')}
onPress={() => navigation.goBack()}
/>
),
headerRight: () => (
<HeaderRight
type='text'
content={t('screenTabs:shared.mute.mute')}
destructive
destructiveColor={colors.yellow}
onPress={async () => {
await mutateAsync({
type: 'updateAccountProperty',
id: account.id,
payload: { property: 'mute', currentValue: false }
})
navigation.pop(1)
}}
loading={isLoading}
/>
)
})
}, [theme, isLoading, durations, notification, account.id])
return (
<ModalScrollView>
<View
style={{
margin: StyleConstants.Spacing.Global.PagePadding,
borderWidth: 1,
borderColor: colors.yellow,
borderRadius: StyleConstants.BorderRadius
}}
>
<ComponentAccount account={account} props={{}} />
</View>
<View
style={{
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding
}}
>
<CustomText
fontStyle='M'
style={{ color: colors.primaryDefault, marginBottom: StyleConstants.Spacing.M }}
>
{t('screenTabs:shared.mute.description')}
</CustomText>
<Selections
title={t('screenTabs:shared.mute.duration.heading')}
options={durations}
setOptions={setDurations}
/>
<Pressable
style={{ flex: 1, flexDirection: 'row', marginTop: StyleConstants.Spacing.M }}
onPress={() => setNotification(!notification)}
>
<Icon
style={{
marginTop: (StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M) / 2,
marginRight: StyleConstants.Spacing.S
}}
name={notification ? 'check-square' : 'square'}
size={StyleConstants.Font.Size.M}
color={colors.primaryDefault}
/>
<CustomText fontStyle='M' style={{ color: colors.primaryDefault }}>
{t('screenTabs:shared.mute.notification')}
</CustomText>
</Pressable>
</View>
</ModalScrollView>
)
}
export default TabSharedMute

View File

@ -21,7 +21,6 @@ const TabSharedReport: React.FC<TabSharedStackScreenProps<'Tab-Shared-Report'>>
params: { account, status }
}
}) => {
console.log('account', account.id)
const { colors } = useTheme()
const { t } = useTranslation(['common', 'screenTabs'])

View File

@ -9,6 +9,7 @@ import TabSharedToot from '@screens/Tabs/Shared/Toot'
import TabSharedUsers from '@screens/Tabs/Shared/Users'
import React from 'react'
import TabSharedFilter from './Filter'
import TabSharedMute from './Mute'
const TabShared = ({ Stack }: { Stack: any }) => {
return (
@ -44,6 +45,12 @@ const TabShared = ({ Stack }: { Stack: any }) => {
name='Tab-Shared-History'
component={TabSharedHistory}
/>
<Stack.Screen
key='Tab-Shared-Mute'
name='Tab-Shared-Mute'
component={TabSharedMute}
options={{ presentation: 'modal' }}
/>
<Stack.Screen
key='Tab-Shared-Report'
name='Tab-Shared-Report'

View File

@ -4,6 +4,7 @@ type Features =
| 'account_follow_notify'
| 'notification_type_status'
| 'account_return_suspended'
| 'mute_duration'
| 'edit_post'
| 'deprecate_auth_follow'
| 'notification_type_update'
@ -20,6 +21,7 @@ const features: { feature: Features; version: number }[] = [
{ feature: 'account_follow_notify', version: 3.3 },
{ feature: 'notification_type_status', version: 3.3 },
{ feature: 'account_return_suspended', version: 3.3 },
{ feature: 'mute_duration', version: 3.3 },
{ feature: 'edit_post', version: 3.5 },
{ feature: 'deprecate_auth_follow', version: 3.5 },
{ feature: 'notification_type_update', version: 3.5 },

View File

@ -104,6 +104,9 @@ export type TabSharedStackParamList = {
| { source: 'hashtag'; tag_name: Mastodon.Tag['name'] }
'Tab-Shared-Hashtag': { tag_name: Mastodon.Tag['name']; queryKey?: QueryKeyTimeline }
'Tab-Shared-History': { status: Mastodon.Status; detectedLanguage: string }
'Tab-Shared-Mute': {
account: Pick<Mastodon.Account, 'id' | 'acct' | 'username' | 'url'>
}
'Tab-Shared-Report': {
account: Pick<Mastodon.Account, 'id' | 'acct' | 'username' | 'url'>
status?: Pick<Mastodon.Status, 'id' | '_remote' | 'uri'>

View File

@ -25,10 +25,7 @@ const useAppsQuery = (
type MutationVarsApps = { domain: string; scopes: string[] }
export const redirectUri = AuthSession.makeRedirectUri({
native: 'tooot://instance-auth',
useProxy: false
})
export const redirectUri = AuthSession.makeRedirectUri({ native: 'tooot://instance-auth' })
const mutationFunctionApps = async ({ domain, scopes }: MutationVarsApps) => {
return apiGeneral<Mastodon.Apps>({
method: 'post',
@ -49,4 +46,5 @@ const useAppsMutation = (
return useMutation(mutationFunctionApps, options)
}
export { useAppsQuery, useAppsMutation }
export { useAppsMutation, useAppsQuery }

6047
yarn.lock

File diff suppressed because it is too large Load Diff