mirror of
https://github.com/tooot-app/app
synced 2025-04-13 01:42:41 +02:00
237 lines
6.5 KiB
TypeScript
237 lines
6.5 KiB
TypeScript
import client from '@api/client'
|
|
import analytics from '@components/analytics'
|
|
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
|
|
import { RootState } from '@root/store'
|
|
import * as AuthSession from 'expo-auth-session'
|
|
|
|
export type InstanceLocal = {
|
|
appData: {
|
|
clientId: string
|
|
clientSecret: string
|
|
}
|
|
url: string
|
|
token: string
|
|
account: {
|
|
id: Mastodon.Account['id']
|
|
preferences: Mastodon.Preferences
|
|
}
|
|
notification: {
|
|
unread: boolean
|
|
latestTime?: Mastodon.Notification['created_at']
|
|
}
|
|
}
|
|
|
|
export type InstancesState = {
|
|
local: {
|
|
activeIndex: number | null
|
|
instances: InstanceLocal[]
|
|
}
|
|
|
|
remote: {
|
|
url: string
|
|
}
|
|
}
|
|
|
|
export const localUpdateAccountPreferences = createAsyncThunk(
|
|
'instances/localUpdateAccountPreferences',
|
|
async (): Promise<Mastodon.Preferences> => {
|
|
const preferences = await client<Mastodon.Preferences>({
|
|
method: 'get',
|
|
instance: 'local',
|
|
url: `preferences`
|
|
})
|
|
|
|
return Promise.resolve(preferences)
|
|
}
|
|
)
|
|
|
|
export const localAddInstance = createAsyncThunk(
|
|
'instances/localAddInstance',
|
|
async ({
|
|
url,
|
|
token,
|
|
appData
|
|
}: {
|
|
url: InstanceLocal['url']
|
|
token: InstanceLocal['token']
|
|
appData: InstanceLocal['appData']
|
|
}): Promise<InstanceLocal> => {
|
|
const store = require('@root/store')
|
|
const state = store.getState().instances
|
|
|
|
const { id } = await client<Mastodon.Account>({
|
|
method: 'get',
|
|
instance: 'remote',
|
|
instanceDomain: url,
|
|
url: `accounts/verify_credentials`,
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
|
|
// Overwrite existing account?
|
|
// if (
|
|
// state.local.instances.filter(
|
|
// instance => instance && instance.account && instance.account.id === id
|
|
// ).length
|
|
// ) {
|
|
// return Promise.reject()
|
|
// }
|
|
|
|
const preferences = await client<Mastodon.Preferences>({
|
|
method: 'get',
|
|
instance: 'remote',
|
|
instanceDomain: url,
|
|
url: `preferences`,
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
|
|
return Promise.resolve({
|
|
appData,
|
|
url,
|
|
token,
|
|
account: {
|
|
id,
|
|
preferences
|
|
},
|
|
notification: {
|
|
unread: false
|
|
}
|
|
})
|
|
}
|
|
)
|
|
export const localRemoveInstance = createAsyncThunk(
|
|
'instances/localRemoveInstance',
|
|
async (index?: InstancesState['local']['activeIndex']): Promise<number> => {
|
|
const store = require('@root/store')
|
|
const local = store.getState().instances.local
|
|
|
|
if (index) {
|
|
return Promise.resolve(index)
|
|
} else {
|
|
if (local.activeIndex !== null) {
|
|
const currentInstance = local.instances[local.activeIndex]
|
|
|
|
const revoked = await AuthSession.revokeAsync(
|
|
{
|
|
clientId: currentInstance.appData.clientId,
|
|
clientSecret: currentInstance.appData.clientSecret,
|
|
token: currentInstance.token,
|
|
scopes: ['read', 'write', 'follow', 'push']
|
|
},
|
|
{
|
|
revocationEndpoint: `https://${currentInstance.url}/oauth/revoke`
|
|
}
|
|
)
|
|
if (!revoked) {
|
|
console.warn('Revoking error')
|
|
}
|
|
|
|
return Promise.resolve(local.activeIndex)
|
|
} else {
|
|
throw new Error('Active index invalid, cannot remove instance')
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
export const instancesInitialState: InstancesState = {
|
|
local: {
|
|
activeIndex: null,
|
|
instances: []
|
|
},
|
|
remote: {
|
|
url: 'm.cmx.im'
|
|
}
|
|
}
|
|
|
|
const instancesSlice = createSlice({
|
|
name: 'instances',
|
|
initialState: instancesInitialState,
|
|
reducers: {
|
|
localUpdateActiveIndex: (
|
|
state,
|
|
action: PayloadAction<NonNullable<InstancesState['local']['activeIndex']>>
|
|
) => {
|
|
if (action.payload < state.local.instances.length) {
|
|
state.local.activeIndex = action.payload
|
|
} else {
|
|
throw new Error('Set index cannot be found')
|
|
}
|
|
},
|
|
localUpdateNotification: (
|
|
state,
|
|
action: PayloadAction<Partial<InstanceLocal['notification']>>
|
|
) => {
|
|
state.local.instances[state.local.activeIndex!].notification = {
|
|
...state.local.instances[state.local.activeIndex!].notification,
|
|
...action.payload
|
|
}
|
|
},
|
|
remoteUpdate: (
|
|
state,
|
|
action: PayloadAction<InstancesState['remote']['url']>
|
|
) => {
|
|
state.remote.url = action.payload
|
|
}
|
|
},
|
|
extraReducers: builder => {
|
|
builder
|
|
.addCase(localAddInstance.fulfilled, (state, action) => {
|
|
state.local.instances.push(action.payload)
|
|
state.local.activeIndex = state.local.instances.length - 1
|
|
|
|
analytics('login')
|
|
})
|
|
.addCase(localAddInstance.rejected, (state, action) => {
|
|
console.error(state.local)
|
|
console.error(action.error)
|
|
})
|
|
|
|
.addCase(localRemoveInstance.fulfilled, (state, action) => {
|
|
state.local.instances.splice(action.payload, 1)
|
|
state.local.activeIndex = state.local.instances.length
|
|
? state.local.instances.length - 1
|
|
: null
|
|
|
|
analytics('logout')
|
|
})
|
|
.addCase(localRemoveInstance.rejected, (state, action) => {
|
|
console.error(state.local)
|
|
console.error(action.error)
|
|
})
|
|
|
|
.addCase(localUpdateAccountPreferences.fulfilled, (state, action) => {
|
|
state.local.instances[state.local.activeIndex!].account.preferences =
|
|
action.payload
|
|
})
|
|
.addCase(localUpdateAccountPreferences.rejected, (_, action) => {
|
|
console.error(action.error)
|
|
})
|
|
}
|
|
})
|
|
|
|
export const getLocalActiveIndex = ({ instances: { local } }: RootState) =>
|
|
local.activeIndex
|
|
export const getLocalInstances = ({ instances: { local } }: RootState) =>
|
|
local.instances
|
|
export const getLocalUrl = ({ instances: { local } }: RootState) =>
|
|
local.activeIndex ? local.instances[local.activeIndex].url : undefined
|
|
// export const getLocalToken = ({ instances: { local } }: RootState) =>
|
|
// local && local.activeIndex && local.instances[local.activeIndex].token
|
|
export const getLocalAccount = ({ instances: { local } }: RootState) =>
|
|
local.activeIndex !== null
|
|
? local.instances[local.activeIndex].account
|
|
: undefined
|
|
export const getLocalNotification = ({ instances: { local } }: RootState) =>
|
|
local.activeIndex !== null
|
|
? local.instances[local.activeIndex].notification
|
|
: undefined
|
|
export const getRemoteUrl = ({ instances: { remote } }: RootState) => remote.url
|
|
|
|
export const {
|
|
localUpdateActiveIndex,
|
|
localUpdateNotification,
|
|
remoteUpdate
|
|
} = instancesSlice.actions
|
|
|
|
export default instancesSlice.reducer
|