1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Lots of updates

This commit is contained in:
Zhiyuan Zheng
2021-01-07 19:13:09 +01:00
parent dcb36a682d
commit 4b99813bb7
104 changed files with 2463 additions and 1619 deletions

View File

@ -1,16 +0,0 @@
import client from '@api/client'
export const accountFetch = async ({
queryKey
}: {
queryKey: QueryKey.Account
}): Promise<Mastodon.Account> => {
const [_, { id }] = queryKey
const res = await client({
method: 'get',
instance: 'local',
url: `accounts/${id}`
})
return Promise.resolve(res.body)
}

View File

@ -1,21 +0,0 @@
import client from '@api/client'
export const announcementFetch = async ({
queryKey
}: {
queryKey: QueryKey.Announcements
}): Promise<Mastodon.Announcement[]> => {
const [_, { showAll }] = queryKey
const res = await client({
method: 'get',
instance: 'local',
url: `announcements`,
...(showAll && {
params: {
with_dismissed: 'true'
}
})
})
return Promise.resolve(res.body)
}

View File

@ -1,23 +0,0 @@
import client from '@api/client'
export const applicationFetch = async ({
queryKey
}: {
queryKey: QueryKey.Application
}): Promise<Mastodon.AppOauth> => {
const [_, { instanceDomain }] = queryKey
const formData = new FormData()
formData.append('client_name', 'test_dudu')
formData.append('redirect_uris', 'exp://127.0.0.1:19000')
formData.append('scopes', 'read write follow push')
const res = await client({
method: 'post',
instance: 'remote',
instanceDomain,
url: `apps`,
body: formData
})
return Promise.resolve(res.body)
}

View File

@ -1,10 +0,0 @@
import client from '@api/client'
export const emojisFetch = async (): Promise<Mastodon.Emoji[]> => {
const res = await client({
method: 'get',
instance: 'local',
url: 'custom_emojis'
})
return Promise.resolve(res.body)
}

View File

@ -1,17 +0,0 @@
import client from '@api/client'
export const instanceFetch = async ({
queryKey
}: {
queryKey: QueryKey.Instance
}): Promise<Mastodon.Instance> => {
const [_, { instanceDomain }] = queryKey
const res = await client({
method: 'get',
instance: 'remote',
instanceDomain,
url: `instance`
})
return Promise.resolve(res.body)
}

View File

@ -1,10 +0,0 @@
import client from '@api/client'
export const listsFetch = async (): Promise<Mastodon.List[]> => {
const res = await client({
method: 'get',
instance: 'local',
url: 'lists'
})
return Promise.resolve(res.body)
}

View File

@ -1,19 +0,0 @@
import client from '@api/client'
export const relationshipFetch = async ({
queryKey
}: {
queryKey: QueryKey.Relationship
}): Promise<Mastodon.Relationship> => {
const [_, { id }] = queryKey
const res = await client({
method: 'get',
instance: 'local',
url: `accounts/relationships`,
params: {
'id[]': id
}
})
return Promise.resolve(res.body[0])
}

View File

@ -1,28 +0,0 @@
import client from '@api/client'
export const relationshipsFetch = async ({
queryKey,
pageParam
}: {
queryKey: QueryKey.Relationships
pageParam?: { direction: 'next'; id: Mastodon.Status['id'] }
}): Promise<Mastodon.Account[]> => {
const [_, type, { id }] = queryKey
let params: { [key: string]: string } = {}
if (pageParam) {
switch (pageParam.direction) {
case 'next':
params.max_id = pageParam.id
break
}
}
const res = await client({
method: 'get',
instance: 'local',
url: `accounts/${id}/${type}`,
params
})
return Promise.resolve(res.body)
}

View File

