1
0
mirror of https://github.com/tooot-app/app synced 2025-04-25 07:28:41 +02:00

Test development branch

This commit is contained in:
Zhiyuan Zheng 2021-02-09 01:16:12 +01:00
parent 1f4108c2d4
commit c46888acab
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
9 changed files with 158 additions and 155 deletions

27
.envrc.example Normal file
View File

@ -0,0 +1,27 @@
export TOOOT_ENVIRONMENT=""
export SENTRY_ORGANIZATION=""
export SENTRY_PROJECT=""
export SENTRY_AUTH_TOKEN=""
export SENTRY_DSN=""
# Fastlane start
export LC_ALL=""
export LANG=""
export FASTLANE_USER=""
export MATCH_PASSWORD=""
export MATCH_GIT_URL=""
export MATCH_GIT_BASIC_AUTHORIZATION=""
export APP_STORE_CONNECT_API_KEY_KEY_ID=""
export APP_STORE_CONNECT_API_KEY_ISSUER_ID=""
export APP_STORE_CONNECT_API_KEY_KEY=""
export ANDROID_KEYSTORE=""
export ANDROID_KEYSTORE_PASSWORD=""
export ANDROID_KEYSTORE_ALIAS=""
export ANDROID_KEYSTORE_KEY_PASSWORD=""
export SUPPLY_JSON_KEY_DATA=""
# Fastlane end

50
.github/workflows/development.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: Build development
on:
push:
branches:
- development
jobs:
build:
runs-on: macos-latest
steps:
- name: -- Step 1 -- Checkout code
uses: actions/checkout@v2
- name: -- Step 2 -- Setup node
uses: actions/setup-node@v2
with:
node-version: 14.x
- name: -- Step 3 -- Use Expo action
uses: expo/expo-github-action@v5
with:
expo-version: 4.x
expo-username: ${{ secrets.EXPO_USERNAME }}
expo-token: ${{ secrets.EXPO_TOKEN }}
- name: -- Step 4 -- Install node dependencies
run: yarn install
- name: -- Step 5 -- Install native dependencies
run: npx pod-install
- name: -- Step 6 -- Install ruby dependencies
run: bundle install
- name: -- Step 7 -- Run fastlane
env:
TOOOT_ENVIRONMENT: development
SENTRY_ORGANIZATION: ${{ secrets.SENTRY_ORGANIZATION }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_KEYSTORE_ALIAS }}
ANDROID_KEYSTORE_KEY_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_KEY_PASSWORD }}
SUPPLY_JSON_KEY_DATA: ${{ secrets.SUPPLY_JSON_KEY_DATA }}
FL_GITHUB_RELEASE_API_BEARER: ${{ secrets.GITHUB_TOKEN }}
run: yarn app:build

View File

