mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Rewrite timeline logic
This commit is contained in:
@ -75,8 +75,16 @@ const addInstance = createAsyncThunk(
|
||||
latestTime: undefined
|
||||
},
|
||||
push: {
|
||||
loading: false,
|
||||
enabled: false
|
||||
global: { loading: false, value: false },
|
||||
decode: { loading: false, value: false },
|
||||
alerts: {
|
||||
follow: { loading: false, value: true },
|
||||
favourite: { loading: false, value: true },
|
||||
reblog: { loading: false, value: true },
|
||||
mention: { loading: false, value: true },
|
||||
poll: { loading: false, value: true }
|
||||
},
|
||||
keys: undefined
|
||||
},
|
||||
drafts: []
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
import apiGeneral from '@api/general'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
|
||||
const serverUnregister = async () => {
|
||||
const deviceToken = (await Notifications.getDevicePushTokenAsync()).data
|
||||
|
||||
return apiGeneral<{ endpoint: string; publicKey: string; auth: string }>({
|
||||
method: 'post',
|
||||
domain: 'testpush.home.xmflsct.com',
|
||||
url: 'unregister',
|
||||
body: { deviceToken }
|
||||
})
|
||||
}
|
||||
|
||||
const pushDisable = async () => {
|
||||
await serverUnregister()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export default pushDisable
|
@ -1,61 +0,0 @@
|
||||
import apiGeneral from '@api/general'
|
||||
import apiInstance from '@api/instance'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
const serverRegister = async () => {
|
||||
const deviceToken = (await Notifications.getDevicePushTokenAsync()).data
|
||||
const expoToken = (
|
||||
await Notifications.getExpoPushTokenAsync({
|
||||
experienceId: '@xmflsct/tooot'
|
||||
})
|
||||
).data
|
||||
|
||||
return apiGeneral<{ endpoint: string; publicKey: string; auth: string }>({
|
||||
method: 'post',
|
||||
domain: 'testpush.home.xmflsct.com',
|
||||
url: 'register',
|
||||
body: { deviceToken, expoToken }
|
||||
})
|
||||
}
|
||||
|
||||
const pushEnable = async (): Promise<Mastodon.PushSubscription> => {
|
||||
const { status: existingStatus } = await Notifications.getPermissionsAsync()
|
||||
let finalStatus = existingStatus
|
||||
if (existingStatus !== 'granted') {
|
||||
const { status } = await Notifications.requestPermissionsAsync()
|
||||
finalStatus = status
|
||||
}
|
||||
if (finalStatus !== 'granted') {
|
||||
alert('Failed to get push token for push notification!')
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
const serverRes = (await serverRegister()).body
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append(
|
||||
'subscription[endpoint]',
|
||||
'https://testpush.home.xmflsct.com/test1'
|
||||
)
|
||||
formData.append('subscription[keys][p256dh]', serverRes.publicKey)
|
||||
formData.append('subscription[keys][auth]', serverRes.auth)
|
||||
|
||||
const res = await apiInstance<Mastodon.PushSubscription>({
|
||||
method: 'post',
|
||||
url: 'push/subscription',
|
||||
body: formData
|
||||
})
|
||||
return res.body
|
||||
|
||||
// if (Platform.OS === 'android') {
|
||||
// Notifications.setNotificationChannelAsync('default', {
|
||||
// name: 'default',
|
||||
// importance: Notifications.AndroidImportance.MAX,
|
||||
// vibrationPattern: [0, 250, 250, 250],
|
||||
// lightColor: '#FF231F7C'
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
export default pushEnable
|
127
src/utils/slices/instances/push/register.ts
Normal file
127
src/utils/slices/instances/push/register.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import apiGeneral from '@api/general'
|
||||
import apiInstance from '@api/instance'
|
||||
import { RootState } from '@root/store'
|
||||
import {
|
||||
getInstance,
|
||||
Instance,
|
||||
PUSH_SERVER
|
||||
} from '@utils/slices/instancesSlice'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
const register1 = async ({
|
||||
expoToken,
|
||||
instanceUrl,
|
||||
accountId,
|
||||
accountFull
|
||||
}: {
|
||||
expoToken: string
|
||||
instanceUrl: string
|
||||
accountId: Mastodon.Account['id']
|
||||
accountFull: string
|
||||
}) => {
|
||||
return apiGeneral<{
|
||||
endpoint: string
|
||||
keys: { public: string; private: string; auth: string }
|
||||
}>({
|
||||
method: 'post',
|
||||
domain: PUSH_SERVER,
|
||||
url: 'v1/register1',
|
||||
body: { expoToken, instanceUrl, accountId, accountFull }
|
||||
})
|
||||
}
|
||||
|
||||
const register2 = async ({
|
||||
expoToken,
|
||||
serverKey,
|
||||
instanceUrl,
|
||||
accountId,
|
||||
removeKeys
|
||||
}: {
|
||||
expoToken: string
|
||||
serverKey: Mastodon.PushSubscription['server_key']
|
||||
instanceUrl: string
|
||||
accountId: Mastodon.Account['id']
|
||||
removeKeys: boolean
|
||||
}) => {
|
||||
return apiGeneral({
|
||||
method: 'post',
|
||||
domain: PUSH_SERVER,
|
||||
url: 'v1/register2',
|
||||
body: { expoToken, instanceUrl, accountId, serverKey, removeKeys }
|
||||
})
|
||||
}
|
||||
|
||||
const pushRegister = async (
|
||||
state: RootState,
|
||||
expoToken: string
|
||||
): Promise<Instance['push']['keys']> => {
|
||||
const instance = getInstance(state)
|
||||
const instanceUrl = instance?.url
|
||||
const instanceUri = instance?.uri
|
||||
const instanceAccount = instance?.account
|
||||
const instancePush = instance?.push
|
||||
|
||||
if (!instanceUrl || !instanceUri || !instanceAccount || !instancePush) {
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
const { status: existingStatus } = await Notifications.getPermissionsAsync()
|
||||
let finalStatus = existingStatus
|
||||
if (existingStatus !== 'granted') {
|
||||
const { status } = await Notifications.requestPermissionsAsync()
|
||||
finalStatus = status
|
||||
}
|
||||
if (finalStatus !== 'granted') {
|
||||
alert('Failed to get push token for push notification!')
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
const accountId = instanceAccount.id
|
||||
const accountFull = `@${instanceAccount.acct}@${instanceUri}`
|
||||
const serverRes = await register1({
|
||||
expoToken,
|
||||
instanceUrl,
|
||||
accountId,
|
||||
accountFull
|
||||
})
|
||||
console.log('endpoint', serverRes.body.endpoint)
|
||||
console.log('token', instance?.token)
|
||||
|
||||
const alerts = instancePush.alerts
|
||||
const formData = new FormData()
|
||||
formData.append('subscription[endpoint]', serverRes.body.endpoint)
|
||||
formData.append('subscription[keys][p256dh]', serverRes.body.keys.public)
|
||||
formData.append('subscription[keys][auth]', serverRes.body.keys.auth)
|
||||
Object.keys(alerts).map(key =>
|
||||
// @ts-ignore
|
||||
formData.append(`data[alerts][${key}]`, alerts[key].value.toString())
|
||||
)
|
||||
|
||||
const res = await apiInstance<Mastodon.PushSubscription>({
|
||||
method: 'post',
|
||||
url: 'push/subscription',
|
||||
body: formData
|
||||
})
|
||||
|
||||
await register2({
|
||||
expoToken,
|
||||
serverKey: res.body.server_key,
|
||||
instanceUrl,
|
||||
accountId,
|
||||
removeKeys: instancePush.decode.value === false
|
||||
})
|
||||
|
||||
return Promise.resolve(serverRes.body.keys)
|
||||
|
||||
// if (Platform.OS === 'android') {
|
||||
// Notifications.setNotificationChannelAsync('default', {
|
||||
// name: 'default',
|
||||
// importance: Notifications.AndroidImportance.MAX,
|
||||
// vibrationPattern: [0, 250, 250, 250],
|
||||
// lightColor: '#FF231F7C'
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
export default pushRegister
|
32
src/utils/slices/instances/push/unregister.ts
Normal file
32
src/utils/slices/instances/push/unregister.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import apiGeneral from '@api/general'
|
||||
import apiInstance from '@api/instance'
|
||||
import { RootState } from '@root/store'
|
||||
import { getInstance, PUSH_SERVER } from '@utils/slices/instancesSlice'
|
||||
|
||||
const pushUnregister = async (state: RootState, expoToken: string) => {
|
||||
const instance = getInstance(state)
|
||||
|
||||
if (!instance?.url || !instance.account.id) {
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
await apiInstance<{}>({
|
||||
method: 'delete',
|
||||
url: 'push/subscription'
|
||||
})
|
||||
|
||||
await apiGeneral<{ endpoint: string; publicKey: string; auth: string }>({
|
||||
method: 'post',
|
||||
domain: PUSH_SERVER,
|
||||
url: 'v1/unregister',
|
||||
body: {
|
||||
expoToken,
|
||||
instanceUrl: instance.url,
|
||||
accountId: instance.account.id
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
export default pushUnregister
|
@ -1,17 +1,27 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||
import { RootState } from '@root/store'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
import { Instance } from '../instancesSlice'
|
||||
import pushDisable from './push/disable'
|
||||
import pushEnable from './push/enable'
|
||||
import pushRegister from './push/register'
|
||||
import pushUnregister from './push/unregister'
|
||||
|
||||
export const updatePush = createAsyncThunk(
|
||||
export const updateInstancePush = createAsyncThunk(
|
||||
'instances/updatePush',
|
||||
async (
|
||||
enable: boolean
|
||||
): Promise<Instance['push']['subscription'] | boolean> => {
|
||||
if (enable) {
|
||||
return pushEnable()
|
||||
disable: boolean,
|
||||
{ getState }
|
||||
): Promise<Instance['push']['keys'] | undefined> => {
|
||||
const state = getState() as RootState
|
||||
const expoToken = (
|
||||
await Notifications.getExpoPushTokenAsync({
|
||||
experienceId: '@xmflsct/tooot'
|
||||
})
|
||||
).data
|
||||
|
||||
if (disable) {
|
||||
return await pushRegister(state, expoToken)
|
||||
} else {
|
||||
return pushDisable()
|
||||
return await pushUnregister(state, expoToken)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
27
src/utils/slices/instances/updatePushAlert.ts
Normal file
27
src/utils/slices/instances/updatePushAlert.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import apiInstance from '@api/instance'
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||
import { Instance } from '../instancesSlice'
|
||||
|
||||
export const updateInstancePushAlert = createAsyncThunk(
|
||||
'instances/updatePushAlert',
|
||||
async ({
|
||||
alerts
|
||||
}: {
|
||||
changed: keyof Instance['push']['alerts']
|
||||
alerts: Instance['push']['alerts']
|
||||
}): Promise<Instance['push']['alerts']> => {
|
||||
const formData = new FormData()
|
||||
Object.keys(alerts).map(alert =>
|
||||
// @ts-ignore
|
||||
formData.append(`data[alerts][${alert}]`, alerts[alert].value.toString())
|
||||
)
|
||||
|
||||
await apiInstance<Mastodon.PushSubscription>({
|
||||
method: 'put',
|
||||
url: 'push/subscription',
|
||||
body: formData
|
||||
})
|
||||
|
||||
return Promise.resolve(alerts)
|
||||
}
|
||||
)
|
39
src/utils/slices/instances/updatePushDecode.ts
Normal file
39
src/utils/slices/instances/updatePushDecode.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import apiGeneral from '@api/general'
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||
import { RootState } from '@root/store'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
import { getInstance, Instance, PUSH_SERVER } from '../instancesSlice'
|
||||
|
||||
export const updateInstancePushDecode = createAsyncThunk(
|
||||
'instances/updatePushDecode',
|
||||
async (
|
||||
disalbe: boolean,
|
||||
{ getState }
|
||||
): Promise<Instance['push']['decode']['value']> => {
|
||||
const state = getState() as RootState
|
||||
const instance = getInstance(state)
|
||||
if (!instance?.url || !instance.account.id || !instance.push.keys) {
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
const expoToken = (
|
||||
await Notifications.getExpoPushTokenAsync({
|
||||
experienceId: '@xmflsct/tooot'
|
||||
})
|
||||
).data
|
||||
|
||||
await apiGeneral({
|
||||
method: 'post',
|
||||
domain: PUSH_SERVER,
|
||||
url: 'v1/update-decode',
|
||||
body: {
|
||||
expoToken,
|
||||
instanceUrl: instance.url,
|
||||
accountId: instance.account.id,
|
||||
...(disalbe && { keys: instance.push.keys })
|
||||
}
|
||||
})
|
||||
|
||||
return Promise.resolve(disalbe)
|
||||
}
|
||||
)
|
@ -6,7 +6,11 @@ import { findIndex } from 'lodash'
|
||||
import addInstance from './instances/add'
|
||||
import removeInstance from './instances/remove'
|
||||
import { updateAccountPreferences } from './instances/updateAccountPreferences'
|
||||
import { updatePush } from './instances/updatePush'
|
||||
import { updateInstancePush } from './instances/updatePush'
|
||||
import { updateInstancePushAlert } from './instances/updatePushAlert'
|
||||
import { updateInstancePushDecode } from './instances/updatePushDecode'
|
||||
|
||||
export const PUSH_SERVER = __DEV__ ? 'testpush.tooot.app' : 'push.tooot.app'
|
||||
|
||||
export type Instance = {
|
||||
active: boolean
|
||||
@ -29,11 +33,65 @@ export type Instance = {
|
||||
readTime?: Mastodon.Notification['created_at']
|
||||
latestTime?: Mastodon.Notification['created_at']
|
||||
}
|
||||
push: {
|
||||
loading: boolean
|
||||
enabled: boolean
|
||||
subscription?: Mastodon.PushSubscription
|
||||
}
|
||||
push:
|
||||
| {
|
||||
global: { loading: boolean; value: true }
|
||||
decode: { loading: boolean; value: boolean }
|
||||
alerts: {
|
||||
follow: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['follow']
|
||||
}
|
||||
favourite: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['favourite']
|
||||
}
|
||||
reblog: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['reblog']
|
||||
}
|
||||
mention: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['mention']
|
||||
}
|
||||
poll: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['poll']
|
||||
}
|
||||
}
|
||||
keys: {
|
||||
auth: string
|
||||
public: string
|
||||
private: string
|
||||
}
|
||||
}
|
||||
| {
|
||||
global: { loading: boolean; value: false }
|
||||
decode: { loading: boolean; value: boolean }
|
||||
alerts: {
|
||||
follow: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['follow']
|
||||
}
|
||||
favourite: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['favourite']
|
||||
}
|
||||
reblog: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['reblog']
|
||||
}
|
||||
mention: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['mention']
|
||||
}
|
||||
poll: {
|
||||
loading: boolean
|
||||
value: Mastodon.PushSubscription['alerts']['poll']
|
||||
}
|
||||
}
|
||||
keys: undefined
|
||||
}
|
||||
drafts: ComposeStateDraft[]
|
||||
}
|
||||
|
||||
@ -119,7 +177,6 @@ const instancesSlice = createSlice({
|
||||
state.instances.push(action.payload.data)
|
||||
break
|
||||
case 'overwrite':
|
||||
console.log('overwriting')
|
||||
state.instances = state.instances.map(instance => {
|
||||
if (
|
||||
instance.url === action.payload.data.url &&
|
||||
@ -152,6 +209,7 @@ const instancesSlice = createSlice({
|
||||
console.error(action.error)
|
||||
})
|
||||
|
||||
// Update Instance Account Preferences
|
||||
.addCase(updateAccountPreferences.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].account.preferences = action.payload
|
||||
@ -160,14 +218,56 @@ const instancesSlice = createSlice({
|
||||
console.error(action.error)
|
||||
})
|
||||
|
||||
.addCase(updatePush.fulfilled, (state, action) => {
|
||||
// Update Instance Push Global
|
||||
.addCase(updateInstancePush.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
if (typeof action.payload === 'boolean') {
|
||||
state.instances[activeIndex].push.enabled = action.payload
|
||||
} else {
|
||||
state.instances[activeIndex].push.enabled = true
|
||||
state.instances[activeIndex].push.subscription = action.payload
|
||||
}
|
||||
state.instances[activeIndex].push.global.loading = false
|
||||
state.instances[activeIndex].push.global.value = action.meta.arg
|
||||
state.instances[activeIndex].push.keys = action.payload
|
||||
})
|
||||
.addCase(updateInstancePush.rejected, state => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.global.loading = false
|
||||
})
|
||||
.addCase(updateInstancePush.pending, state => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.global.loading = true
|
||||
})
|
||||
|
||||
// Update Instance Push Decode
|
||||
.addCase(updateInstancePushDecode.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.decode.loading = false
|
||||
state.instances[activeIndex].push.decode.value = action.payload
|
||||
})
|
||||
.addCase(updateInstancePushDecode.rejected, state => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.decode.loading = false
|
||||
})
|
||||
.addCase(updateInstancePushDecode.pending, state => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.decode.loading = true
|
||||
})
|
||||
|
||||
// Update Instance Push Individual Alert
|
||||
.addCase(updateInstancePushAlert.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.alerts[
|
||||
action.meta.arg.changed
|
||||
].loading = false
|
||||
state.instances[activeIndex].push.alerts = action.payload
|
||||
})
|
||||
.addCase(updateInstancePushAlert.rejected, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.alerts[
|
||||
action.meta.arg.changed
|
||||
].loading = false
|
||||
})
|
||||
.addCase(updateInstancePushAlert.pending, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].push.alerts[
|
||||
action.meta.arg.changed
|
||||
].loading = true
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -217,6 +317,11 @@ export const getInstanceNotification = ({
|
||||
return instanceActive !== -1 ? instances[instanceActive].notification : null
|
||||
}
|
||||
|
||||
export const getInstancePush = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].push : null
|
||||
}
|
||||
|
||||
export const getInstanceDrafts = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].drafts : null
|
||||
|
Reference in New Issue
Block a user