Merge pull request #186 from tooot-app/main

Finally ready to release v3
This commit is contained in:
xmflsct 2021-12-15 23:35:23 +01:00 committed by GitHub
commit dc46c441e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 189 additions and 128 deletions

View File

@ -5,7 +5,7 @@ export SENTRY_PROJECT=""
export SENTRY_AUTH_TOKEN=""
export SENTRY_DSN=""
export TOOOT_API_KEY=""
export TOOOT_PUSH_KEY_PUBLIC=""
# Fastlane start
export LC_ALL=""

View File

@ -41,7 +41,7 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
TOOOT_API_KEY: ${{ secrets.TOOOT_API_KEY }}
TOOOT_PUSH_KEY_PUBLIC: ${{ secrets.TOOOT_PUSH_KEY_PUBLIC }}
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}

View File

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.4)
CFPropertyList (3.0.5)
rexml
activesupport (6.1.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
@ -17,17 +17,17 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.518.0)
aws-sdk-core (3.121.3)
aws-partitions (1.541.0)
aws-sdk-core (3.124.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.50.0)
aws-sdk-core (~> 3, >= 3.121.2)
aws-sdk-kms (1.52.0)
aws-sdk-core (~> 3, >= 3.122.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.104.0)
aws-sdk-core (~> 3, >= 3.121.2)
aws-sdk-s3 (1.109.0)
aws-sdk-core (~> 3, >= 3.122.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
@ -86,7 +86,7 @@ GEM
escape (0.0.4)
ethon (0.15.0)
ffi (>= 1.15.0)
excon (0.87.0)
excon (0.89.0)
faraday (1.8.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
@ -112,7 +112,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.5)
fastlane (2.197.0)
fastlane (2.199.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@ -152,14 +152,14 @@ GEM
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-json (1.0.0)
fastlane-plugin-sentry (1.10.1)
fastlane-plugin-sentry (1.11.0)
fastlane-plugin-versioning_android (0.1.0)
fastlane-plugin-yarn (1.2)
ffi (1.15.4)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.12.0)
google-apis-androidpublisher_v3 (0.14.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.1)
addressable (~> 2.5, >= 2.5.1)
@ -170,11 +170,11 @@ GEM
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.7.0)
google-apis-iamcredentials_v1 (0.9.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.5.0)
google-apis-playcustomapp_v1 (0.6.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.8.0)
google-apis-storage_v1 (0.10.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
@ -182,15 +182,15 @@ GEM
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.34.1)
addressable (~> 2.5)
google-cloud-storage (1.35.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.0.0)
googleauth (1.1.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@ -218,7 +218,7 @@ GEM
naturally (2.2.1)
netrc (0.11.0)
optparse (0.1.1)
os (1.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.6)
rake (13.0.6)
@ -244,7 +244,7 @@ GEM
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.1)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
@ -275,6 +275,7 @@ GEM
PLATFORMS
universal-darwin-20
universal-darwin-21
DEPENDENCIES
cocoapods

View File

@ -13,7 +13,7 @@ export default (): ExpoConfig => ({
assetBundlePatterns: ['assets/*'],
extra: {
sentryDSN: process.env.SENTRY_DSN,
toootApiKey: process.env.TOOOT_API_KEY
toootPushKeyPublic: process.env.TOOOT_PUSH_KEY_PUBLIC
},
hooks: {
postPublish: [

View File

@ -144,6 +144,7 @@
}
},
"push": {
"notAvailable": "Your phone does not support tooot's push notification",
"enable": {
"direct": "Enable push notification",
"settings": "Enable in settings"
@ -184,6 +185,12 @@
"empty": "None"
}
},
"push": {
"content": {
"enabled": "Enabled",
"disabled": "Disabled"
}
},
"update": {
"title": "Update to latest version"
},

View File

@ -144,6 +144,7 @@
}
},
"push": {
"notAvailable": "您的机型暂不支持 tooot 推送通知",
"enable": {
"direct": "启用推送通知",
"settings": "在系统设置中启用"
@ -184,6 +185,12 @@
"empty": "无公告"
}
},
"push": {
"content": {
"enabled": "已启用",
"disabled": "未启用"
}
},
"update": {
"title": "更新至最新版本"
},