@ -1,30 +0,0 @@
import client from '@api/client'
export const searchFetch = async ({
queryKey
}: {
queryKey: QueryKey.Search
}): Promise<
| Mastodon.Account[]
| Mastodon.Tag[]
| Mastodon.Status[]
| {
accounts: Mastodon.Account[]
hashtags: Mastodon.Tag[]
statuses: Mastodon.Status[]
}
> => {
const [_, { type, term, limit = 20 }] = queryKey
const res = await client({
version: 'v2',
method: 'get',
instance: 'local',
url: 'search',
params: { ...(type && { type }), q: term, limit }
})
if (type) {
return Promise.resolve(res.body[type])
} else {
return Promise.resolve(res.body)
}
}

View File

@ -1,212 +0,0 @@
import { uniqBy } from 'lodash'
import client from '@api/client'
export const timelineFetch = async ({
queryKey,
pageParam
}: {
queryKey: QueryKey.Timeline
pageParam?: { direction: 'prev' | 'next'; id: Mastodon.Status['id'] }
}): Promise<{
toots: Mastodon.Status[]
pointer?: number
pinnedLength?: number
}> => {
const [page, { account, hashtag, list, toot }] = queryKey
let res
let params: { [key: string]: string } = {}
if (pageParam) {
switch (pageParam.direction) {
case 'prev':
if (page === 'Bookmarks' || page === 'Favourites') {
params.max_id = pageParam.id
} else {
params.min_id = pageParam.id
}
break
case 'next':
if (page === 'Bookmarks' || page === 'Favourites') {
params.min_id = pageParam.id
} else {
params.max_id = pageParam.id
}
break
}
}
switch (page) {
case 'Following':
res = await client({
method: 'get',
instance: 'local',
url: 'timelines/home',
params
})
return Promise.resolve({ toots: res.body })
case 'Local':
params.local = 'true'
res = await client({
method: 'get',
instance: 'local',
url: 'timelines/public',
params
})
return Promise.resolve({ toots: res.body })
case 'LocalPublic':
res = await client({
method: 'get',
instance: 'local',
url: 'timelines/public',
params
})
return Promise.resolve({ toots: res.body })
case 'RemotePublic':
res = await client({
method: 'get',
instance: 'remote',
url: 'timelines/public',
params
})
return Promise.resolve({ toots: res.body })
case 'Notifications':
res = await client({
method: 'get',
instance: 'local',
url: 'notifications',
params
})
return Promise.resolve({ toots: res.body })
case 'Account_Default':
if (pageParam && pageParam.direction === 'next') {
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true',
...params
}
})
return Promise.resolve({ toots: res.body })
} else {
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
pinned: 'true'
}
})
const pinnedLength = res.body.length
let toots: Mastodon.Status[] = res.body
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true'
}
})
toots = uniqBy([...toots, ...res.body], 'id')
return Promise.resolve({ toots: toots, pinnedLength })
}
case 'Account_All':
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params
})
return Promise.resolve({ toots: res.body })
case 'Account_Media':
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
only_media: 'true',
...params
}
})
return Promise.resolve({ toots: res.body })
case 'Hashtag':
res = await client({
method: 'get',
instance: 'local',
url: `timelines/tag/${hashtag}`,
params
})
return Promise.resolve({ toots: res.body })
case 'Conversations':
res = await client({
method: 'get',
instance: 'local',
url: `conversations`,
params
})
if (pageParam) {
// Bug in pull to refresh in conversations
res.body = res.body.filter(
(b: Mastodon.Conversation) => b.id !== pageParam.id
)
}
return Promise.resolve({ toots: res.body })
case 'Bookmarks':
res = await client({
method: 'get',
instance: 'local',
url: `bookmarks`,
params
})
return Promise.resolve({ toots: res.body })
case 'Favourites':
res = await client({
method: 'get',
instance: 'local',
url: `favourites`,
params
})
return Promise.resolve({ toots: res.body })
case 'List':
res = await client({
method: 'get',
instance: 'local',
url: `timelines/list/${list}`,
params
})
return Promise.resolve({ toots: res.body })
case 'Toot':
res = await client({
method: 'get',
instance: 'local',
url: `statuses/${toot}`
})
const theToot = res.body
res = await client({
method: 'get',
instance: 'local',
url: `statuses/${toot}/context`
})
return Promise.resolve({
toots: [...res.body.ancestors, theToot, ...res.body.descendants],
pointer: res.body.ancestors.length
})
default:
return Promise.reject()
}
}

