diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 33696ceb..515be358 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,9 +30,11 @@ jobs:
expo-token: ${{ secrets.EXPO_TOKEN }}
- name: -- Step 4 -- Install node dependencies
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
- - name: -- Step 6 -- Run fastlane
+ - name: -- Step 7 -- Run fastlane
env:
ENVIRONMENT: ${{ steps.branch.outputs.branch }}
LC_ALL: en_US.UTF-8
diff --git a/Gemfile b/Gemfile
index 2ccf2ecb..803989f1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,6 @@
source "https://rubygems.org"
gem "fastlane"
+gem 'cocoapods'
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
diff --git a/Gemfile.lock b/Gemfile.lock
index 3181cf5c..fb410963 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2,8 +2,16 @@ GEM
remote: https://rubygems.org/
specs:
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)
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)
atomos (0.1.3)
aws-eventstream (1.1.1)
@@ -24,10 +32,48 @@ GEM
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
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)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
+ concurrent-ruby (1.1.8)
declarative (0.0.20)
declarative-option (0.1.0)
digest-crc (0.6.3)
@@ -36,6 +82,9 @@ GEM
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.2)
+ escape (0.0.4)
+ ethon (0.12.0)
+ ffi (>= 1.3.0)
excon (0.79.0)
faraday (1.3.0)
faraday-net_http (~> 1.0)
@@ -89,6 +138,9 @@ GEM
fastlane-plugin-json (1.0.0)
fastlane-plugin-versioning_android (0.1.0)
fastlane-plugin-yarn (1.2)
+ ffi (1.15.0)
+ fourflusher (2.3.1)
+ fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-api-client (0.38.0)
addressable (~> 2.5, >= 2.5.1)
@@ -137,16 +189,22 @@ GEM
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
+ i18n (1.8.9)
+ concurrent-ruby (~> 1.0)
jmespath (1.4.0)
json (2.5.1)
jwt (2.2.2)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.0.2)
+ minitest (5.14.4)
+ molinillo (0.6.6)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
+ nap (1.1.0)
naturally (2.2.1)
+ netrc (0.11.0)
os (1.1.1)
plist (3.6.0)
public_suffix (4.0.6)
@@ -158,6 +216,7 @@ GEM
retriable (3.1.2)
rexml (3.2.4)
rouge (2.0.7)
+ ruby-macho (1.4.0)
ruby2_keywords (0.0.4)
rubyzip (2.3.0)
security (0.1.3)
@@ -173,10 +232,15 @@ GEM
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
+ thread_safe (0.3.6)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
+ typhoeus (1.4.0)
+ ethon (>= 0.9.0)
+ tzinfo (1.2.9)
+ thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
@@ -199,6 +263,7 @@ PLATFORMS
ruby
DEPENDENCIES
+ cocoapods
fastlane
fastlane-plugin-json
fastlane-plugin-versioning_android
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 58317885..3d696d46 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -9,7 +9,7 @@ VERSIONS = read_json( json_path: "./package.json" )[:versions]
ENVIRONMENT = ENV["ENVIRONMENT"]
VERSION = "#{VERSIONS[:major]}.#{VERSIONS[:minor]}"
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"
case ENVIRONMENT
when "candidate"
@@ -32,7 +32,7 @@ private_lane :prepare_appstore_ios do
key: "NSAppTransportSecurity",
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
end
@@ -42,11 +42,6 @@ private_lane :update_expo_ios do
set_info_plist_value( path: EXPO_PLIST, key: "EXUpdatesReleaseChannel", value: RELEASE_CHANNEL )
end
-desc 'IOS: Install pods'
-private_lane :install_pods_ios do
- cocoapods(podfile: "./ios/", deployment: true)
-end
-
desc "ANDROID: Prepare play store"
private_lane :prepare_playstore_android do
android_set_version_name( version_name: VERSION, gradle_file: "./android/app/build.gradle" )
@@ -79,7 +74,6 @@ private_lane :build_ios do
case ENVIRONMENT
when "candidate"
- install_pods_ios
prepare_appstore_ios
match( type: "appstore", readonly: 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"
)
when "release"
- install_pods_ios
prepare_appstore_ios
match( type: "appstore", readonly: true )
build_ios_app( export_method: "app-store", include_bitcode: true, silent: true )
diff --git a/src/components/GracefullyImage.tsx b/src/components/GracefullyImage.tsx
index 03462ac2..33f658a5 100644
--- a/src/components/GracefullyImage.tsx
+++ b/src/components/GracefullyImage.tsx
@@ -1,9 +1,15 @@
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
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 FastImage, { ImageStyle } from 'react-native-fast-image'
// blurhas -> if blurhash, show before any loading succeed
// original -> load original
@@ -70,7 +76,7 @@ const GracefullyImage = React.memo(
const previewView = useMemo(
() =>
uri.preview && !imageLoaded ? (
-
@@ -79,14 +85,14 @@ const GracefullyImage = React.memo(
)
const originalView = useMemo(
() => (
-
),
- [source, imageLoaded]
+ [source]
)
const blurhashView = useMemo(() => {
return blurhash && (hidden || !imageLoaded) ? (
diff --git a/src/components/Header/Center.tsx b/src/components/Header/Center.tsx
index cbd1424e..6edae7aa 100644
--- a/src/components/Header/Center.tsx
+++ b/src/components/Header/Center.tsx
@@ -23,7 +23,7 @@ const HeaderCenter = React.memo(
/>
)
},
- () => true
+ (prev, next) => prev.content === next.content
)
const styles = StyleSheet.create({
diff --git a/src/screens/Compose.tsx b/src/screens/Compose.tsx
index 5d0f1838..608e9b39 100644
--- a/src/screens/Compose.tsx
+++ b/src/screens/Compose.tsx
@@ -340,6 +340,12 @@ const ScreenCompose: React.FC = ({
[totalTextCount, composeState]
)
+ const headerContent = useMemo(() => {
+ return `${totalTextCount} / ${maxTootChars}${
+ __DEV__ ? ` Dirty: ${composeState.dirty.toString()}` : ''
+ }`
+ }, [totalTextCount, maxTootChars, composeState.dirty])
+
return (
= ({
name='Screen-Compose-Root'
component={ComposeRoot}
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: () =>
+ }
+ }),
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: () => (
-
- ),
headerRight
}}
/>
diff --git a/src/screens/Compose/DraftsList/Root.tsx b/src/screens/Compose/DraftsList/Root.tsx
index a30dd6e6..fa1bf999 100644
--- a/src/screens/Compose/DraftsList/Root.tsx
+++ b/src/screens/Compose/DraftsList/Root.tsx
@@ -9,7 +9,7 @@ import {
} from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
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 {
Dimensions,
@@ -42,12 +42,6 @@ const ComposeDraftsListRoot: React.FC = ({ timestamp }) => {
draft => draft.timestamp !== timestamp
)
- useEffect(() => {
- if (instanceDrafts?.length === 0) {
- navigation.goBack()
- }
- }, [instanceDrafts?.length])
-
const actionWidth =
StyleConstants.Font.Size.L + StyleConstants.Spacing.Global.PagePadding * 4
diff --git a/src/screens/Compose/EditAttachment/Root.tsx b/src/screens/Compose/EditAttachment/Root.tsx
index 6aca40f6..e6dd3c18 100644
--- a/src/screens/Compose/EditAttachment/Root.tsx
+++ b/src/screens/Compose/EditAttachment/Root.tsx
@@ -1,7 +1,7 @@
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
import { StyleConstants } from '@utils/styles/constants'
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 { ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'
import ComposeContext from '../utils/createContext'
@@ -55,8 +55,10 @@ const ComposeEditAttachmentRoot: React.FC = ({ index }) => {
}
})
+ const scrollViewRef = useRef(null)
+
return (
-
+
{mediaDisplay}
@@ -67,6 +69,7 @@ const ComposeEditAttachmentRoot: React.FC = ({ index }) => {
styles.altTextInput,
{ borderColor: theme.border, color: theme.primary }
]}
+ onFocus={() => scrollViewRef.current?.scrollToEnd()}
autoCapitalize='none'
autoCorrect={false}
maxLength={1500}
diff --git a/src/screens/Compose/Root.tsx b/src/screens/Compose/Root.tsx
index 66f45819..8ed0369a 100644
--- a/src/screens/Compose/Root.tsx
+++ b/src/screens/Compose/Root.tsx
@@ -4,7 +4,13 @@ import { useSearchQuery } from '@utils/queryHooks/search'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
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 { Circle } from 'react-native-animated-spinkit'
import ComposeActions from './Root/Actions'
@@ -30,90 +36,93 @@ const prefetchEmojis = (
})
}
-const ComposeRoot: React.FC = () => {
- const { theme } = useTheme()
+const ComposeRoot = React.memo(
+ () => {
+ const { theme } = useTheme()
- const { composeState, composeDispatch } = useContext(ComposeContext)
+ const { composeState, composeDispatch } = useContext(ComposeContext)
- const { isFetching, data, refetch } = useSearchQuery({
- type:
- composeState.tag?.type === 'accounts' ||
- composeState.tag?.type === 'hashtags'
- ? composeState.tag.type
- : undefined,
- term: composeState.tag?.text.substring(1),
- options: { enabled: false }
- })
+ const { isFetching, data, refetch } = useSearchQuery({
+ type:
+ composeState.tag?.type === 'accounts' ||
+ composeState.tag?.type === 'hashtags'
+ ? composeState.tag.type
+ : undefined,
+ term: composeState.tag?.text.substring(1),
+ options: { enabled: false }
+ })
- useEffect(() => {
- if (
- (composeState.tag?.type === 'accounts' ||
- composeState.tag?.type === 'hashtags') &&
- composeState.tag?.text
- ) {
- refetch()
- }
- }, [composeState.tag])
+ useEffect(() => {
+ if (
+ (composeState.tag?.type === 'accounts' ||
+ composeState.tag?.type === 'hashtags') &&
+ composeState.tag?.text
+ ) {
+ refetch()
+ }
+ }, [composeState.tag])
- const { data: emojisData } = useEmojisQuery({})
- useEffect(() => {
- if (emojisData && emojisData.length) {
- let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = []
- forEach(
- groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'),
- (value, key) => sortedEmojis.push({ title: key, data: value })
- )
- composeDispatch({
- type: 'emoji',
- payload: { ...composeState.emoji, emojis: sortedEmojis }
- })
- prefetchEmojis(sortedEmojis)
- }
- }, [emojisData])
+ const { data: emojisData } = useEmojisQuery({})
+ useEffect(() => {
+ if (emojisData && emojisData.length) {
+ let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = []
+ forEach(
+ groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'),
+ (value, key) => sortedEmojis.push({ title: key, data: value })
+ )
+ composeDispatch({
+ type: 'emoji',
+ payload: { ...composeState.emoji, emojis: sortedEmojis }
+ })
+ prefetchEmojis(sortedEmojis)
+ }
+ }, [emojisData])
- const listEmpty = useMemo(() => {
- if (isFetching) {
- return (
-
-
-
- )
- }
- }, [isFetching])
+ const listEmpty = useMemo(() => {
+ if (isFetching) {
+ return (
+
+
+
+ )
+ }
+ }, [isFetching])
- const listItem = useCallback(
- ({ item }) => (
-
- ),
- [composeState]
- )
+ const listItem = useCallback(
+ ({ item }) => (
+
+ ),
+ [composeState]
+ )
- return (
-
- Math.random().toString()}
- />
-
-
-
-
- )
-}
+ return (
+
+ Math.random().toString()}
+ />
+
+
+
+
+ )
+ },
+ () => true
+)
const styles = StyleSheet.create({
base: {
diff --git a/src/screens/Compose/Root/Footer/Attachments.tsx b/src/screens/Compose/Root/Footer/Attachments.tsx
index 85acd838..28fa9afb 100644
--- a/src/screens/Compose/Root/Footer/Attachments.tsx
+++ b/src/screens/Compose/Root/Footer/Attachments.tsx
@@ -246,7 +246,7 @@ const ComposeAttachments: React.FC = () => {
snapToAlignment='center'
renderItem={renderAttachment}
snapToOffsets={snapToOffsets}
- keyboardShouldPersistTaps='handled'
+ keyboardShouldPersistTaps='always'
showsHorizontalScrollIndicator={false}
data={composeState.attachments.uploads}
keyExtractor={item =>
diff --git a/src/screens/Compose/Root/Footer/Emojis.tsx b/src/screens/Compose/Root/Footer/Emojis.tsx
index bc7605b5..807370a8 100644
--- a/src/screens/Compose/Root/Footer/Emojis.tsx
+++ b/src/screens/Compose/Root/Footer/Emojis.tsx
@@ -76,7 +76,7 @@ const ComposeEmojis: React.FC = () => {
item.shortcode}
renderSectionHeader={listHeader}
diff --git a/src/screens/Compose/Root/Header/SpoilerInput.tsx b/src/screens/Compose/Root/Header/SpoilerInput.tsx
index 512c85c2..c7fcd970 100644
--- a/src/screens/Compose/Root/Header/SpoilerInput.tsx
+++ b/src/screens/Compose/Root/Header/SpoilerInput.tsx
@@ -46,7 +46,7 @@ const ComposeSpoilerInput: React.FC = () => {
})
}}
ref={composeState.textInputFocus.refs.spoiler}
- scrollEnabled
+ scrollEnabled={false}
onFocus={() =>
composeDispatch({
type: 'textInputFocus',
diff --git a/src/screens/Compose/Root/Header/TextInput.tsx b/src/screens/Compose/Root/Header/TextInput.tsx
index 56c144d2..bd1987fc 100644
--- a/src/screens/Compose/Root/Header/TextInput.tsx
+++ b/src/screens/Compose/Root/Header/TextInput.tsx
@@ -52,7 +52,7 @@ const ComposeTextInput: React.FC = () => {
})
}}
ref={composeState.textInputFocus.refs.text}
- scrollEnabled
+ scrollEnabled={false}
>
{composeState.text.formatted}
diff --git a/src/screens/ImagesViewer.tsx b/src/screens/ImagesViewer.tsx
index 5b06f0d3..2f10884c 100644
--- a/src/screens/ImagesViewer.tsx
+++ b/src/screens/ImagesViewer.tsx
@@ -86,9 +86,9 @@ const HeaderComponent = React.memo(
analytics('imageviewer_more_share_press')
switch (Platform.OS) {
case 'ios':
- return Share.share({ url: imageUrls[currentIndex].url })
+ Share.share({ url: imageUrls[currentIndex].url })
case 'android':
- return Share.share({ message: imageUrls[currentIndex].url })
+ Share.share({ message: imageUrls[currentIndex].url })
}
break
}
diff --git a/src/screens/Tabs/Me/Switch/Root.tsx b/src/screens/Tabs/Me/Switch/Root.tsx
index 66be5f38..005e9fd4 100644
--- a/src/screens/Tabs/Me/Switch/Root.tsx
+++ b/src/screens/Tabs/Me/Switch/Root.tsx
@@ -54,7 +54,7 @@ const ScreenMeSwitchRoot: React.FC = () => {
const instanceActive = useSelector(getInstanceActive)
return (
-
+
{t('content.existing')}