mirror of https://github.com/tooot-app/app
commit
73d6703e56
|
@ -30,9 +30,11 @@ jobs:
|
||||||
expo-token: ${{ secrets.EXPO_TOKEN }}
|
expo-token: ${{ secrets.EXPO_TOKEN }}
|
||||||
- name: -- Step 4 -- Install node dependencies
|
- name: -- Step 4 -- Install node dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
- name: -- Step 5 -- Install ruby dependencies
|
- name: -- Step 5 -- Install native dependencies
|
||||||
|
run: npx pod-install
|
||||||
|
- name: -- Step 6 -- Install ruby dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: -- Step 6 -- Run fastlane
|
- name: -- Step 7 -- Run fastlane
|
||||||
env:
|
env:
|
||||||
ENVIRONMENT: ${{ steps.branch.outputs.branch }}
|
ENVIRONMENT: ${{ steps.branch.outputs.branch }}
|
||||||
LC_ALL: en_US.UTF-8
|
LC_ALL: en_US.UTF-8
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -1,5 +1,6 @@
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem "fastlane"
|
gem "fastlane"
|
||||||
|
gem 'cocoapods'
|
||||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
||||||
|
|
65
Gemfile.lock
65
Gemfile.lock
|
@ -2,8 +2,16 @@ GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.3)
|
CFPropertyList (3.0.3)
|
||||||
|
activesupport (5.2.4.5)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (>= 0.7, < 2)
|
||||||
|
minitest (~> 5.1)
|
||||||
|
tzinfo (~> 1.1)
|
||||||
addressable (2.7.0)
|
addressable (2.7.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
|
algoliasearch (1.27.5)
|
||||||
|
httpclient (~> 2.8, >= 2.8.3)
|
||||||
|
json (>= 1.5.1)
|
||||||
artifactory (3.0.15)
|
artifactory (3.0.15)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.1.1)
|
aws-eventstream (1.1.1)
|
||||||
|
@ -24,10 +32,48 @@ GEM
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
babosa (1.0.4)
|
babosa (1.0.4)
|
||||||
claide (1.0.3)
|
claide (1.0.3)
|
||||||
|
cocoapods (1.10.1)
|
||||||
|
addressable (~> 2.6)
|
||||||
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
cocoapods-core (= 1.10.1)
|
||||||
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
|
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||||
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
|
cocoapods-search (>= 1.0.0, < 2.0)
|
||||||
|
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||||
|
cocoapods-try (>= 1.1.0, < 2.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
escape (~> 0.0.4)
|
||||||
|
fourflusher (>= 2.3.0, < 3.0)
|
||||||
|
gh_inspector (~> 1.0)
|
||||||
|
molinillo (~> 0.6.6)
|
||||||
|
nap (~> 1.0)
|
||||||
|
ruby-macho (~> 1.4)
|
||||||
|
xcodeproj (>= 1.19.0, < 2.0)
|
||||||
|
cocoapods-core (1.10.1)
|
||||||
|
activesupport (> 5.0, < 6)
|
||||||
|
addressable (~> 2.6)
|
||||||
|
algoliasearch (~> 1.0)
|
||||||
|
concurrent-ruby (~> 1.1)
|
||||||
|
fuzzy_match (~> 2.0.4)
|
||||||
|
nap (~> 1.0)
|
||||||
|
netrc (~> 0.11)
|
||||||
|
public_suffix
|
||||||
|
typhoeus (~> 1.0)
|
||||||
|
cocoapods-deintegrate (1.0.4)
|
||||||
|
cocoapods-downloader (1.4.0)
|
||||||
|
cocoapods-plugins (1.0.0)
|
||||||
|
nap
|
||||||
|
cocoapods-search (1.0.0)
|
||||||
|
cocoapods-trunk (1.5.0)
|
||||||
|
nap (>= 0.8, < 2.0)
|
||||||
|
netrc (~> 0.11)
|
||||||
|
cocoapods-try (1.2.0)
|
||||||
colored (1.2)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander-fastlane (4.4.6)
|
commander-fastlane (4.4.6)
|
||||||
highline (~> 1.7.2)
|
highline (~> 1.7.2)
|
||||||
|
concurrent-ruby (1.1.8)
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
declarative-option (0.1.0)
|
declarative-option (0.1.0)
|
||||||
digest-crc (0.6.3)
|
digest-crc (0.6.3)
|
||||||
|
@ -36,6 +82,9 @@ GEM
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.7.6)
|
dotenv (2.7.6)
|
||||||
emoji_regex (3.2.2)
|
emoji_regex (3.2.2)
|
||||||
|
escape (0.0.4)
|
||||||
|
ethon (0.12.0)
|
||||||
|
ffi (>= 1.3.0)
|
||||||
excon (0.79.0)
|
excon (0.79.0)
|
||||||
faraday (1.3.0)
|
faraday (1.3.0)
|
||||||
faraday-net_http (~> 1.0)
|
faraday-net_http (~> 1.0)
|
||||||
|
@ -89,6 +138,9 @@ GEM
|
||||||
fastlane-plugin-json (1.0.0)
|
fastlane-plugin-json (1.0.0)
|
||||||
fastlane-plugin-versioning_android (0.1.0)
|
fastlane-plugin-versioning_android (0.1.0)
|
||||||
fastlane-plugin-yarn (1.2)
|
fastlane-plugin-yarn (1.2)
|
||||||
|
ffi (1.15.0)
|
||||||
|
fourflusher (2.3.1)
|
||||||
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-api-client (0.38.0)
|
google-api-client (0.38.0)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
@ -137,16 +189,22 @@ GEM
|
||||||
http-cookie (1.0.3)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
|
i18n (1.8.9)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
jmespath (1.4.0)
|
jmespath (1.4.0)
|
||||||
json (2.5.1)
|
json (2.5.1)
|
||||||
jwt (2.2.2)
|
jwt (2.2.2)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
mini_magick (4.11.0)
|
mini_magick (4.11.0)
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
|
minitest (5.14.4)
|
||||||
|
molinillo (0.6.6)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.3.0)
|
||||||
|
nap (1.1.0)
|
||||||
naturally (2.2.1)
|
naturally (2.2.1)
|
||||||
|
netrc (0.11.0)
|
||||||
os (1.1.1)
|
os (1.1.1)
|
||||||
plist (3.6.0)
|
plist (3.6.0)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.6)
|
||||||
|
@ -158,6 +216,7 @@ GEM
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rexml (3.2.4)
|
rexml (3.2.4)
|
||||||
rouge (2.0.7)
|
rouge (2.0.7)
|
||||||
|
ruby-macho (1.4.0)
|
||||||
ruby2_keywords (0.0.4)
|
ruby2_keywords (0.0.4)
|
||||||
rubyzip (2.3.0)
|
rubyzip (2.3.0)
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
|
@ -173,10 +232,15 @@ GEM
|
||||||
terminal-notifier (2.0.0)
|
terminal-notifier (2.0.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
|
thread_safe (0.3.6)
|
||||||
tty-cursor (0.7.1)
|
tty-cursor (0.7.1)
|
||||||
tty-screen (0.8.1)
|
tty-screen (0.8.1)
|
||||||
tty-spinner (0.9.3)
|
tty-spinner (0.9.3)
|
||||||
tty-cursor (~> 0.7)
|
tty-cursor (~> 0.7)
|
||||||
|
typhoeus (1.4.0)
|
||||||
|
ethon (>= 0.9.0)
|
||||||
|
tzinfo (1.2.9)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
|
@ -199,6 +263,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
cocoapods
|
||||||
fastlane
|
fastlane
|
||||||
fastlane-plugin-json
|
fastlane-plugin-json
|
||||||
fastlane-plugin-versioning_android
|
fastlane-plugin-versioning_android
|
||||||
|
|
|
@ -9,7 +9,7 @@ VERSIONS = read_json( json_path: "./package.json" )[:versions]
|
||||||
ENVIRONMENT = ENV["ENVIRONMENT"]
|
ENVIRONMENT = ENV["ENVIRONMENT"]
|
||||||
VERSION = "#{VERSIONS[:major]}.#{VERSIONS[:minor]}"
|
VERSION = "#{VERSIONS[:major]}.#{VERSIONS[:minor]}"
|
||||||
RELEASE_CHANNEL = "#{VERSIONS[:major]}-#{ENVIRONMENT}"
|
RELEASE_CHANNEL = "#{VERSIONS[:major]}-#{ENVIRONMENT}"
|
||||||
BUILD_NUMBER = ENV["GITHUB_RUN_NUMBER"]
|
BUILD_NUMBER = "#{Time.now.strftime("%y%m%d")}#{ENV["GITHUB_RUN_NUMBER"]}"
|
||||||
GITHUB_REPO = "tooot-app/app"
|
GITHUB_REPO = "tooot-app/app"
|
||||||
case ENVIRONMENT
|
case ENVIRONMENT
|
||||||
when "candidate"
|
when "candidate"
|
||||||
|
@ -32,7 +32,7 @@ private_lane :prepare_appstore_ios do
|
||||||
key: "NSAppTransportSecurity",
|
key: "NSAppTransportSecurity",
|
||||||
value: {}
|
value: {}
|
||||||
)
|
)
|
||||||
increment_build_number( xcodeproj: XCODEPROJ, build_number: BUILD_NUMBER, skip_info_plist: true )
|
increment_build_number( xcodeproj: XCODEPROJ, build_number: BUILD_NUMBER )
|
||||||
app_store_connect_api_key
|
app_store_connect_api_key
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,11 +42,6 @@ private_lane :update_expo_ios do
|
||||||
set_info_plist_value( path: EXPO_PLIST, key: "EXUpdatesReleaseChannel", value: RELEASE_CHANNEL )
|
set_info_plist_value( path: EXPO_PLIST, key: "EXUpdatesReleaseChannel", value: RELEASE_CHANNEL )
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'IOS: Install pods'
|
|
||||||
private_lane :install_pods_ios do
|
|
||||||
cocoapods(podfile: "./ios/", deployment: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "ANDROID: Prepare play store"
|
desc "ANDROID: Prepare play store"
|
||||||
private_lane :prepare_playstore_android do
|
private_lane :prepare_playstore_android do
|
||||||
android_set_version_name( version_name: VERSION, gradle_file: "./android/app/build.gradle" )
|
android_set_version_name( version_name: VERSION, gradle_file: "./android/app/build.gradle" )
|
||||||
|
@ -79,7 +74,6 @@ private_lane :build_ios do
|
||||||
|
|
||||||
case ENVIRONMENT
|
case ENVIRONMENT
|
||||||
when "candidate"
|
when "candidate"
|
||||||
install_pods_ios
|
|
||||||
prepare_appstore_ios
|
prepare_appstore_ios
|
||||||
match( type: "appstore", readonly: true )
|
match( type: "appstore", readonly: true )
|
||||||
build_ios_app( export_method: "app-store", include_symbols: true, include_bitcode: true, silent: true )
|
build_ios_app( export_method: "app-store", include_symbols: true, include_bitcode: true, silent: true )
|
||||||
|
@ -90,7 +84,6 @@ private_lane :build_ios do
|
||||||
changelog: "Ready for testing"
|
changelog: "Ready for testing"
|
||||||
)
|
)
|
||||||
when "release"
|
when "release"
|
||||||
install_pods_ios
|
|
||||||
prepare_appstore_ios
|
prepare_appstore_ios
|
||||||
match( type: "appstore", readonly: true )
|
match( type: "appstore", readonly: true )
|
||||||
build_ios_app( export_method: "app-store", include_bitcode: true, silent: true )
|
build_ios_app( export_method: "app-store", include_bitcode: true, silent: true )
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import { Pressable, StyleProp, StyleSheet, ViewStyle } from 'react-native'
|
import {
|
||||||
|
Image,
|
||||||
|
ImageStyle,
|
||||||
|
Pressable,
|
||||||
|
StyleProp,
|
||||||
|
StyleSheet,
|
||||||
|
ViewStyle
|
||||||
|
} from 'react-native'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
import FastImage, { ImageStyle } from 'react-native-fast-image'
|
|
||||||
|
|
||||||
// blurhas -> if blurhash, show before any loading succeed
|
// blurhas -> if blurhash, show before any loading succeed
|
||||||
// original -> load original
|
// original -> load original
|
||||||
|
@ -70,7 +76,7 @@ const GracefullyImage = React.memo(
|
||||||
const previewView = useMemo(
|
const previewView = useMemo(
|
||||||
() =>
|
() =>
|
||||||
uri.preview && !imageLoaded ? (
|
uri.preview && !imageLoaded ? (
|
||||||
<FastImage
|
<Image
|
||||||
source={{ uri: uri.preview }}
|
source={{ uri: uri.preview }}
|
||||||
style={[{ flex: 1 }, imageStyle]}
|
style={[{ flex: 1 }, imageStyle]}
|
||||||
/>
|
/>
|
||||||
|
@ -79,14 +85,14 @@ const GracefullyImage = React.memo(
|
||||||
)
|
)
|
||||||
const originalView = useMemo(
|
const originalView = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<FastImage
|
<Image
|
||||||
source={source}
|
source={source}
|
||||||
style={[{ flex: imageLoaded ? 1 : undefined }, imageStyle]}
|
style={[{ flex: 1 }, imageStyle]}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[source, imageLoaded]
|
[source]
|
||||||
)
|
)
|
||||||
const blurhashView = useMemo(() => {
|
const blurhashView = useMemo(() => {
|
||||||
return blurhash && (hidden || !imageLoaded) ? (
|
return blurhash && (hidden || !imageLoaded) ? (
|
||||||
|
|
|
@ -23,7 +23,7 @@ const HeaderCenter = React.memo(
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) => prev.content === next.content
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|
|
@ -340,6 +340,12 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
||||||
[totalTextCount, composeState]
|
[totalTextCount, composeState]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const headerContent = useMemo(() => {
|
||||||
|
return `${totalTextCount} / ${maxTootChars}${
|
||||||
|
__DEV__ ? ` Dirty: ${composeState.dirty.toString()}` : ''
|
||||||
|
}`
|
||||||
|
}, [totalTextCount, maxTootChars, composeState.dirty])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={styles.base}
|
style={styles.base}
|
||||||
|
@ -358,26 +364,26 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
||||||
name='Screen-Compose-Root'
|
name='Screen-Compose-Root'
|
||||||
component={ComposeRoot}
|
component={ComposeRoot}
|
||||||
options={{
|
options={{
|
||||||
|
...Platform.select({
|
||||||
|
ios: {
|
||||||
|
headerTitle: headerContent,
|
||||||
|
headerTitleStyle: {
|
||||||
|
fontWeight:
|
||||||
|
totalTextCount > maxTootChars
|
||||||
|
? StyleConstants.Font.Weight.Bold
|
||||||
|
: StyleConstants.Font.Weight.Normal,
|
||||||
|
fontSize: StyleConstants.Font.Size.M
|
||||||
|
},
|
||||||
|
headerTintColor:
|
||||||
|
totalTextCount > maxTootChars
|
||||||
|
? theme.red
|
||||||
|
: theme.secondary
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
headerCenter: () => <HeaderCenter content={headerContent} />
|
||||||
|
}
|
||||||
|
}),
|
||||||
headerLeft,
|
headerLeft,
|
||||||
headerTitle: `${totalTextCount} / ${maxTootChars}${
|
|
||||||
__DEV__ ? ` Dirty: ${composeState.dirty.toString()}` : ''
|
|
||||||
}`,
|
|
||||||
headerTitleStyle: {
|
|
||||||
fontWeight:
|
|
||||||
totalTextCount > maxTootChars
|
|
||||||
? StyleConstants.Font.Weight.Bold
|
|
||||||
: StyleConstants.Font.Weight.Normal,
|
|
||||||
fontSize: StyleConstants.Font.Size.M
|
|
||||||
},
|
|
||||||
headerTintColor:
|
|
||||||
totalTextCount > maxTootChars ? theme.red : theme.secondary,
|
|
||||||
headerCenter: () => (
|
|
||||||
<HeaderCenter
|
|
||||||
content={`${totalTextCount} / ${maxTootChars}${
|
|
||||||
__DEV__ ? ` Dirty: ${composeState.dirty.toString()}` : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
headerRight
|
headerRight
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
import React, { useCallback, useContext, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
Dimensions,
|
Dimensions,
|
||||||
|
@ -42,12 +42,6 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
||||||
draft => draft.timestamp !== timestamp
|
draft => draft.timestamp !== timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (instanceDrafts?.length === 0) {
|
|
||||||
navigation.goBack()
|
|
||||||
}
|
|
||||||
}, [instanceDrafts?.length])
|
|
||||||
|
|
||||||
const actionWidth =
|
const actionWidth =
|
||||||
StyleConstants.Font.Size.L + StyleConstants.Spacing.Global.PagePadding * 4
|
StyleConstants.Font.Size.L + StyleConstants.Spacing.Global.PagePadding * 4
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
|
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useContext, useMemo } from 'react'
|
import React, { useContext, useMemo, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'
|
import { ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'
|
||||||
import ComposeContext from '../utils/createContext'
|
import ComposeContext from '../utils/createContext'
|
||||||
|
@ -55,8 +55,10 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const scrollViewRef = useRef<ScrollView>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView ref={scrollViewRef}>
|
||||||
{mediaDisplay}
|
{mediaDisplay}
|
||||||
<View style={styles.altTextContainer}>
|
<View style={styles.altTextContainer}>
|
||||||
<Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
|
<Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
|
||||||
|
@ -67,6 +69,7 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
|
||||||
styles.altTextInput,
|
styles.altTextInput,
|
||||||
{ borderColor: theme.border, color: theme.primary }
|
{ borderColor: theme.border, color: theme.primary }
|
||||||
]}
|
]}
|
||||||
|
onFocus={() => scrollViewRef.current?.scrollToEnd()}
|
||||||
autoCapitalize='none'
|
autoCapitalize='none'
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
maxLength={1500}
|
maxLength={1500}
|
||||||
|
|
|
@ -4,7 +4,13 @@ import { useSearchQuery } from '@utils/queryHooks/search'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { forEach, groupBy, sortBy } from 'lodash'
|
import { forEach, groupBy, sortBy } from 'lodash'
|
||||||
import React, { useCallback, useContext, useEffect, useMemo } from 'react'
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef
|
||||||
|
} from 'react'
|
||||||
import { FlatList, Image, StyleSheet, View } from 'react-native'
|
import { FlatList, Image, StyleSheet, View } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
import ComposeActions from './Root/Actions'
|
import ComposeActions from './Root/Actions'
|
||||||
|
@ -30,90 +36,93 @@ const prefetchEmojis = (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComposeRoot: React.FC = () => {
|
const ComposeRoot = React.memo(
|
||||||
const { theme } = useTheme()
|
() => {
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
|
|
||||||
const { isFetching, data, refetch } = useSearchQuery({
|
const { isFetching, data, refetch } = useSearchQuery({
|
||||||
type:
|
type:
|
||||||
composeState.tag?.type === 'accounts' ||
|
composeState.tag?.type === 'accounts' ||
|
||||||
composeState.tag?.type === 'hashtags'
|
composeState.tag?.type === 'hashtags'
|
||||||
? composeState.tag.type
|
? composeState.tag.type
|
||||||
: undefined,
|
: undefined,
|
||||||
term: composeState.tag?.text.substring(1),
|
term: composeState.tag?.text.substring(1),
|
||||||
options: { enabled: false }
|
options: { enabled: false }
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
(composeState.tag?.type === 'accounts' ||
|
(composeState.tag?.type === 'accounts' ||
|
||||||
composeState.tag?.type === 'hashtags') &&
|
composeState.tag?.type === 'hashtags') &&
|
||||||
composeState.tag?.text
|
composeState.tag?.text
|
||||||
) {
|
) {
|
||||||
refetch()
|
refetch()
|
||||||
}
|
}
|
||||||
}, [composeState.tag])
|
}, [composeState.tag])
|
||||||
|
|
||||||
const { data: emojisData } = useEmojisQuery({})
|
const { data: emojisData } = useEmojisQuery({})
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (emojisData && emojisData.length) {
|
if (emojisData && emojisData.length) {
|
||||||
let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = []
|
let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = []
|
||||||
forEach(
|
forEach(
|
||||||
groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'),
|
groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'),
|
||||||
(value, key) => sortedEmojis.push({ title: key, data: value })
|
(value, key) => sortedEmojis.push({ title: key, data: value })
|
||||||
)
|
)
|
||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'emoji',
|
type: 'emoji',
|
||||||
payload: { ...composeState.emoji, emojis: sortedEmojis }
|
payload: { ...composeState.emoji, emojis: sortedEmojis }
|
||||||
})
|
})
|
||||||
prefetchEmojis(sortedEmojis)
|
prefetchEmojis(sortedEmojis)
|
||||||
}
|
}
|
||||||
}, [emojisData])
|
}, [emojisData])
|
||||||
|
|
||||||
const listEmpty = useMemo(() => {
|
const listEmpty = useMemo(() => {
|
||||||
if (isFetching) {
|
if (isFetching) {
|
||||||
return (
|
return (
|
||||||
<View key='listEmpty' style={styles.loading}>
|
<View key='listEmpty' style={styles.loading}>
|
||||||
<Circle
|
<Circle
|
||||||
size={StyleConstants.Font.Size.M * 1.25}
|
size={StyleConstants.Font.Size.M * 1.25}
|
||||||
color={theme.secondary}
|
color={theme.secondary}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [isFetching])
|
}, [isFetching])
|
||||||
|
|
||||||
const listItem = useCallback(
|
const listItem = useCallback(
|
||||||
({ item }) => (
|
({ item }) => (
|
||||||
<ComposeRootSuggestion
|
<ComposeRootSuggestion
|
||||||
item={item}
|
item={item}
|
||||||
composeState={composeState}
|
composeState={composeState}
|
||||||
composeDispatch={composeDispatch}
|
composeDispatch={composeDispatch}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[composeState]
|
[composeState]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
<FlatList
|
<FlatList
|
||||||
renderItem={listItem}
|
renderItem={listItem}
|
||||||
ListEmptyComponent={listEmpty}
|
ListEmptyComponent={listEmpty}
|
||||||
keyboardShouldPersistTaps='handled'
|
keyboardShouldPersistTaps='always'
|
||||||
ListHeaderComponent={ComposeRootHeader}
|
ListHeaderComponent={ComposeRootHeader}
|
||||||
ListFooterComponent={ComposeRootFooter}
|
ListFooterComponent={ComposeRootFooter}
|
||||||
ItemSeparatorComponent={ComponentSeparator}
|
ItemSeparatorComponent={ComponentSeparator}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
data={data ? data[composeState.tag?.type] : undefined}
|
data={data ? data[composeState.tag?.type] : undefined}
|
||||||
keyExtractor={() => Math.random().toString()}
|
keyExtractor={() => Math.random().toString()}
|
||||||
/>
|
/>
|
||||||
<ComposeActions />
|
<ComposeActions />
|
||||||
<ComposeDrafts />
|
<ComposeDrafts />
|
||||||
<ComposePosting />
|
<ComposePosting />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
() => true
|
||||||
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
base: {
|
base: {
|
||||||
|
|
|
@ -246,7 +246,7 @@ const ComposeAttachments: React.FC = () => {
|
||||||
snapToAlignment='center'
|
snapToAlignment='center'
|
||||||
renderItem={renderAttachment}
|
renderItem={renderAttachment}
|
||||||
snapToOffsets={snapToOffsets}
|
snapToOffsets={snapToOffsets}
|
||||||
keyboardShouldPersistTaps='handled'
|
keyboardShouldPersistTaps='always'
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
data={composeState.attachments.uploads}
|
data={composeState.attachments.uploads}
|
||||||
keyExtractor={item =>
|
keyExtractor={item =>
|
||||||
|
|
|
@ -76,7 +76,7 @@ const ComposeEmojis: React.FC = () => {
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
<SectionList
|
<SectionList
|
||||||
horizontal
|
horizontal
|
||||||
keyboardShouldPersistTaps='handled'
|
keyboardShouldPersistTaps='always'
|
||||||
sections={composeState.emoji.emojis || []}
|
sections={composeState.emoji.emojis || []}
|
||||||
keyExtractor={item => item.shortcode}
|
keyExtractor={item => item.shortcode}
|
||||||
renderSectionHeader={listHeader}
|
renderSectionHeader={listHeader}
|
||||||
|
|
|
@ -46,7 +46,7 @@ const ComposeSpoilerInput: React.FC = () => {
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
ref={composeState.textInputFocus.refs.spoiler}
|
ref={composeState.textInputFocus.refs.spoiler}
|
||||||
scrollEnabled
|
scrollEnabled={false}
|
||||||
onFocus={() =>
|
onFocus={() =>
|
||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'textInputFocus',
|
type: 'textInputFocus',
|
||||||
|
|
|
@ -52,7 +52,7 @@ const ComposeTextInput: React.FC = () => {
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
ref={composeState.textInputFocus.refs.text}
|
ref={composeState.textInputFocus.refs.text}
|
||||||
scrollEnabled
|
scrollEnabled={false}
|
||||||
>
|
>
|
||||||
<Text>{composeState.text.formatted}</Text>
|
<Text>{composeState.text.formatted}</Text>
|
||||||
</TextInput>
|
</TextInput>
|
||||||
|
|
|
@ -86,9 +86,9 @@ const HeaderComponent = React.memo(
|
||||||
analytics('imageviewer_more_share_press')
|
analytics('imageviewer_more_share_press')
|
||||||
switch (Platform.OS) {
|
switch (Platform.OS) {
|
||||||
case 'ios':
|
case 'ios':
|
||||||
return Share.share({ url: imageUrls[currentIndex].url })
|
Share.share({ url: imageUrls[currentIndex].url })
|
||||||
case 'android':
|
case 'android':
|
||||||
return Share.share({ message: imageUrls[currentIndex].url })
|
Share.share({ message: imageUrls[currentIndex].url })
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ const ScreenMeSwitchRoot: React.FC = () => {
|
||||||
const instanceActive = useSelector(getInstanceActive)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView style={styles.base} keyboardShouldPersistTaps='handled'>
|
<ScrollView style={styles.base} keyboardShouldPersistTaps='always'>
|
||||||
<View style={[styles.firstSection, { borderBottomColor: theme.border }]}>
|
<View style={[styles.firstSection, { borderBottomColor: theme.border }]}>
|
||||||
<Text style={[styles.header, { color: theme.primary }]}>
|
<Text style={[styles.header, { color: theme.primary }]}>
|
||||||
{t('content.existing')}
|
{t('content.existing')}
|
||||||
|
|
Loading…
Reference in New Issue