View File

@ -0,0 +1,27 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = ['Account', { id: Mastodon.Account['id'] }]
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { id } = queryKey[1]
return client<Mastodon.Account>({
method: 'get',
instance: 'local',
url: `accounts/${id}`
})
}
const hookAccount = <TData = Mastodon.Account>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<Mastodon.Account, AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Account', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookAccount

View File

@ -0,0 +1,35 @@
import client from '@api/client'
import { InstancesState } from '@utils/slices/instancesSlice'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = [
'AccountCheck',
{
id: Mastodon.Account['id']
index: NonNullable<InstancesState['local']['activeIndex']>
}
]
const queryFunction = async ({ queryKey }: { queryKey: QueryKey }) => {
const { id, index } = queryKey[1]
return client<Mastodon.Account>({
method: 'get',
instance: 'local',
localIndex: index,
url: `accounts/${id}`
})
}
const hookAccountCheck = <TData = Mastodon.Account>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<Mastodon.Account, AxiosError, TData>
}) => {
const queryKey: QueryKey = ['AccountCheck', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookAccountCheck

View File

@ -0,0 +1,32 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
type QueryKey = ['Announcements', { showAll?: boolean }]
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { showAll } = queryKey[1]
return client<Mastodon.Announcement[]>({
method: 'get',
instance: 'local',
url: `announcements`,
...(showAll && {
params: {
with_dismissed: 'true'
}
})
})
}
const hookAnnouncement = <TData = Mastodon.Announcement[]>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<Mastodon.Announcement[], AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Announcements', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookAnnouncement

View File

@ -0,0 +1,34 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = ['Apps', { instanceDomain?: string }]
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { instanceDomain } = queryKey[1]
const formData = new FormData()
formData.append('client_name', 'test_dudu')
formData.append('redirect_uris', 'exp://127.0.0.1:19000')
formData.append('scopes', 'read write follow push')
return client<Mastodon.Apps>({
method: 'post',
instance: 'remote',
instanceDomain,
url: `apps`,
body: formData
})
}
const hookApps = <TData = Mastodon.Apps>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<Mastodon.Apps, AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Apps', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookApps

View File

@ -0,0 +1,24 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
type QueryKey = ['Emojis']
const queryFunction = () => {
return client<Mastodon.Emoji[]>({
method: 'get',
instance: 'local',
url: 'custom_emojis'
})
}
const hookEmojis = <TData = Mastodon.Emoji[]>({
options
}: {
options?: UseQueryOptions<Mastodon.Emoji[], AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Emojis']
return useQuery(queryKey, queryFunction, options)
}
export default hookEmojis

View File

@ -0,0 +1,28 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = ['Instance', { instanceDomain?: string }]
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { instanceDomain } = queryKey[1]
return client<Mastodon.Instance>({
method: 'get',
instance: 'remote',
instanceDomain,
url: `instance`
})
}
const hookInstance = <TData = Mastodon.Instance>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<Mastodon.Instance, AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Instance', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookInstance

View File

@ -0,0 +1,24 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = ['Lists']
const queryFunction = () => {
return client<Mastodon.List[]>({
method: 'get',
instance: 'local',
url: 'lists'
})
}
const hookLists = <TData = Mastodon.List[]>({
options
}: {
options?: UseQueryOptions<Mastodon.List[], AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Lists']
return useQuery(queryKey, queryFunction, options)
}
export default hookLists

View File

@ -0,0 +1,30 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = ['Relationship', { id: Mastodon.Account['id'] }]
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { id } = queryKey[1]
return client<Mastodon.Relationship>({
method: 'get',
instance: 'local',
url: `accounts/relationships`,
params: {
'id[]': id
}
})
}
const hookRelationship = <TData = Mastodon.Relationship>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<Mastodon.Relationship, AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Relationship', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookRelationship

