forked from Archives/Athou_commafeed
use typed createAsyncThunk
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import { RootState } from "app/store"
|
import { createAppAsyncThunk, RootState } from "app/store"
|
||||||
import { Entries, Entry, MarkRequest, TagRequest } from "app/types"
|
import { Entry, MarkRequest, TagRequest } from "app/types"
|
||||||
import { scrollToWithCallback } from "app/utils"
|
import { scrollToWithCallback } from "app/utils"
|
||||||
import { flushSync } from "react-dom"
|
import { flushSync } from "react-dom"
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
@@ -47,27 +47,24 @@ const initialState: EntriesState = {
|
|||||||
|
|
||||||
const getEndpoint = (sourceType: EntrySourceType) =>
|
const getEndpoint = (sourceType: EntrySourceType) =>
|
||||||
sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries
|
sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries
|
||||||
export const loadEntries = createAsyncThunk<
|
export const loadEntries = createAppAsyncThunk(
|
||||||
Entries,
|
"entries/load",
|
||||||
{ source: EntrySource; clearSearch: boolean },
|
async (
|
||||||
{
|
arg: {
|
||||||
state: RootState
|
source: EntrySource
|
||||||
}
|
clearSearch: boolean
|
||||||
>("entries/load", async (arg, thunkApi) => {
|
},
|
||||||
if (arg.clearSearch) thunkApi.dispatch(setSearch(""))
|
thunkApi
|
||||||
|
) => {
|
||||||
|
if (arg.clearSearch) thunkApi.dispatch(setSearch(""))
|
||||||
|
|
||||||
const state = thunkApi.getState()
|
const state = thunkApi.getState()
|
||||||
const endpoint = getEndpoint(arg.source.type)
|
const endpoint = getEndpoint(arg.source.type)
|
||||||
const result = await endpoint(buildGetEntriesPaginatedRequest(state, arg.source, 0))
|
const result = await endpoint(buildGetEntriesPaginatedRequest(state, arg.source, 0))
|
||||||
return result.data
|
return result.data
|
||||||
})
|
|
||||||
export const loadMoreEntries = createAsyncThunk<
|
|
||||||
Entries,
|
|
||||||
void,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
}
|
||||||
>("entries/loadMore", async (_, thunkApi) => {
|
)
|
||||||
|
export const loadMoreEntries = createAppAsyncThunk("entries/loadMore", async (_, thunkApi) => {
|
||||||
const state = thunkApi.getState()
|
const state = thunkApi.getState()
|
||||||
const { source } = state.entries
|
const { source } = state.entries
|
||||||
const offset =
|
const offset =
|
||||||
@@ -85,22 +82,16 @@ const buildGetEntriesPaginatedRequest = (state: RootState, source: EntrySource,
|
|||||||
tag: source.type === "tag" ? source.id : undefined,
|
tag: source.type === "tag" ? source.id : undefined,
|
||||||
keywords: state.entries.search,
|
keywords: state.entries.search,
|
||||||
})
|
})
|
||||||
export const reloadEntries = createAsyncThunk<
|
export const reloadEntries = createAppAsyncThunk("entries/reload", async (arg, thunkApi) => {
|
||||||
void,
|
|
||||||
void,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("entries/reload", async (arg, thunkApi) => {
|
|
||||||
const state = thunkApi.getState()
|
const state = thunkApi.getState()
|
||||||
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
|
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
|
||||||
})
|
})
|
||||||
export const search = createAsyncThunk<void, string, { state: RootState }>("entries/search", async (arg, thunkApi) => {
|
export const search = createAppAsyncThunk("entries/search", async (arg: string, thunkApi) => {
|
||||||
const state = thunkApi.getState()
|
const state = thunkApi.getState()
|
||||||
thunkApi.dispatch(setSearch(arg))
|
thunkApi.dispatch(setSearch(arg))
|
||||||
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
|
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
|
||||||
})
|
})
|
||||||
export const markEntry = createAsyncThunk(
|
export const markEntry = createAppAsyncThunk(
|
||||||
"entries/entry/mark",
|
"entries/entry/mark",
|
||||||
(arg: { entry: Entry; read: boolean }) => {
|
(arg: { entry: Entry; read: boolean }) => {
|
||||||
client.entry.mark({
|
client.entry.mark({
|
||||||
@@ -112,9 +103,15 @@ export const markEntry = createAsyncThunk(
|
|||||||
condition: arg => arg.entry.read !== arg.read,
|
condition: arg => arg.entry.read !== arg.read,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
export const markMultipleEntries = createAsyncThunk(
|
export const markMultipleEntries = createAppAsyncThunk(
|
||||||
"entries/entry/markMultiple",
|
"entries/entry/markMultiple",
|
||||||
async (arg: { entries: Entry[]; read: boolean }, thunkApi) => {
|
async (
|
||||||
|
arg: {
|
||||||
|
entries: Entry[]
|
||||||
|
read: boolean
|
||||||
|
},
|
||||||
|
thunkApi
|
||||||
|
) => {
|
||||||
const requests: MarkRequest[] = arg.entries.map(e => ({
|
const requests: MarkRequest[] = arg.entries.map(e => ({
|
||||||
id: e.id,
|
id: e.id,
|
||||||
read: arg.read,
|
read: arg.read,
|
||||||
@@ -123,88 +120,90 @@ export const markMultipleEntries = createAsyncThunk(
|
|||||||
thunkApi.dispatch(reloadTree())
|
thunkApi.dispatch(reloadTree())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
export const markEntriesUpToEntry = createAsyncThunk<void, Entry, { state: RootState }>(
|
export const markEntriesUpToEntry = createAppAsyncThunk("entries/entry/upToEntry", async (arg: Entry, thunkApi) => {
|
||||||
"entries/entry/upToEntry",
|
const state = thunkApi.getState()
|
||||||
async (arg, thunkApi) => {
|
const { entries } = state.entries
|
||||||
const state = thunkApi.getState()
|
|
||||||
const { entries } = state.entries
|
|
||||||
|
|
||||||
const index = entries.findIndex(e => e.id === arg.id)
|
const index = entries.findIndex(e => e.id === arg.id)
|
||||||
if (index === -1) return
|
if (index === -1) return
|
||||||
|
|
||||||
thunkApi.dispatch(
|
thunkApi.dispatch(
|
||||||
markMultipleEntries({
|
markMultipleEntries({
|
||||||
entries: entries.slice(0, index + 1),
|
entries: entries.slice(0, index + 1),
|
||||||
read: true,
|
read: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
export const markAllEntries = createAppAsyncThunk(
|
||||||
|
"entries/entry/markAll",
|
||||||
|
async (
|
||||||
|
arg: {
|
||||||
|
sourceType: EntrySourceType
|
||||||
|
req: MarkRequest
|
||||||
|
},
|
||||||
|
thunkApi
|
||||||
|
) => {
|
||||||
|
const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries
|
||||||
|
await endpoint(arg.req)
|
||||||
|
thunkApi.dispatch(reloadEntries())
|
||||||
|
thunkApi.dispatch(reloadTree())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
export const markAllEntries = createAsyncThunk<
|
export const starEntry = createAppAsyncThunk("entries/entry/star", (arg: { entry: Entry; starred: boolean }) => {
|
||||||
void,
|
|
||||||
{ sourceType: EntrySourceType; req: MarkRequest },
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("entries/entry/markAll", async (arg, thunkApi) => {
|
|
||||||
const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries
|
|
||||||
await endpoint(arg.req)
|
|
||||||
thunkApi.dispatch(reloadEntries())
|
|
||||||
thunkApi.dispatch(reloadTree())
|
|
||||||
})
|
|
||||||
export const starEntry = createAsyncThunk("entries/entry/star", (arg: { entry: Entry; starred: boolean }) => {
|
|
||||||
client.entry.star({
|
client.entry.star({
|
||||||
id: arg.entry.id,
|
id: arg.entry.id,
|
||||||
feedId: +arg.entry.feedId,
|
feedId: +arg.entry.feedId,
|
||||||
starred: arg.starred,
|
starred: arg.starred,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
export const selectEntry = createAsyncThunk<
|
export const selectEntry = createAppAsyncThunk(
|
||||||
void,
|
"entries/entry/select",
|
||||||
{
|
(
|
||||||
entry: Entry
|
arg: {
|
||||||
expand: boolean
|
entry: Entry
|
||||||
markAsRead: boolean
|
expand: boolean
|
||||||
scrollToEntry: boolean
|
markAsRead: boolean
|
||||||
},
|
scrollToEntry: boolean
|
||||||
{ state: RootState }
|
},
|
||||||
>("entries/entry/select", (arg, thunkApi) => {
|
thunkApi
|
||||||
const state = thunkApi.getState()
|
) => {
|
||||||
const entry = state.entries.entries.find(e => e.id === arg.entry.id)
|
const state = thunkApi.getState()
|
||||||
if (!entry) return
|
const entry = state.entries.entries.find(e => e.id === arg.entry.id)
|
||||||
|
if (!entry) return
|
||||||
|
|
||||||
// flushSync is required because we need the newly selected entry to be expanded
|
// flushSync is required because we need the newly selected entry to be expanded
|
||||||
// and the previously selected entry to be collapsed to be able to scroll to the right position
|
// and the previously selected entry to be collapsed to be able to scroll to the right position
|
||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
// mark as read if requested
|
// mark as read if requested
|
||||||
if (arg.markAsRead) {
|
if (arg.markAsRead) {
|
||||||
thunkApi.dispatch(markEntry({ entry, read: true }))
|
thunkApi.dispatch(markEntry({ entry, read: true }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// set entry as selected
|
// set entry as selected
|
||||||
thunkApi.dispatch(entriesSlice.actions.setSelectedEntry(entry))
|
thunkApi.dispatch(entriesSlice.actions.setSelectedEntry(entry))
|
||||||
|
|
||||||
// expand if requested
|
// expand if requested
|
||||||
const previouslySelectedEntry = state.entries.entries.find(e => e.id === state.entries.selectedEntryId)
|
const previouslySelectedEntry = state.entries.entries.find(e => e.id === state.entries.selectedEntryId)
|
||||||
if (previouslySelectedEntry) {
|
if (previouslySelectedEntry) {
|
||||||
thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry: previouslySelectedEntry, expanded: false }))
|
thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry: previouslySelectedEntry, expanded: false }))
|
||||||
}
|
}
|
||||||
thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry, expanded: arg.expand }))
|
thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry, expanded: arg.expand }))
|
||||||
})
|
})
|
||||||
|
|
||||||
if (arg.scrollToEntry) {
|
if (arg.scrollToEntry) {
|
||||||
const entryElement = document.getElementById(Constants.dom.entryId(entry))
|
const entryElement = document.getElementById(Constants.dom.entryId(entry))
|
||||||
if (entryElement) {
|
if (entryElement) {
|
||||||
const alwaysScrollToEntry = state.user.settings?.alwaysScrollToEntry
|
const alwaysScrollToEntry = state.user.settings?.alwaysScrollToEntry
|
||||||
const entryEntirelyVisible = Constants.layout.isTopVisible(entryElement) && Constants.layout.isBottomVisible(entryElement)
|
const entryEntirelyVisible = Constants.layout.isTopVisible(entryElement) && Constants.layout.isBottomVisible(entryElement)
|
||||||
if (alwaysScrollToEntry || !entryEntirelyVisible) {
|
if (alwaysScrollToEntry || !entryEntirelyVisible) {
|
||||||
const scrollSpeed = state.user.settings?.scrollSpeed
|
const scrollSpeed = state.user.settings?.scrollSpeed
|
||||||
thunkApi.dispatch(entriesSlice.actions.setScrollingToEntry(true))
|
thunkApi.dispatch(entriesSlice.actions.setScrollingToEntry(true))
|
||||||
scrollToEntry(entryElement, scrollSpeed, () => thunkApi.dispatch(entriesSlice.actions.setScrollingToEntry(false)))
|
scrollToEntry(entryElement, scrollSpeed, () => thunkApi.dispatch(entriesSlice.actions.setScrollingToEntry(false)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
const scrollToEntry = (entryElement: HTMLElement, scrollSpeed: number | undefined, onScrollEnded: () => void) => {
|
const scrollToEntry = (entryElement: HTMLElement, scrollSpeed: number | undefined, onScrollEnded: () => void) => {
|
||||||
scrollToWithCallback({
|
scrollToWithCallback({
|
||||||
options: {
|
options: {
|
||||||
@@ -216,59 +215,57 @@ const scrollToEntry = (entryElement: HTMLElement, scrollSpeed: number | undefine
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectPreviousEntry = createAsyncThunk<
|
export const selectPreviousEntry = createAppAsyncThunk(
|
||||||
void,
|
"entries/entry/selectPrevious",
|
||||||
{
|
(
|
||||||
expand: boolean
|
arg: {
|
||||||
markAsRead: boolean
|
expand: boolean
|
||||||
scrollToEntry: boolean
|
markAsRead: boolean
|
||||||
},
|
scrollToEntry: boolean
|
||||||
{ state: RootState }
|
},
|
||||||
>("entries/entry/selectPrevious", (arg, thunkApi) => {
|
thunkApi
|
||||||
const state = thunkApi.getState()
|
) => {
|
||||||
const { entries } = state.entries
|
const state = thunkApi.getState()
|
||||||
const previousIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) - 1
|
const { entries } = state.entries
|
||||||
if (previousIndex >= 0) {
|
const previousIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) - 1
|
||||||
thunkApi.dispatch(
|
if (previousIndex >= 0) {
|
||||||
selectEntry({
|
thunkApi.dispatch(
|
||||||
entry: entries[previousIndex],
|
selectEntry({
|
||||||
expand: arg.expand,
|
entry: entries[previousIndex],
|
||||||
markAsRead: arg.markAsRead,
|
expand: arg.expand,
|
||||||
scrollToEntry: arg.scrollToEntry,
|
markAsRead: arg.markAsRead,
|
||||||
})
|
scrollToEntry: arg.scrollToEntry,
|
||||||
)
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
export const selectNextEntry = createAsyncThunk<
|
export const selectNextEntry = createAppAsyncThunk(
|
||||||
void,
|
"entries/entry/selectNext",
|
||||||
{
|
(
|
||||||
expand: boolean
|
arg: {
|
||||||
markAsRead: boolean
|
expand: boolean
|
||||||
scrollToEntry: boolean
|
markAsRead: boolean
|
||||||
},
|
scrollToEntry: boolean
|
||||||
{ state: RootState }
|
},
|
||||||
>("entries/entry/selectNext", (arg, thunkApi) => {
|
thunkApi
|
||||||
const state = thunkApi.getState()
|
) => {
|
||||||
const { entries } = state.entries
|
const state = thunkApi.getState()
|
||||||
const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1
|
const { entries } = state.entries
|
||||||
if (nextIndex < entries.length) {
|
const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1
|
||||||
thunkApi.dispatch(
|
if (nextIndex < entries.length) {
|
||||||
selectEntry({
|
thunkApi.dispatch(
|
||||||
entry: entries[nextIndex],
|
selectEntry({
|
||||||
expand: arg.expand,
|
entry: entries[nextIndex],
|
||||||
markAsRead: arg.markAsRead,
|
expand: arg.expand,
|
||||||
scrollToEntry: arg.scrollToEntry,
|
markAsRead: arg.markAsRead,
|
||||||
})
|
scrollToEntry: arg.scrollToEntry,
|
||||||
)
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
export const tagEntry = createAsyncThunk<
|
export const tagEntry = createAppAsyncThunk("entries/entry/tag", async (arg: TagRequest, thunkApi) => {
|
||||||
void,
|
|
||||||
TagRequest,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("entries/entry/tag", async (arg, thunkApi) => {
|
|
||||||
await client.entry.tag(arg)
|
await client.entry.tag(arg)
|
||||||
thunkApi.dispatch(reloadTags())
|
thunkApi.dispatch(reloadTags())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import { RootState } from "app/store"
|
import { createAppAsyncThunk } from "app/store"
|
||||||
|
|
||||||
interface RedirectState {
|
interface RedirectState {
|
||||||
to?: string
|
to?: string
|
||||||
@@ -8,52 +8,46 @@ interface RedirectState {
|
|||||||
|
|
||||||
const initialState: RedirectState = {}
|
const initialState: RedirectState = {}
|
||||||
|
|
||||||
export const redirectToLogin = createAsyncThunk("redirect/login", (_, thunkApi) => thunkApi.dispatch(redirectTo("/login")))
|
export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkApi) => thunkApi.dispatch(redirectTo("/login")))
|
||||||
export const redirectToRegistration = createAsyncThunk("redirect/register", (_, thunkApi) => thunkApi.dispatch(redirectTo("/register")))
|
export const redirectToRegistration = createAppAsyncThunk("redirect/register", (_, thunkApi) => thunkApi.dispatch(redirectTo("/register")))
|
||||||
export const redirectToPasswordRecovery = createAsyncThunk("redirect/passwordRecovery", (_, thunkApi) =>
|
export const redirectToPasswordRecovery = createAppAsyncThunk("redirect/passwordRecovery", (_, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo("/passwordRecovery"))
|
thunkApi.dispatch(redirectTo("/passwordRecovery"))
|
||||||
)
|
)
|
||||||
export const redirectToApiDocumentation = createAsyncThunk("redirect/api", (_, thunkApi) => thunkApi.dispatch(redirectTo("/api")))
|
export const redirectToApiDocumentation = createAppAsyncThunk("redirect/api", (_, thunkApi) => thunkApi.dispatch(redirectTo("/api")))
|
||||||
|
|
||||||
export const redirectToSelectedSource = createAsyncThunk<
|
export const redirectToSelectedSource = createAppAsyncThunk("redirect/selectedSource", (_, thunkApi) => {
|
||||||
void,
|
|
||||||
void,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("redirect/selectedSource", (_, thunkApi) => {
|
|
||||||
const { source } = thunkApi.getState().entries
|
const { source } = thunkApi.getState().entries
|
||||||
thunkApi.dispatch(redirectTo(`/app/${source.type}/${source.id}`))
|
thunkApi.dispatch(redirectTo(`/app/${source.type}/${source.id}`))
|
||||||
})
|
})
|
||||||
export const redirectToCategory = createAsyncThunk("redirect/category", (id: string, thunkApi) =>
|
export const redirectToCategory = createAppAsyncThunk("redirect/category", (id: string, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo(`/app/category/${id}`))
|
thunkApi.dispatch(redirectTo(`/app/category/${id}`))
|
||||||
)
|
)
|
||||||
export const redirectToRootCategory = createAsyncThunk("redirect/category/root", (_, thunkApi) =>
|
export const redirectToRootCategory = createAppAsyncThunk("redirect/category/root", (_, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectToCategory(Constants.categories.all.id))
|
thunkApi.dispatch(redirectToCategory(Constants.categories.all.id))
|
||||||
)
|
)
|
||||||
export const redirectToCategoryDetails = createAsyncThunk("redirect/category/details", (id: string, thunkApi) =>
|
export const redirectToCategoryDetails = createAppAsyncThunk("redirect/category/details", (id: string, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo(`/app/category/${id}/details`))
|
thunkApi.dispatch(redirectTo(`/app/category/${id}/details`))
|
||||||
)
|
)
|
||||||
export const redirectToFeed = createAsyncThunk("redirect/feed", (id: string | number, thunkApi) =>
|
export const redirectToFeed = createAppAsyncThunk("redirect/feed", (id: string | number, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo(`/app/feed/${id}`))
|
thunkApi.dispatch(redirectTo(`/app/feed/${id}`))
|
||||||
)
|
)
|
||||||
export const redirectToFeedDetails = createAsyncThunk("redirect/feed/details", (id: string, thunkApi) =>
|
export const redirectToFeedDetails = createAppAsyncThunk("redirect/feed/details", (id: string, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo(`/app/feed/${id}/details`))
|
thunkApi.dispatch(redirectTo(`/app/feed/${id}/details`))
|
||||||
)
|
)
|
||||||
export const redirectToTag = createAsyncThunk("redirect/tag", (id: string, thunkApi) => thunkApi.dispatch(redirectTo(`/app/tag/${id}`)))
|
export const redirectToTag = createAppAsyncThunk("redirect/tag", (id: string, thunkApi) => thunkApi.dispatch(redirectTo(`/app/tag/${id}`)))
|
||||||
export const redirectToTagDetails = createAsyncThunk("redirect/tag/details", (id: string, thunkApi) =>
|
export const redirectToTagDetails = createAppAsyncThunk("redirect/tag/details", (id: string, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo(`/app/tag/${id}/details`))
|
thunkApi.dispatch(redirectTo(`/app/tag/${id}/details`))
|
||||||
)
|
)
|
||||||
export const redirectToAdd = createAsyncThunk("redirect/add", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/add")))
|
export const redirectToAdd = createAppAsyncThunk("redirect/add", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/add")))
|
||||||
export const redirectToSettings = createAsyncThunk("redirect/settings", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/settings")))
|
export const redirectToSettings = createAppAsyncThunk("redirect/settings", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/settings")))
|
||||||
export const redirectToAdminUsers = createAsyncThunk("redirect/admin/users", (_, thunkApi) =>
|
export const redirectToAdminUsers = createAppAsyncThunk("redirect/admin/users", (_, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo("/app/admin/users"))
|
thunkApi.dispatch(redirectTo("/app/admin/users"))
|
||||||
)
|
)
|
||||||
export const redirectToMetrics = createAsyncThunk("redirect/admin/metrics", (_, thunkApi) =>
|
export const redirectToMetrics = createAppAsyncThunk("redirect/admin/metrics", (_, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo("/app/admin/metrics"))
|
thunkApi.dispatch(redirectTo("/app/admin/metrics"))
|
||||||
)
|
)
|
||||||
export const redirectToDonate = createAsyncThunk("redirect/donate", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/donate")))
|
export const redirectToDonate = createAppAsyncThunk("redirect/donate", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/donate")))
|
||||||
export const redirectToAbout = createAsyncThunk("redirect/about", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/about")))
|
export const redirectToAbout = createAppAsyncThunk("redirect/about", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/about")))
|
||||||
|
|
||||||
export const redirectSlice = createSlice({
|
export const redirectSlice = createSlice({
|
||||||
name: "redirect",
|
name: "redirect",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PayloadAction, createAsyncThunk, createSlice } from "@reduxjs/toolkit"
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
|
import { createAppAsyncThunk } from "app/store"
|
||||||
import { ServerInfo } from "app/types"
|
import { ServerInfo } from "app/types"
|
||||||
|
|
||||||
interface ServerState {
|
interface ServerState {
|
||||||
@@ -11,7 +12,7 @@ const initialState: ServerState = {
|
|||||||
webSocketConnected: false,
|
webSocketConnected: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reloadServerInfos = createAsyncThunk("server/infos", () => client.server.getServerInfos().then(r => r.data))
|
export const reloadServerInfos = createAppAsyncThunk("server/infos", () => client.server.getServerInfos().then(r => r.data))
|
||||||
export const serverSlice = createSlice({
|
export const serverSlice = createSlice({
|
||||||
name: "server",
|
name: "server",
|
||||||
initialState,
|
initialState,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
|
import { createAppAsyncThunk } from "app/store"
|
||||||
import { Category, CollapseRequest } from "app/types"
|
import { Category, CollapseRequest } from "app/types"
|
||||||
import { visitCategoryTree } from "app/utils"
|
import { visitCategoryTree } from "app/utils"
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
@@ -19,8 +20,8 @@ const initialState: TreeState = {
|
|||||||
sidebarVisible: true,
|
sidebarVisible: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reloadTree = createAsyncThunk("tree/reload", () => client.category.getRoot().then(r => r.data))
|
export const reloadTree = createAppAsyncThunk("tree/reload", () => client.category.getRoot().then(r => r.data))
|
||||||
export const collapseTreeCategory = createAsyncThunk("tree/category/collapse", async (req: CollapseRequest) =>
|
export const collapseTreeCategory = createAppAsyncThunk("tree/category/collapse", async (req: CollapseRequest) =>
|
||||||
client.category.collapse(req)
|
client.category.collapse(req)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { t } from "@lingui/macro"
|
||||||
import { showNotification } from "@mantine/notifications"
|
import { showNotification } from "@mantine/notifications"
|
||||||
import { createAsyncThunk, createSlice, isAnyOf } from "@reduxjs/toolkit"
|
import { createSlice, isAnyOf } from "@reduxjs/toolkit"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
import { RootState } from "app/store"
|
import { createAppAsyncThunk } from "app/store"
|
||||||
import { ReadingMode, ReadingOrder, Settings, SharingSettings, UserModel } from "app/types"
|
import { ReadingMode, ReadingOrder, Settings, SharingSettings, UserModel } from "app/types"
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import { reloadEntries } from "./entries"
|
import { reloadEntries } from "./entries"
|
||||||
@@ -15,121 +15,79 @@ interface UserState {
|
|||||||
|
|
||||||
const initialState: UserState = {}
|
const initialState: UserState = {}
|
||||||
|
|
||||||
export const reloadSettings = createAsyncThunk("settings/reload", () => client.user.getSettings().then(r => r.data))
|
export const reloadSettings = createAppAsyncThunk("settings/reload", () => client.user.getSettings().then(r => r.data))
|
||||||
export const reloadProfile = createAsyncThunk("profile/reload", () => client.user.getProfile().then(r => r.data))
|
export const reloadProfile = createAppAsyncThunk("profile/reload", () => client.user.getProfile().then(r => r.data))
|
||||||
export const reloadTags = createAsyncThunk("entries/tags", () => client.entry.getTags().then(r => r.data))
|
export const reloadTags = createAppAsyncThunk("entries/tags", () => client.entry.getTags().then(r => r.data))
|
||||||
export const changeReadingMode = createAsyncThunk<void, ReadingMode, { state: RootState }>(
|
export const changeReadingMode = createAppAsyncThunk("settings/readingMode", (readingMode: ReadingMode, thunkApi) => {
|
||||||
"settings/readingMode",
|
const { settings } = thunkApi.getState().user
|
||||||
(readingMode, thunkApi) => {
|
if (!settings) return
|
||||||
const { settings } = thunkApi.getState().user
|
client.user.saveSettings({ ...settings, readingMode })
|
||||||
if (!settings) return
|
thunkApi.dispatch(reloadEntries())
|
||||||
client.user.saveSettings({ ...settings, readingMode })
|
})
|
||||||
thunkApi.dispatch(reloadEntries())
|
export const changeReadingOrder = createAppAsyncThunk("settings/readingOrder", (readingOrder: ReadingOrder, thunkApi) => {
|
||||||
}
|
const { settings } = thunkApi.getState().user
|
||||||
)
|
if (!settings) return
|
||||||
export const changeReadingOrder = createAsyncThunk<void, ReadingOrder, { state: RootState }>(
|
client.user.saveSettings({ ...settings, readingOrder })
|
||||||
"settings/readingOrder",
|
thunkApi.dispatch(reloadEntries())
|
||||||
(readingOrder, thunkApi) => {
|
})
|
||||||
const { settings } = thunkApi.getState().user
|
export const changeLanguage = createAppAsyncThunk("settings/language", (language: string, thunkApi) => {
|
||||||
if (!settings) return
|
|
||||||
client.user.saveSettings({ ...settings, readingOrder })
|
|
||||||
thunkApi.dispatch(reloadEntries())
|
|
||||||
}
|
|
||||||
)
|
|
||||||
export const changeLanguage = createAsyncThunk<
|
|
||||||
void,
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("settings/language", (language, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
client.user.saveSettings({ ...settings, language })
|
client.user.saveSettings({ ...settings, language })
|
||||||
})
|
})
|
||||||
export const changeScrollSpeed = createAsyncThunk<
|
export const changeScrollSpeed = createAppAsyncThunk("settings/scrollSpeed", (speed: boolean, thunkApi) => {
|
||||||
void,
|
|
||||||
boolean,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("settings/scrollSpeed", (speed, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
client.user.saveSettings({ ...settings, scrollSpeed: speed ? 400 : 0 })
|
client.user.saveSettings({ ...settings, scrollSpeed: speed ? 400 : 0 })
|
||||||
})
|
})
|
||||||
export const changeShowRead = createAsyncThunk<
|
export const changeShowRead = createAppAsyncThunk("settings/showRead", (showRead: boolean, thunkApi) => {
|
||||||
void,
|
|
||||||
boolean,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("settings/showRead", (showRead, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
client.user.saveSettings({ ...settings, showRead })
|
client.user.saveSettings({ ...settings, showRead })
|
||||||
})
|
})
|
||||||
export const changeScrollMarks = createAsyncThunk<
|
export const changeScrollMarks = createAppAsyncThunk("settings/scrollMarks", (scrollMarks: boolean, thunkApi) => {
|
||||||
void,
|
|
||||||
boolean,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("settings/scrollMarks", (scrollMarks, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
client.user.saveSettings({ ...settings, scrollMarks })
|
client.user.saveSettings({ ...settings, scrollMarks })
|
||||||
})
|
})
|
||||||
export const changeAlwaysScrollToEntry = createAsyncThunk<
|
export const changeAlwaysScrollToEntry = createAppAsyncThunk("settings/alwaysScrollToEntry", (alwaysScrollToEntry: boolean, thunkApi) => {
|
||||||
void,
|
|
||||||
boolean,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("settings/alwaysScrollToEntry", (alwaysScrollToEntry, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
client.user.saveSettings({ ...settings, alwaysScrollToEntry })
|
client.user.saveSettings({ ...settings, alwaysScrollToEntry })
|
||||||
})
|
})
|
||||||
export const changeMarkAllAsReadConfirmation = createAsyncThunk<
|
export const changeMarkAllAsReadConfirmation = createAppAsyncThunk(
|
||||||
void,
|
"settings/markAllAsReadConfirmation",
|
||||||
boolean,
|
(markAllAsReadConfirmation: boolean, thunkApi) => {
|
||||||
{
|
const { settings } = thunkApi.getState().user
|
||||||
state: RootState
|
if (!settings) return
|
||||||
|
client.user.saveSettings({ ...settings, markAllAsReadConfirmation })
|
||||||
}
|
}
|
||||||
>("settings/markAllAsReadConfirmation", (markAllAsReadConfirmation, thunkApi) => {
|
)
|
||||||
const { settings } = thunkApi.getState().user
|
export const changeCustomContextMenu = createAppAsyncThunk("settings/customContextMenu", (customContextMenu: boolean, thunkApi) => {
|
||||||
if (!settings) return
|
|
||||||
client.user.saveSettings({ ...settings, markAllAsReadConfirmation })
|
|
||||||
})
|
|
||||||
export const changeCustomContextMenu = createAsyncThunk<
|
|
||||||
void,
|
|
||||||
boolean,
|
|
||||||
{
|
|
||||||
state: RootState
|
|
||||||
}
|
|
||||||
>("settings/customContextMenu", (customContextMenu, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
const { settings } = thunkApi.getState().user
|
||||||
if (!settings) return
|
if (!settings) return
|
||||||
client.user.saveSettings({ ...settings, customContextMenu })
|
client.user.saveSettings({ ...settings, customContextMenu })
|
||||||
})
|
})
|
||||||
export const changeSharingSetting = createAsyncThunk<
|
export const changeSharingSetting = createAppAsyncThunk(
|
||||||
void,
|
"settings/sharingSetting",
|
||||||
{ site: keyof SharingSettings; value: boolean },
|
(
|
||||||
{
|
sharingSetting: {
|
||||||
state: RootState
|
site: keyof SharingSettings
|
||||||
}
|
value: boolean
|
||||||
>("settings/sharingSetting", (sharingSetting, thunkApi) => {
|
|
||||||
const { settings } = thunkApi.getState().user
|
|
||||||
if (!settings) return
|
|
||||||
client.user.saveSettings({
|
|
||||||
...settings,
|
|
||||||
sharingSettings: {
|
|
||||||
...settings.sharingSettings,
|
|
||||||
[sharingSetting.site]: sharingSetting.value,
|
|
||||||
},
|
},
|
||||||
})
|
thunkApi
|
||||||
})
|
) => {
|
||||||
|
const { settings } = thunkApi.getState().user
|
||||||
|
if (!settings) return
|
||||||
|
client.user.saveSettings({
|
||||||
|
...settings,
|
||||||
|
sharingSettings: {
|
||||||
|
...settings.sharingSettings,
|
||||||
|
[sharingSetting.site]: sharingSetting.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export const userSlice = createSlice({
|
export const userSlice = createSlice({
|
||||||
name: "user",
|
name: "user",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { configureStore } from "@reduxjs/toolkit"
|
import { configureStore, createAsyncThunk } from "@reduxjs/toolkit"
|
||||||
import { setupListeners } from "@reduxjs/toolkit/query"
|
import { setupListeners } from "@reduxjs/toolkit/query"
|
||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
|
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
|
||||||
import entriesReducer from "./slices/entries"
|
import entriesReducer from "./slices/entries"
|
||||||
@@ -24,3 +24,7 @@ export type AppDispatch = typeof store.dispatch
|
|||||||
|
|
||||||
export const useAppDispatch: () => AppDispatch = useDispatch
|
export const useAppDispatch: () => AppDispatch = useDispatch
|
||||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||||
|
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
|
||||||
|
state: RootState
|
||||||
|
dispatch: AppDispatch
|
||||||
|
}>()
|
||||||
|
|||||||
Reference in New Issue
Block a user