@ -15,7 +15,7 @@ case ENVIRONMENT
when "development" when "development"
GITHUB_RELEASE= "" GITHUB_RELEASE= ""
when "staging" when "staging"
GITHUB_RELEASE = "v#{VERSION}(#{BUILD_NUMBER})" GITHUB_RELEASE = "v#{VERSION}-rc#{VERSIONS[:patch]}"
when "production" when "production"
GITHUB_RELEASE = "v#{VERSION}" GITHUB_RELEASE = "v#{VERSION}"
end end
@ -44,20 +44,14 @@ private_lane :prepare_playstore_android do
end end
desc "Create new GitHub release" desc "Create new GitHub release"
private_lane :github_release do private_lane :github_release do |options|
case ENVIRONMENT
when "staging"
is_prerelease = true
when "production"
is_prerelease = false
end
set_github_release( set_github_release(
repository_name: GITHUB_REPO, repository_name: GITHUB_REPO,
name: GITHUB_RELEASE, name: GITHUB_RELEASE,
tag_name: GITHUB_RELEASE, tag_name: GITHUB_RELEASE,
description: "No changelog provided", description: "No changelog provided",
commitish: git_branch, commitish: git_branch,
is_prerelease: is_prerelease is_prerelease: options[:prerelease]
) )
end end
@ -76,8 +70,10 @@ private_lane :build_ios do
case ENVIRONMENT case ENVIRONMENT
when "development" when "development"
match( type: "development", readonly: true ) match( type: "development", readonly: true )
build_ios_app( export_method: "development", output_directory: BUILD_DIRECTORY, output_name: "#{VERSION}-#{BUILD_NUMBER}" ) if !is_ci
install_on_device( skip_wifi: true ) build_ios_app( export_method: "development", output_directory: BUILD_DIRECTORY )
install_on_device( skip_wifi: true )
end
when "staging" when "staging"
prepare_appstore_ios prepare_appstore_ios
match( type: "appstore", readonly: true ) match( type: "appstore", readonly: true )
@ -101,14 +97,16 @@ private_lane :build_android do
case ENVIRONMENT case ENVIRONMENT
when "development" when "development"
build_android_app( if !is_ci
task: 'assemble', build_android_app(
build_type: 'debug', task: 'assemble',
project_dir: "./android" build_type: 'debug',
) project_dir: "./android"
adb( )
command: "install #{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]}" adb(
) command: "install #{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]}"
)
end
when "staging" when "staging"
prepare_playstore_android prepare_playstore_android
build_android_app( build_android_app(
@ -127,7 +125,7 @@ private_lane :build_android do
} }
) )
upload_to_play_store( upload_to_play_store(
track: "alpha", track: "beta",
skip_upload_metadata: true, skip_upload_metadata: true,
skip_upload_changelogs: true, skip_upload_changelogs: true,
skip_upload_images: true, skip_upload_images: true,
@ -147,9 +145,9 @@ lane :build do
build_android build_android
case ENVIRONMENT case ENVIRONMENT
when "staging" when "staging"
github_release github_release(prerelease: true)
when "production" when "production"
github_release github_release(prerelease: false)
end end
end end
expo_release expo_release

View File

@ -112,7 +112,7 @@
"native": "210201", "native": "210201",
"major": 0, "major": 0,
"minor": 3, "minor": 3,
"patch": 0, "patch": 1,
"expo": "40.0.0" "expo": "40.0.0"
} }
} }

View File