View File

@ -0,0 +1,46 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useInfiniteQuery, UseInfiniteQueryOptions } from 'react-query'
export type QueryKey = [
'Relationships',
{ type: 'following' | 'followers'; id: Mastodon.Account['id'] }
]
const queryFunction = ({
queryKey,
pageParam
}: {
queryKey: QueryKey
pageParam?: { direction: 'next'; id: Mastodon.Status['id'] }
}) => {
const { type, id } = queryKey[1]
let params: { [key: string]: string } = {}
if (pageParam) {
switch (pageParam.direction) {
case 'next':
params.max_id = pageParam.id
break
}
}
return client<Mastodon.Account[]>({
method: 'get',
instance: 'local',
url: `accounts/${id}/${type}`,
params
})
}
const hookRelationships = <TData = Mastodon.Account[]>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseInfiniteQueryOptions<Mastodon.Account[], AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Relationships', { ...queryKeyParams }]
return useInfiniteQuery(queryKey, queryFunction, options)
}
export default hookRelationships

View File

@ -0,0 +1,41 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = [
'Search',
{
type?: 'accounts' | 'hashtags' | 'statuses'
term?: string
limit?: number
}
]
type SearchResult = {
accounts: Mastodon.Account[]
hashtags: Mastodon.Tag[]
statuses: Mastodon.Status[]
}
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
const { type, term, limit = 20 } = queryKey[1]
return client<SearchResult>({
version: 'v2',
method: 'get',
instance: 'local',
url: 'search',
params: { ...(type && { type }), ...(term && { q: term }), limit }
})
}
const hookSearch = <TData = SearchResult>({
options,
...queryKeyParams
}: QueryKey[1] & {
options?: UseQueryOptions<SearchResult, AxiosError, TData>
}) => {
const queryKey: QueryKey = ['Search', { ...queryKeyParams }]
return useQuery(queryKey, queryFunction, options)
}
export default hookSearch

View File

