mirror of
https://github.com/tooot-app/app
synced 2025-02-12 01:40:44 +01:00
Added instance configuration support
This commit is contained in:
parent
98dd7b2b46
commit
7ac789c18c
24
src/@types/mastodon.d.ts
vendored
24
src/@types/mastodon.d.ts
vendored
@ -299,8 +299,28 @@ declare namespace Mastodon {
|
||||
// Others
|
||||
thumbnail?: string
|
||||
contact_account?: Account
|
||||
|
||||
// Custom
|
||||
configuration?: {
|
||||
statuses: {
|
||||
max_characters: number
|
||||
max_media_attachments: number
|
||||
characters_reserved_per_url: number
|
||||
}
|
||||
media_attachments: {
|
||||
supported_mime_types: string[]
|
||||
image_size_limit: number
|
||||
image_matrix_limit: number
|
||||
video_size_limit: number
|
||||
video_frame_rate_limit: number
|
||||
video_matrix_limit: number
|
||||
}
|
||||
polls: {
|
||||
max_options: number
|
||||
max_characters_per_option: number
|
||||
min_expiration: number
|
||||
max_expiration: number
|
||||
}
|
||||
}
|
||||
// Custom - to be deprecated in v4
|
||||
max_toot_chars?: number
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import pushUseReceive from '@utils/push/useReceive'
|
||||
import pushUseRespond from '@utils/push/useRespond'
|
||||
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
||||
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
||||
import { updateConfiguration } from '@utils/slices/instances/updateConfiguration'
|
||||
import { updateFilters } from '@utils/slices/instances/updateFilters'
|
||||
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
@ -108,6 +109,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||
// Lazily update users's preferences, for e.g. composing default visibility
|
||||
useEffect(() => {
|
||||
if (instanceActive !== -1) {
|
||||
dispatch(updateConfiguration())
|
||||
dispatch(updateFilters())
|
||||
dispatch(updateAccountPreferences())
|
||||
}
|
||||
|
@ -22,9 +22,8 @@ const InstanceAuth = React.memo(
|
||||
useProxy: false
|
||||
})
|
||||
|
||||
const navigation = useNavigation<
|
||||
TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>
|
||||
>()
|
||||
const navigation =
|
||||
useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
||||
const queryClient = useQueryClient()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
@ -70,7 +69,6 @@ const InstanceAuth = React.memo(
|
||||
domain: instanceDomain,
|
||||
token: accessToken,
|
||||
instance,
|
||||
max_toot_chars: instance.max_toot_chars,
|
||||
appData
|
||||
})
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ export interface Props {
|
||||
resize?: { width?: number; height?: number } // Resize mode contain
|
||||
showActionSheetWithOptions: (
|
||||
options: ActionSheetOptions,
|
||||
callback: (i: number) => void
|
||||
callback: (i?: number | undefined) => void | Promise<void>
|
||||
) => void
|
||||
}
|
||||
|
||||
@ -57,9 +57,8 @@ const mediaSelector = async ({
|
||||
},
|
||||
async buttonIndex => {
|
||||
if (buttonIndex === 0) {
|
||||
const {
|
||||
status
|
||||
} = await ImagePicker.requestMediaLibraryPermissionsAsync()
|
||||
const { status } =
|
||||
await ImagePicker.requestMediaLibraryPermissionsAsync()
|
||||
if (status !== 'granted') {
|
||||
Alert.alert(
|
||||
i18next.t('componentMediaSelector:library.alert.title'),
|
||||
|
@ -9,7 +9,7 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||
import {
|
||||
getInstanceAccount,
|
||||
getInstanceMaxTootChar,
|
||||
getInstanceConfigurationStatusMaxChars,
|
||||
removeInstanceDraft,
|
||||
updateInstanceDraft
|
||||
} from '@utils/slices/instancesSlice'
|
||||
@ -103,7 +103,10 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
initialReducerState
|
||||
)
|
||||
|
||||
const maxTootChars = useSelector(getInstanceMaxTootChar, () => true)
|
||||
const maxTootChars = useSelector(
|
||||
getInstanceConfigurationStatusMaxChars,
|
||||
() => true
|
||||
)
|
||||
const totalTextCount =
|
||||
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
||||
composeState.text.count
|
||||
|
@ -29,6 +29,8 @@ import ComposeDrafts from './Root/Drafts'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { ComposeState } from './utils/types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { getInstanceConfigurationStatusCharsURL } from '@utils/slices/instancesSlice'
|
||||
|
||||
const prefetchEmojis = (
|
||||
sortedEmojis: NonNullable<ComposeState['emoji']['emojis']>,
|
||||
@ -54,11 +56,18 @@ const prefetchEmojis = (
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export let instanceConfigurationStatusCharsURL = 23
|
||||
|
||||
const ComposeRoot = React.memo(
|
||||
() => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
const { theme } = useTheme()
|
||||
|
||||
instanceConfigurationStatusCharsURL = useSelector(
|
||||
getInstanceConfigurationStatusCharsURL,
|
||||
() => true
|
||||
)
|
||||
|
||||
const accessibleRefDrafts = useRef(null)
|
||||
const accessibleRefAttachments = useRef(null)
|
||||
const accessibleRefEmojis = useRef(null)
|
||||
|
@ -1,12 +1,14 @@
|
||||
import analytics from '@components/analytics'
|
||||
import Icon from '@components/Icon'
|
||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useContext, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import ComposeContext from '../utils/createContext'
|
||||
import addAttachment from './Footer/addAttachment'
|
||||
|
||||
@ -15,6 +17,10 @@ const ComposeActions: React.FC = () => {
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const { t } = useTranslation('screenCompose')
|
||||
const { theme } = useTheme()
|
||||
const instanceConfigurationStatusMaxAttachments = useSelector(
|
||||
getInstanceConfigurationStatusMaxAttachments,
|
||||
() => true
|
||||
)
|
||||
|
||||
const attachmentColor = useMemo(() => {
|
||||
if (composeState.poll.active) return theme.disabled
|
||||
@ -28,7 +34,10 @@ const ComposeActions: React.FC = () => {
|
||||
const attachmentOnPress = useCallback(async () => {
|
||||
if (composeState.poll.active) return
|
||||
|
||||
if (composeState.attachments.uploads.length < 4) {
|
||||
if (
|
||||
composeState.attachments.uploads.length <
|
||||
instanceConfigurationStatusMaxAttachments
|
||||
) {
|
||||
analytics('compose_actions_attachment_press', {
|
||||
count: composeState.attachments.uploads.length
|
||||
})
|
||||
|
@ -3,11 +3,13 @@ import Button from '@components/Button'
|
||||
import Icon from '@components/Icon'
|
||||
import { MenuRow } from '@components/Menu'
|
||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||
import { getInstanceConfigurationPoll } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, TextInput, View } from 'react-native'
|
||||
import { StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
|
||||
const ComposePoll: React.FC = () => {
|
||||
@ -21,6 +23,16 @@ const ComposePoll: React.FC = () => {
|
||||
const { t } = useTranslation('screenCompose')
|
||||
const { mode, theme } = useTheme()
|
||||
|
||||
const instanceConfigurationPoll = useSelector(
|
||||
getInstanceConfigurationPoll,
|
||||
() => true
|
||||
)
|
||||
const MAX_OPTIONS = instanceConfigurationPoll.max_options
|
||||
const MAX_CHARS_PER_OPTION =
|
||||
instanceConfigurationPoll.max_characters_per_option
|
||||
const MIN_EXPIRATION = instanceConfigurationPoll.min_expiration
|
||||
const MAX_EXPIRATION = instanceConfigurationPoll.max_expiration
|
||||
|
||||
const [firstRender, setFirstRender] = useState(true)
|
||||
useEffect(() => {
|
||||
setFirstRender(false)
|
||||
@ -67,7 +79,7 @@ const ComposePoll: React.FC = () => {
|
||||
: t('content.root.footer.poll.option.placeholder.single')
|
||||
}
|
||||
placeholderTextColor={theme.disabled}
|
||||
maxLength={50}
|
||||
maxLength={MAX_CHARS_PER_OPTION}
|
||||
// @ts-ignore
|
||||
value={options[i]}
|
||||
onChangeText={e =>
|
||||
@ -82,37 +94,38 @@ const ComposePoll: React.FC = () => {
|
||||
})}
|
||||
</View>
|
||||
<View style={styles.controlAmount}>
|
||||
<View style={styles.firstButton}>
|
||||
<Button
|
||||
{...(total > 2
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'content.root.footer.poll.quantity.reduce.accessibilityLabel',
|
||||
{ amount: total - 1 }
|
||||
)
|
||||
}
|
||||
: {
|
||||
accessibilityHint: t(
|
||||
'content.root.footer.poll.quantity.reduce.accessibilityHint',
|
||||
{ amount: total }
|
||||
)
|
||||
})}
|
||||
onPress={() => {
|
||||
analytics('compose_poll_reduce_press')
|
||||
total > 2 &&
|
||||
composeDispatch({
|
||||
type: 'poll',
|
||||
payload: { total: total - 1 }
|
||||
})
|
||||
}}
|
||||
type='icon'
|
||||
content='Minus'
|
||||
round
|
||||
disabled={!(total > 2)}
|
||||
/>
|
||||
</View>
|
||||
<Button
|
||||
{...(total < 4
|
||||
{...(total > 2
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'content.root.footer.poll.quantity.reduce.accessibilityLabel',
|
||||
{ amount: total - 1 }
|
||||
)
|
||||
}
|
||||
: {
|
||||
accessibilityHint: t(
|
||||
'content.root.footer.poll.quantity.reduce.accessibilityHint',
|
||||
{ amount: total }
|
||||
)
|
||||
})}
|
||||
onPress={() => {
|
||||
analytics('compose_poll_reduce_press')
|
||||
total > 2 &&
|
||||
composeDispatch({
|
||||
type: 'poll',
|
||||
payload: { total: total - 1 }
|
||||
})
|
||||
}}
|
||||
type='icon'
|
||||
content='Minus'
|
||||
round
|
||||
disabled={!(total > 2)}
|
||||
/>
|
||||
<Text style={styles.controlCount}>
|
||||
{total} / {MAX_OPTIONS}
|
||||
</Text>
|
||||
<Button
|
||||
{...(total < MAX_OPTIONS
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'content.root.footer.poll.quantity.increase.accessibilityLabel',
|
||||
@ -127,7 +140,7 @@ const ComposePoll: React.FC = () => {
|
||||
})}
|
||||
onPress={() => {
|
||||
analytics('compose_poll_increase_press')
|
||||
total < 4 &&
|
||||
total < MAX_OPTIONS &&
|
||||
composeDispatch({
|
||||
type: 'poll',
|
||||
payload: { total: total + 1 }
|
||||
@ -136,7 +149,7 @@ const ComposePoll: React.FC = () => {
|
||||
type='icon'
|
||||
content='Plus'
|
||||
round
|
||||
disabled={!(total < 4)}
|
||||
disabled={!(total < MAX_OPTIONS)}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.controlOptions}>
|
||||
@ -158,7 +171,7 @@ const ComposePoll: React.FC = () => {
|
||||
cancelButtonIndex: 2
|
||||
},
|
||||
index => {
|
||||
if (index < 2) {
|
||||
if (index && index < 2) {
|
||||
analytics('compose_poll_expiration_press', {
|
||||
current: multiple,
|
||||
new: index === 1
|
||||
@ -177,6 +190,7 @@ const ComposePoll: React.FC = () => {
|
||||
title={t('content.root.footer.poll.expiration.heading')}
|
||||
content={t(`content.root.footer.poll.expiration.options.${expire}`)}
|
||||
onPress={() => {
|
||||
// @ts-ignore
|
||||
const expirations: [
|
||||
'300',
|
||||
'1800',
|
||||
@ -185,7 +199,19 @@ const ComposePoll: React.FC = () => {
|
||||
'86400',
|
||||
'259200',
|
||||
'604800'
|
||||
] = ['300', '1800', '3600', '21600', '86400', '259200', '604800']
|
||||
] = [
|
||||
'300',
|
||||
'1800',
|
||||
'3600',
|
||||
'21600',
|
||||
'86400',
|
||||
'259200',
|
||||
'604800'
|
||||
].filter(
|
||||
expiration =>
|
||||
parseInt(expiration) >= MIN_EXPIRATION &&
|
||||
parseInt(expiration) <= MAX_EXPIRATION
|
||||
)
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: [
|
||||
@ -197,7 +223,7 @@ const ComposePoll: React.FC = () => {
|
||||
cancelButtonIndex: 7
|
||||
},
|
||||
index => {
|
||||
if (index < 7) {
|
||||
if (index && expirations.length < 7) {
|
||||
analytics('compose_poll_expiration_press', {
|
||||
current: expire,
|
||||
new: expirations[index]
|
||||
@ -246,14 +272,15 @@ const styles = StyleSheet.create({
|
||||
controlAmount: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
marginRight: StyleConstants.Spacing.M
|
||||
},
|
||||
controlOptions: {
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||
},
|
||||
firstButton: {
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
controlCount: {
|
||||
marginHorizontal: StyleConstants.Spacing.S
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -13,7 +13,7 @@ export interface Props {
|
||||
composeDispatch: Dispatch<ComposeAction>
|
||||
showActionSheetWithOptions: (
|
||||
options: ActionSheetOptions,
|
||||
callback: (i: number) => void
|
||||
callback: (i?: number | undefined) => void | Promise<void>
|
||||
) => void
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { FetchOptions } from 'react-query/types/core/query'
|
||||
import Autolinker from '@root/modules/autolinker'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { ComposeAction, ComposeState } from './utils/types'
|
||||
import { instanceConfigurationStatusCharsURL } from './Root'
|
||||
|
||||
export interface Params {
|
||||
textInput: ComposeState['textInputFocus']['current']
|
||||
@ -92,7 +93,7 @@ const formatText = ({
|
||||
children.push(<TagText key={index} text={main} />)
|
||||
switch (tag.type) {
|
||||
case 'url':
|
||||
contentLength = contentLength + 23
|
||||
contentLength = contentLength + instanceConfigurationStatusCharsURL
|
||||
break
|
||||
case 'accounts':
|
||||
const theMatch = main.match(/@/g)
|
||||
|
@ -22,7 +22,7 @@ const instancesPersistConfig = {
|
||||
key: 'instances',
|
||||
prefix,
|
||||
storage: secureStorage,
|
||||
version: 5,
|
||||
version: 6,
|
||||
// @ts-ignore
|
||||
migrate: createMigrate(instancesMigration)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { InstanceV3 } from './v3'
|
||||
import { InstanceV4 } from './v4'
|
||||
import { InstanceV5 } from './v5'
|
||||
|
||||
const instancesMigration = {
|
||||
4: (state: InstanceV3) => {
|
||||
@ -27,7 +28,6 @@ const instancesMigration = {
|
||||
}
|
||||
},
|
||||
5: (state: InstanceV4) => {
|
||||
// Migration is run on each start, don't know why
|
||||
// @ts-ignore
|
||||
if (state.instances.length && !state.instances[0].notifications_filter) {
|
||||
return {
|
||||
@ -47,6 +47,11 @@ const instancesMigration = {
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
},
|
||||
6: (state: InstanceV5) => {
|
||||
return state.instances.map(instance => {
|
||||
return { ...instance, configuration: undefined }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
93
src/utils/migrations/instances/v5.ts
Normal file
93
src/utils/migrations/instances/v5.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||
|
||||
type Instance = {
|
||||
active: boolean
|
||||
appData: {
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
}
|
||||
url: string
|
||||
token: string
|
||||
uri: Mastodon.Instance['uri']
|
||||
urls: Mastodon.Instance['urls']
|
||||
max_toot_chars: number
|
||||
account: {
|
||||
id: Mastodon.Account['id']
|
||||
acct: Mastodon.Account['acct']
|
||||
avatarStatic: Mastodon.Account['avatar_static']
|
||||
preferences: Mastodon.Preferences
|
||||
}
|
||||
filters: Mastodon.Filter[]
|
||||
notifications_filter: {
|
||||
follow: boolean
|
||||
favourite: boolean
|
||||
reblog: boolean
|
||||
mention: boolean
|
||||
poll: boolean
|
||||
follow_request: boolean
|
||||
}
|
||||
push:
|
||||
| {
|
||||
global: { loading: boolean; value: boolean }
|
||||
decode: { loading: boolean; value: true }
|
||||
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: boolean }
|
||||
decode: { loading: boolean; value: false }
|
||||
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[]
|
||||
}
|
||||
|
||||
export type InstanceV5 = {
|
||||
instances: Instance[]
|
||||
}
|
@ -9,13 +9,11 @@ const addInstance = createAsyncThunk(
|
||||
domain,
|
||||
token,
|
||||
instance,
|
||||
max_toot_chars = 500,
|
||||
appData
|
||||
}: {
|
||||
domain: Instance['url']
|
||||
token: Instance['token']
|
||||
instance: Mastodon.Instance
|
||||
max_toot_chars?: number
|
||||
appData: Instance['appData']
|
||||
}): Promise<{ type: 'add' | 'overwrite'; data: Instance }> => {
|
||||
const { store } = require('@root/store')
|
||||
@ -70,13 +68,18 @@ const addInstance = createAsyncThunk(
|
||||
token,
|
||||
uri: instance.uri,
|
||||
urls: instance.urls,
|
||||
max_toot_chars,
|
||||
account: {
|
||||
id,
|
||||
acct,
|
||||
avatarStatic: avatar_static,
|
||||
preferences
|
||||
},
|
||||
...(instance.max_toot_chars && {
|
||||
max_toot_chars: instance.max_toot_chars
|
||||
}),
|
||||
...(instance.configuration && {
|
||||
configuration: instance.configuration
|
||||
}),
|
||||
filters,
|
||||
notifications_filter: {
|
||||
follow: true,
|
||||
|
12
src/utils/slices/instances/updateConfiguration.ts
Normal file
12
src/utils/slices/instances/updateConfiguration.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import apiInstance from '@api/instance'
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||
|
||||
export const updateConfiguration = createAsyncThunk(
|
||||
'instances/updateConfiguration',
|
||||
async (): Promise<Mastodon.Instance> => {
|
||||
return apiInstance<Mastodon.Instance>({
|
||||
method: 'get',
|
||||
url: `instance`
|
||||
}).then(res => res.body)
|
||||
}
|
||||
)
|
@ -6,6 +6,7 @@ import { findIndex } from 'lodash'
|
||||
import addInstance from './instances/add'
|
||||
import removeInstance from './instances/remove'
|
||||
import { updateAccountPreferences } from './instances/updateAccountPreferences'
|
||||
import { updateConfiguration } from './instances/updateConfiguration'
|
||||
import { updateFilters } from './instances/updateFilters'
|
||||
import { updateInstancePush } from './instances/updatePush'
|
||||
import { updateInstancePushAlert } from './instances/updatePushAlert'
|
||||
@ -21,13 +22,14 @@ export type Instance = {
|
||||
token: string
|
||||
uri: Mastodon.Instance['uri']
|
||||
urls: Mastodon.Instance['urls']
|
||||
max_toot_chars: number
|
||||
account: {
|
||||
id: Mastodon.Account['id']
|
||||
acct: Mastodon.Account['acct']
|
||||
avatarStatic: Mastodon.Account['avatar_static']
|
||||
preferences: Mastodon.Preferences
|
||||
}
|
||||
max_toot_chars?: number // To be deprecated in v4
|
||||
configuration?: Mastodon.Instance['configuration']
|
||||
filters: Mastodon.Filter[]
|
||||
notifications_filter: {
|
||||
follow: boolean
|
||||
@ -107,8 +109,8 @@ export const instancesInitialState: InstancesState = {
|
||||
instances: []
|
||||
}
|
||||
|
||||
const findInstanceActive = (state: Instance[]) =>
|
||||
state.findIndex(instance => instance.active)
|
||||
const findInstanceActive = (instances: Instance[]) =>
|
||||
instances.findIndex(instance => instance.active)
|
||||
|
||||
const instancesSlice = createSlice({
|
||||
name: 'instances',
|
||||
@ -254,6 +256,18 @@ const instancesSlice = createSlice({
|
||||
console.error(action.error)
|
||||
})
|
||||
|
||||
// Update Instance Configuration
|
||||
.addCase(updateConfiguration.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].max_toot_chars =
|
||||
action.payload.max_toot_chars
|
||||
state.instances[activeIndex].configuration =
|
||||
action.payload.configuration
|
||||
})
|
||||
.addCase(updateConfiguration.rejected, (_, action) => {
|
||||
console.error(action.error)
|
||||
})
|
||||
|
||||
// Update Instance Push Global
|
||||
.addCase(updateInstancePush.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
@ -314,56 +328,62 @@ export const getInstanceActive = ({ instances: { instances } }: RootState) =>
|
||||
export const getInstances = ({ instances: { instances } }: RootState) =>
|
||||
instances
|
||||
|
||||
export const getInstance = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive] : null
|
||||
}
|
||||
export const getInstance = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]
|
||||
|
||||
export const getInstanceUrl = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].url : null
|
||||
}
|
||||
export const getInstanceUrl = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.url
|
||||
|
||||
export const getInstanceUri = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].uri : null
|
||||
}
|
||||
export const getInstanceUri = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.uri
|
||||
|
||||
export const getInstanceUrls = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].urls : null
|
||||
}
|
||||
export const getInstanceUrls = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.urls
|
||||
|
||||
export const getInstanceMaxTootChar = ({
|
||||
/* Get Instance Configuration */
|
||||
export const getInstanceConfigurationStatusMaxChars = ({
|
||||
instances: { instances }
|
||||
}: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].max_toot_chars : 500
|
||||
}
|
||||
}: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.configuration?.statuses
|
||||
.max_characters ||
|
||||
instances[findInstanceActive(instances)]?.max_toot_chars ||
|
||||
500
|
||||
|
||||
export const getInstanceAccount = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].account : null
|
||||
}
|
||||
export const getInstanceConfigurationStatusMaxAttachments = ({
|
||||
instances: { instances }
|
||||
}: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.configuration?.statuses
|
||||
.max_media_attachments || 4
|
||||
|
||||
export const getInstanceConfigurationStatusCharsURL = ({
|
||||
instances: { instances }
|
||||
}: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.configuration?.statuses
|
||||
.characters_reserved_per_url || 23
|
||||
|
||||
export const getInstanceConfigurationPoll = ({
|
||||
instances: { instances }
|
||||
}: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.configuration?.polls || {
|
||||
max_options: 4,
|
||||
max_characters_per_option: 50,
|
||||
min_expiration: 300,
|
||||
max_expiration: 2629746
|
||||
}
|
||||
/* END */
|
||||
|
||||
export const getInstanceAccount = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.account
|
||||
|
||||
export const getInstanceNotificationsFilter = ({
|
||||
instances: { instances }
|
||||
}: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1
|
||||
? instances[instanceActive].notifications_filter
|
||||
: null
|
||||
}
|
||||
}: RootState) => instances[findInstanceActive(instances)].notifications_filter
|
||||
|
||||
export const getInstancePush = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].push : null
|
||||
}
|
||||
export const getInstancePush = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.push
|
||||
|
||||
export const getInstanceDrafts = ({ instances: { instances } }: RootState) => {
|
||||
const instanceActive = findInstanceActive(instances)
|
||||
return instanceActive !== -1 ? instances[instanceActive].drafts : null
|
||||
}
|
||||
export const getInstanceDrafts = ({ instances: { instances } }: RootState) =>
|
||||
instances[findInstanceActive(instances)]?.drafts
|
||||
|
||||
export const {
|
||||
updateInstanceActive,
|
||||
|
Loading…
x
Reference in New Issue
Block a user