View File

@ -20,9 +20,9 @@ import FastImage from 'react-native-fast-image'
import { FlatList, ScrollView } from 'react-native-gesture-handler'
import { SafeAreaView } from 'react-native-safe-area-context'
const ScreenAnnouncements: React.FC<RootStackScreenProps<
'Screen-Announcements'
>> = ({
const ScreenAnnouncements: React.FC<
RootStackScreenProps<'Screen-Announcements'>
> = ({
route: {
params: { showAll = false }
},
@ -250,6 +250,7 @@ const styles = StyleSheet.create({
announcementContainer: {
width: Dimensions.get('screen').width,
padding: StyleConstants.Spacing.Global.PagePadding,
marginVertical: StyleConstants.Spacing.Global.PagePadding,
justifyContent: 'center'
},
published: {

View File

@ -61,9 +61,11 @@ const HeaderComponent = React.memo(
analytics('imageviewer_more_share_press')
switch (Platform.OS) {
case 'ios':
Share.share({ url: imageUrls[currentIndex].url })
await Share.share({ url: imageUrls[currentIndex].url })
break
case 'android':
Share.share({ message: imageUrls[currentIndex].url })
await Share.share({ message: imageUrls[currentIndex].url })
break
}
break
}

View File

@ -1,5 +1,6 @@
import analytics from '@components/analytics'
import Button from '@components/Button'
import Icon from '@components/Icon'
import { MenuContainer, MenuRow } from '@components/Menu'
import { updateInstancePush } from '@utils/slices/instances/updatePush'
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
@ -12,14 +13,16 @@ import {
} from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager'
import * as Notifications from 'expo-notifications'
import * as WebBrowser from 'expo-web-browser'
import React, { useState, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { AppState, Linking, ScrollView } from 'react-native'
import { AppState, Linking, ScrollView, Text, View } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
const TabMePush: React.FC = () => {
const { theme } = useTheme()
const { t } = useTranslation('screenTabs')
const instanceAccount = useSelector(
getInstanceAccount,
@ -30,6 +33,9 @@ const TabMePush: React.FC = () => {
const dispatch = useDispatch()
const instancePush = useSelector(getInstancePush)
const [pushAvailable, setPushAvailable] = useState<boolean | undefined>(
undefined
)
const [pushEnabled, setPushEnabled] = useState<boolean>()
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
const checkPush = async () => {
@ -39,10 +45,15 @@ const TabMePush: React.FC = () => {
setPushCanAskAgain(settings.canAskAgain)
}
useEffect(() => {
Notifications.getExpoPushTokenAsync({
experienceId: '@xmflsct/tooot'
})
.then(data => setPushAvailable(!!data))
.catch(() => setPushAvailable(false))
checkPush()
AppState.addEventListener('change', checkPush)
const subscription = AppState.addEventListener('change', checkPush)
return () => {
AppState.removeEventListener('change', checkPush)
subscription.remove()
}
}, [])
@ -54,13 +65,15 @@ const TabMePush: React.FC = () => {
const alerts = useMemo(() => {
return instancePush?.alerts
? (['follow', 'favourite', 'reblog', 'mention', 'poll'] as [
'follow',
'favourite',
'reblog',
'mention',
'poll'
]).map(alert => (
? (
['follow', 'favourite', 'reblog', 'mention', 'poll'] as [
'follow',
'favourite',
'reblog',
'mention',
'poll'
]
).map(alert => (
<MenuRow
key={alert}
title={t(`me.push.${alert}.heading`)}
@ -93,80 +106,108 @@ const TabMePush: React.FC = () => {
return (
<ScrollView>
{pushEnabled === false ? (
<MenuContainer>
<Button
type='text'
content={
pushCanAskAgain
? t('me.push.enable.direct')
: t('me.push.enable.settings')
}
style={{
marginTop: StyleConstants.Spacing.Global.PagePadding,
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
}}
onPress={async () => {
if (pushCanAskAgain) {
analytics('me_push_enabled_dialogue')
const result = await Notifications.requestPermissionsAsync()
setPushEnabled(result.granted)
setPushCanAskAgain(result.canAskAgain)
} else {
analytics('me_push_enabled_setting')
Linking.openSettings()
{pushAvailable !== false ? (
<>
{pushEnabled === false ? (
<MenuContainer>
<Button
type='text'
content={
pushCanAskAgain
? t('me.push.enable.direct')
: t('me.push.enable.settings')
}
style={{
marginTop: StyleConstants.Spacing.Global.PagePadding,
marginHorizontal:
StyleConstants.Spacing.Global.PagePadding * 2
}}
onPress={async () => {
if (pushCanAskAgain) {
analytics('me_push_enabled_dialogue')
const result = await Notifications.requestPermissionsAsync()
setPushEnabled(result.granted)
setPushCanAskAgain(result.canAskAgain)
} else {
analytics('me_push_enabled_setting')
Linking.openSettings()
}
}}
/>
</MenuContainer>
) : null}
<MenuContainer>
<MenuRow
title={t('me.push.global.heading', {
acct: `@${instanceAccount?.acct}@${instanceUri}`
})}
description={t('me.push.global.description')}
loading={instancePush?.global.loading}
switchDisabled={!pushEnabled || isLoading}
switchValue={
pushEnabled === false ? false : instancePush?.global.value
}
}}
switchOnValueChange={() => {
analytics('me_push_global', {
current: instancePush?.global.value,
new: !instancePush?.global.value
})
dispatch(updateInstancePush(!instancePush?.global.value))
}}
/>
</MenuContainer>
<MenuContainer>
<MenuRow
title={t('me.push.decode.heading')}
description={t('me.push.decode.description')}
loading={instancePush?.decode.loading}
switchDisabled={
!pushEnabled || !instancePush?.global.value || isLoading
}
switchValue={instancePush?.decode.value}
switchOnValueChange={() => {
analytics('me_push_decode', {
current: instancePush?.decode.value,
new: !instancePush?.decode.value
})
dispatch(updateInstancePushDecode(!instancePush?.decode.value))
}}
/>
<MenuRow
title={t('me.push.howitworks')}
iconBack='ExternalLink'
onPress={() => {
analytics('me_push_howitworks')
WebBrowser.openBrowserAsync('https://tooot.app/how-push-works')
}}
/>
</MenuContainer>
<MenuContainer>{alerts}</MenuContainer>
</>
) : (
<View
style={{
flex: 1,
minHeight: '100%',
justifyContent: 'center',
alignItems: 'center'
}}
>
<Icon
name='Frown'
size={StyleConstants.Font.Size.L}
color={theme.primaryDefault}
/>
</MenuContainer>
) : null}
<MenuContainer>
<MenuRow
title={t('me.push.global.heading', {
acct: `@${instanceAccount?.acct}@${instanceUri}`
})}
description={t('me.push.global.description')}
loading={instancePush?.global.loading}
switchDisabled={!pushEnabled || isLoading}
switchValue={
pushEnabled === false ? false : instancePush?.global.value
}
switchOnValueChange={() => {
analytics('me_push_global', {
current: instancePush?.global.value,
new: !instancePush?.global.value
})
dispatch(updateInstancePush(!instancePush?.global.value))
}}
/>
</MenuContainer>
<MenuContainer>
<MenuRow
title={t('me.push.decode.heading')}
description={t('me.push.decode.description')}
loading={instancePush?.decode.loading}
switchDisabled={
!pushEnabled || !instancePush?.global.value || isLoading
}
switchValue={instancePush?.decode.value}
switchOnValueChange={() => {
analytics('me_push_decode', {
current: instancePush?.decode.value,
new: !instancePush?.decode.value
})
dispatch(updateInstancePushDecode(!instancePush?.decode.value))
}}
/>
<MenuRow
title={t('me.push.howitworks')}
iconBack='ExternalLink'
onPress={() => {
analytics('me_push_howitworks')
WebBrowser.openBrowserAsync('https://tooot.app/how-push-works')
}}
/>
</MenuContainer>
<MenuContainer>{alerts}</MenuContainer>
<Text
style={{
...StyleConstants.FontStyle.M,
color: theme.primaryDefault
}}
>
{t('me.push.notAvailable')}
</Text>
</View>
)}
</ScrollView>
)
}

View File

@ -3,6 +3,7 @@ import { useNavigation } from '@react-navigation/native'
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
import { useListsQuery } from '@utils/queryHooks/lists'
import { getMePage, updateContextMePage } from '@utils/slices/contextsSlice'
import { getInstancePush } from '@utils/slices/instancesSlice'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
@ -53,6 +54,11 @@ const Collections: React.FC = () => {
}
}, [announcementsQuery.isSuccess, announcementsQuery.data?.length])
const instancePush = useSelector(
getInstancePush,
(prev, next) => prev?.global.value === next?.global.value
)
return (
<MenuContainer>
<MenuRow
@ -98,6 +104,17 @@ const Collections: React.FC = () => {
}
/>
) : null}
<MenuRow
iconFront={instancePush ? 'Bell' : 'BellOff'}
iconBack='ChevronRight'
title={t('me.stacks.push.name')}
content={
instancePush
? t('me.root.push.content.enabled')
: t('me.root.push.content.disabled')
}
onPress={() => navigation.navigate('Tab-Me-Push')}
/>
</MenuContainer>
)
}

View File

@ -3,10 +3,7 @@ import Button from '@components/Button'
import { RelationshipOutgoing } from '@components/Relationship'
import { useNavigation } from '@react-navigation/native'
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
import {
getInstanceAccount,
getInstancePush
} from '@utils/slices/instancesSlice'
import { getInstanceAccount } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import React from 'react'
import { useTranslation } from 'react-i18next'
@ -56,7 +53,6 @@ const AccountInformationActions: React.FC<Props> = ({ account, myInfo }) => {
content={t('shared.account.moved')}
onPress={() => {
analytics('account_gotomoved_press')
// @ts-ignore
navigation.push('Tab-Shared-Account', { account: accountMoved })
}}
/>
@ -64,21 +60,9 @@ const AccountInformationActions: React.FC<Props> = ({ account, myInfo }) => {
)
}
const instancePush = useSelector(
getInstancePush,
(prev, next) => prev?.global.value === next?.global.value
)
if (myInfo) {
return (
<View style={styles.base}>
<Button
round
type='icon'
content={instancePush?.global.value ? 'Bell' : 'BellOff'}
style={styles.actionLeft}
onPress={() => navigation.navigate('Tab-Me-Push')}
/>
<Button
type='text'
disabled={account === undefined}

View File

@ -3,6 +3,7 @@ import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot'
import i18n from '@root/i18n/i18n'
import { RootState } from '@root/store'
import { getInstance, Instance } from '@utils/slices/instancesSlice'
import Constants from 'expo-constants'
import * as Notifications from 'expo-notifications'
import * as Random from 'expo-random'
import { Platform } from 'react-native'
@ -57,7 +58,7 @@ const pushRegister = async (
formData.append('subscription[endpoint]', endpoint)
formData.append(
'subscription[keys][p256dh]',
'BO3P7Fe/FxPNijeXayVYViCoLicnnACc+a55wzcS0qIjYU++dtAl2XltgEfU5qPuXrFg5rnxBzbwQG4cAmdNLK4='
Constants.manifest?.extra?.toootPushKeyPublic
)
formData.append('subscription[keys][auth]', auth)
Object.keys(alerts).map(key =>