@ -0,0 +1,291 @@
import client from '@api/client'
import { AxiosError } from 'axios'
import { uniqBy } from 'lodash'
import { useInfiniteQuery, UseInfiniteQueryOptions } from 'react-query'
export type QueryKeyTimeline = [
'Timeline',
{
page: App.Pages
hashtag?: Mastodon.Tag['name']
list?: Mastodon.List['id']
toot?: Mastodon.Status['id']
account?: Mastodon.Account['id']
}
]
const queryFunction = async ({
queryKey,
pageParam
}: {
queryKey: QueryKeyTimeline
pageParam?: { direction: 'prev' | 'next'; id: Mastodon.Status['id'] }
}) => {
const { page, account, hashtag, list, toot } = queryKey[1]
let res
let params: { [key: string]: string } = {}
if (pageParam) {
switch (pageParam.direction) {
case 'prev':
if (page === 'Bookmarks' || page === 'Favourites') {
params.max_id = pageParam.id
} else {
params.min_id = pageParam.id
}
break
case 'next':
if (page === 'Bookmarks' || page === 'Favourites') {
params.min_id = pageParam.id
} else {
params.max_id = pageParam.id
}
break
}
}
switch (page) {
case 'Following':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: 'timelines/home',
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Local':
params.local = 'true'
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: 'timelines/public',
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'LocalPublic':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: 'timelines/public',
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'RemotePublic':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'remote',
url: 'timelines/public',
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Notifications':
res = await client<Mastodon.Notification[]>({
method: 'get',
instance: 'local',
url: 'notifications',
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Account_Default':
if (pageParam && pageParam.direction === 'next') {
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true',
...params
}
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
} else {
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
pinned: 'true'
}
})
const pinnedLength = res.length
let toots = res
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true'
}
})
toots = uniqBy([...toots, ...res], 'id')
return Promise.resolve({
toots: toots,
pointer: undefined,
pinnedLength
})
}
case 'Account_All':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Account_Media':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
only_media: 'true',
...params
}
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Hashtag':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `timelines/tag/${hashtag}`,
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Conversations':
res = await client<Mastodon.Conversation[]>({
method: 'get',
instance: 'local',
url: `conversations`,
params
})
if (pageParam) {
// Bug in pull to refresh in conversations
res = res.filter(b => b.id !== pageParam.id)
}
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Bookmarks':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `bookmarks`,
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Favourites':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `favourites`,
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'List':
res = await client<Mastodon.Status[]>({
method: 'get',
instance: 'local',
url: `timelines/list/${list}`,
params
})
return Promise.resolve({
toots: res,
pointer: undefined,
pinnedLength: undefined
})
case 'Toot':
res = await client<Mastodon.Status>({
method: 'get',
instance: 'local',
url: `statuses/${toot}`
})
const theToot = res
res = await client<{
ancestors: Mastodon.Status[]
descendants: Mastodon.Status[]
}>({
method: 'get',
instance: 'local',
url: `statuses/${toot}/context`
})
return Promise.resolve({
toots: [...res.ancestors, theToot, ...res.descendants],
pointer: res.ancestors.length,
pinnedLength: undefined
})
default:
return Promise.reject()
}
}
type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never
const hookTimeline = <TData = Unpromise<ReturnType<typeof queryFunction>>>({
options,
...queryKeyParams
}: QueryKeyTimeline[1] & {
options?: UseInfiniteQueryOptions<any, AxiosError, TData>
}) => {
const queryKey: QueryKeyTimeline = ['Timeline', { ...queryKeyParams }]
return useInfiniteQuery(queryKey, queryFunction, options)
}
export default hookTimeline

View File