@ -1,11 +1,8 @@
import Button from '@components/Button' import Button from '@components/Button'
import haptics from '@components/haptics'
import Icon from '@components/Icon' import Icon from '@components/Icon'
import { useNavigation } from '@react-navigation/native'
import { useAppsQuery } from '@utils/queryHooks/apps' import { useAppsQuery } from '@utils/queryHooks/apps'
import { useInstanceQuery } from '@utils/queryHooks/instance' import { useInstanceQuery } from '@utils/queryHooks/instance'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getLocalInstances } from '@utils/slices/instancesSlice'
import { getLocalInstances, remoteUpdate } 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 * as WebBrowser from 'expo-web-browser' import * as WebBrowser from 'expo-web-browser'
@ -13,39 +10,30 @@ import { debounce } from 'lodash'
import React, { useCallback, useMemo, useState } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Alert, Image, StyleSheet, Text, TextInput, View } from 'react-native' import { Alert, Image, StyleSheet, Text, TextInput, View } from 'react-native'
import { useQueryClient } from 'react-query' import { useSelector } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
import { Placeholder, Fade } from 'rn-placeholder' import { Placeholder, Fade } from 'rn-placeholder'
import analytics from './analytics' import analytics from './analytics'
import InstanceAuth from './Instance/Auth' import InstanceAuth from './Instance/Auth'
import EULA from './Instance/EULA' import EULA from './Instance/EULA'
import InstanceInfo from './Instance/Info' import InstanceInfo from './Instance/Info'
import { toast } from './toast'
export interface Props { export interface Props {
type: 'local' | 'remote'
disableHeaderImage?: boolean disableHeaderImage?: boolean
goBack?: boolean goBack?: boolean
} }
const ComponentInstance: React.FC<Props> = ({ const ComponentInstance: React.FC<Props> = ({
type,
disableHeaderImage, disableHeaderImage,
goBack = false goBack = false
}) => { }) => {
const { t, i18n } = useTranslation('componentInstance') const { t } = useTranslation('componentInstance')
const { theme } = useTheme() const { theme } = useTheme()
const navigation = useNavigation()
const localInstances = useSelector(getLocalInstances) const localInstances = useSelector(getLocalInstances)
const dispatch = useDispatch()
const queryClient = useQueryClient()
const [instanceDomain, setInstanceDomain] = useState<string>() const [instanceDomain, setInstanceDomain] = useState<string>()
const instanceQuery = useInstanceQuery({ const instanceQuery = useInstanceQuery({
instanceDomain, instanceDomain,
checkPublic: type === 'remote',
options: { enabled: false, retry: false } options: { enabled: false, retry: false }
}) })
const appsQuery = useAppsQuery({ const appsQuery = useAppsQuery({
@ -70,46 +58,30 @@ const ComponentInstance: React.FC<Props> = ({
const processUpdate = useCallback(() => { const processUpdate = useCallback(() => {
if (instanceDomain) { if (instanceDomain) {
switch (type) { analytics('instance_local_login')
case 'local': if (
analytics('instance_local_login') localInstances &&
if ( localInstances.filter(instance => instance.url === instanceDomain)
localInstances && .length
localInstances.filter(instance => instance.url === instanceDomain) ) {
.length Alert.alert(
) { t('update.local.alert.title'),
Alert.alert( t('update.local.alert.message'),
t('update.local.alert.title'), [
t('update.local.alert.message'), {
[ text: t('update.local.alert.buttons.cancel'),
{ style: 'cancel'
text: t('update.local.alert.buttons.cancel'), },
style: 'cancel' {
}, text: t('update.local.alert.buttons.continue'),
{ onPress: () => {
text: t('update.local.alert.buttons.continue'), appsQuery.refetch()
onPress: () => { }
appsQuery.refetch() }
}
}
]
)
} else {
appsQuery.refetch()
}
break
case 'remote':
analytics('instance_remote_register')
haptics('Success')
const queryKey: QueryKeyTimeline = [
'Timeline',
{ page: 'RemotePublic' }
] ]
dispatch(remoteUpdate(instanceDomain)) )
queryClient.resetQueries(queryKey) } else {
toast({ type: 'success', message: t('update.remote.succeed') }) appsQuery.refetch()
navigation.goBack()
break
} }
} }
}, [instanceDomain]) }, [instanceDomain])
@ -129,15 +101,6 @@ const ComponentInstance: React.FC<Props> = ({
[instanceDomain, instanceQuery.isSuccess, instanceQuery.data] [instanceDomain, instanceQuery.isSuccess, instanceQuery.data]
) )
const buttonContent = useMemo(() => {
switch (type) {
case 'local':
return t('server.button.local')
case 'remote':
return t('server.button.remote')
}
}, [i18n.language])
const requestAuth = useMemo(() => { const requestAuth = useMemo(() => {
if ( if (
instanceDomain && instanceDomain &&
@ -179,12 +142,9 @@ const ComponentInstance: React.FC<Props> = ({
styles.textInput, styles.textInput,
{ {
color: theme.primary, color: theme.primary,
borderBottomColor: borderBottomColor: instanceQuery.isError
type === 'remote' && ? theme.red
instanceQuery.data && : theme.border
!instanceQuery.data.publicAllow
? theme.red
: theme.border
} }
]} ]}
onChangeText={onChangeText} onChangeText={onChangeText}
@ -200,13 +160,9 @@ const ComponentInstance: React.FC<Props> = ({
/> />
<Button <Button
type='text' type='text'
content={buttonContent} content={t('server.button.local')}
onPress={processUpdate} onPress={processUpdate}
disabled={ disabled={!instanceQuery.data?.uri || !agreed}
!instanceQuery.data?.uri ||
(type === 'remote' && !instanceQuery.data.publicAllow) ||
!agreed
}
loading={instanceQuery.isFetching || appsQuery.isFetching} loading={instanceQuery.isFetching || appsQuery.isFetching}
/> />
</View> </View>
@ -262,34 +218,32 @@ const ComponentInstance: React.FC<Props> = ({
/> />
</View> </View>
</Placeholder> </Placeholder>
{type === 'local' ? ( <View style={styles.disclaimer}>
<View style={styles.disclaimer}> <Icon
<Icon name='Lock'
name='Lock' size={StyleConstants.Font.Size.S}
size={StyleConstants.Font.Size.S} color={theme.secondary}
color={theme.secondary} style={styles.disclaimerIcon}
style={styles.disclaimerIcon} />
/> <Text style={[styles.disclaimerText, { color: theme.secondary }]}>
<Text style={[styles.disclaimerText, { color: theme.secondary }]}> {t('server.disclaimer.base')}
{t('server.disclaimer.base')} <Text
<Text style={{ color: theme.blue }}
style={{ color: theme.blue }} onPress={() => {
onPress={() => { analytics('view_privacy')
analytics('view_privacy') WebBrowser.openBrowserAsync(
WebBrowser.openBrowserAsync( 'https://tooot.app/privacy-policy'
'https://tooot.app/privacy-policy' )
) }}
}} >
> {t('server.disclaimer.privacy')}
{t('server.disclaimer.privacy')}
</Text>
</Text> </Text>
</View> </Text>
) : null} </View>
</View> </View>
</View> </View>
{type === 'local' ? requestAuth : null} {requestAuth}
</> </>
) )
} }

View File

@ -94,7 +94,7 @@ const AttachmentVideo: React.FC<Props> = ({
}} }}
/> />
) : null ) : null
) : gifv ? null : ( ) : (
<Button <Button
round round
overlay overlay

View File

@ -48,7 +48,7 @@ const ScreenMeRoot: React.FC = () => {
{localActiveIndex !== null ? ( {localActiveIndex !== null ? (
<MyInfo setData={setData} /> <MyInfo setData={setData} />
) : ( ) : (
<ComponentInstance type='local' /> <ComponentInstance />
)} )}
{localActiveIndex !== null ? <Collections /> : null} {localActiveIndex !== null ? <Collections /> : null}
<Settings /> <Settings />

View File

@ -102,7 +102,7 @@ const ScreenMeSwitchRoot: React.FC = () => {
<Text style={[styles.header, { color: theme.primary }]}> <Text style={[styles.header, { color: theme.primary }]}>
{t('content.new')} {t('content.new')}
</Text> </Text>
<ComponentInstance type='local' disableHeaderImage goBack /> <ComponentInstance disableHeaderImage goBack />
</View> </View>
</ScrollView> </ScrollView>
</KeyboardAvoidingView> </KeyboardAvoidingView>

View File

@ -2,43 +2,17 @@ import client from '@api/client'
import { AxiosError } from 'axios' import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query' import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = [ export type QueryKey = ['Instance', { instanceDomain?: string }]
'Instance',
{ instanceDomain?: string; checkPublic: boolean }
]
const queryFunction = async ({ queryKey }: { queryKey: QueryKey }) => { const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { instanceDomain, checkPublic } = queryKey[1] const { instanceDomain } = queryKey[1]
let res: Mastodon.Instance & { publicAllow?: boolean } = await client< return client<Mastodon.Instance>({
Mastodon.Instance
>({
method: 'get', method: 'get',
instance: 'remote', instance: 'remote',
instanceDomain, instanceDomain,
url: `instance` url: `instance`
}) })
if (checkPublic) {
let check
try {
check = await client<Mastodon.Status[]>({
method: 'get',
instance: 'remote',
instanceDomain,
url: `timelines/public`
})
} catch {}
if (check) {
res.publicAllow = true
return res
} else {
res.publicAllow = false
return res
}
}
return res
} }
const useInstanceQuery = < const useInstanceQuery = <