@ -1,70 +1,65 @@
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { RootState } from '@root/store'
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: {
url: string | undefined
token: string | undefined
account: {
id: Mastodon.Account['id'] | undefined
preferences: Mastodon.Preferences
}
notification: {
unread: boolean
latestTime?: Mastodon.Notification['created_at']
}
activeIndex: number | null
instances: InstanceLocal[]
}
remote: {
url: string
}
}
const initialStateLocal: InstancesState['local'] = {
url: undefined,
token: undefined,
account: {
id: undefined,
preferences: {
'posting:default:visibility': undefined,
'posting:default:sensitive': undefined,
'posting:default:language': undefined,
'reading:expand:media': undefined,
'reading:expand:spoilers': undefined
}
},
notification: {
unread: false,
latestTime: undefined
}
}
export const updateLocalAccountPreferences = createAsyncThunk(
'instances/updateLocalAccountPreferences',
async () => {
const { body: preferences } = await client({
export const localUpdateAccountPreferences = createAsyncThunk(
'instances/localUpdateAccountPreferences',
async (): Promise<Mastodon.Preferences> => {
const preferences = await client<Mastodon.Preferences>({
method: 'get',
instance: 'local',
url: `preferences`
})
return preferences as Mastodon.Preferences
return Promise.resolve(preferences)
}
)
export const loginLocal = createAsyncThunk(
'instances/loginLocal',
export const localAddInstance = createAsyncThunk(
'instances/localAddInstance',
async ({
url,
token
token,
appData
}: {
url: InstancesState['local']['url']
token: InstancesState['local']['token']
}) => {
const {
body: { id }
} = await client({
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,
@ -72,7 +67,16 @@ export const loginLocal = createAsyncThunk(
headers: { Authorization: `Bearer ${token}` }
})
const { body: preferences } = await client({
// 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,
@ -80,60 +84,153 @@ export const loginLocal = createAsyncThunk(
headers: { Authorization: `Bearer ${token}` }
})
return {
return Promise.resolve({
appData,
url,
token,
account: {
id,
preferences
},
notification: {
unread: false
}
} as InstancesState['local']
})
}
)
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: {
local: initialStateLocal,
remote: {
url: 'm.cmx.im'
}
} as InstancesState,
initialState: instancesInitialState,
reducers: {
resetLocal: state => {
state.local = initialStateLocal
},
updateNotification: (
localUpdateActiveIndex: (
state,
action: PayloadAction<Partial<InstancesState['local']['notification']>>
action: PayloadAction<NonNullable<InstancesState['local']['activeIndex']>>
) => {
state.local.notification = {
...state.local.notification,
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(loginLocal.fulfilled, (state, action) => {
state.local = action.payload
.addCase(localAddInstance.fulfilled, (state, action) => {
state.local.instances.push(action.payload)
state.local.activeIndex = state.local.instances.length - 1
analytics('login')
})
.addCase(updateLocalAccountPreferences.fulfilled, (state, action) => {
state.local.account.preferences = action.payload
.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 getLocalUrl = (state: RootState) => state.instances.local.url
export const getLocalToken = (state: RootState) => state.instances.local.token
export const getLocalNotification = (state: RootState) =>
state.instances.local.notification
export const getRemoteUrl = (state: RootState) => state.instances.remote.url
export const getLocalAccountId = (state: RootState) =>
state.instances.local.account.id
export const getLocalAccountPreferences = (state: RootState) =>
state.instances.local.account.preferences
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 { resetLocal, updateNotification } = instancesSlice.actions
export const {
localUpdateActiveIndex,
localUpdateNotification,
remoteUpdate
} = instancesSlice.actions
export default instancesSlice.reducer

View File

@ -9,7 +9,7 @@ export type SettingsState = {
analytics: boolean
}
const initialState = {
export const settingsInitialState = {
language: undefined,
theme: 'auto',
browser: 'internal',
@ -26,7 +26,7 @@ export const changeAnalytics = createAsyncThunk(
const settingsSlice = createSlice({
name: 'settings',
initialState: initialState as SettingsState,
initialState: settingsInitialState as SettingsState,
reducers: {
changeLanguage: (
state,

View File

@ -3,11 +3,11 @@ const Base = 4
export const StyleConstants = {
Font: {
Size: { S: 14, M: 16, L: 18 },
LineHeight: { S: 18, M: 22, L: 26 },
LineHeight: { S: 20, M: 22, L: 26 },
Weight: { Bold: '600' as '600' }
},
FontStyle: {
S: { fontSize: 14, lineHeight: 18 },
S: { fontSize: 14, lineHeight: 20 },
M: { fontSize: 16, lineHeight: 22 },
L: { fontSize: 20, lineHeight: 26 }
},

View File

@ -12,7 +12,8 @@ export type ColorDefinitions =
| 'backgroundGradientEnd'
| 'backgroundOverlay'
| 'border'
| 'shimmer'
| 'shimmerDefault'
| 'shimmerHighlight'
const themeColors: {
[key in ColorDefinitions]: {
@ -65,17 +66,13 @@ const themeColors: {
light: 'rgba(18, 18, 18, 0.3)',
dark: 'rgba(255, 255, 255, 0.3)'
},
shimmer: {
light: [
'rgba(18, 18, 18, 0.05)',
'rgba(18, 18, 18, 0.15)',
'rgba(18, 18, 18, 0.05)'
],
dark: [
'rgba(250, 250, 250, 0.05)',
'rgba(250, 250, 250, 0.15)',
'rgba(250, 250, 250, 0.05)'
]
shimmerDefault: {
light: 'rgba(18, 18, 18, 0.05)',
dark: 'rgba(250, 250, 250, 0.05)'
},
shimmerHighlight: {
light: 'rgba(18, 18, 18, 0.15)',
dark: 'rgba(250, 250, 250, 0.15)'
}
}