157 lines
4.2 KiB
TypeScript
157 lines
4.2 KiB
TypeScript
import { PayloadAction, createSelector, createSlice } from "@reduxjs/toolkit";
|
|
import { GetSiteResponse } from "lemmy-js-client";
|
|
import { AppDispatch, RootState } from "../../store";
|
|
import {
|
|
clientSelector,
|
|
userHandleSelector,
|
|
handleSelector,
|
|
} from "./authSelectors";
|
|
import { getRemoteHandle } from "../../helpers/lemmy";
|
|
import { customBackOff } from "../../services/lemmy";
|
|
|
|
interface SiteState {
|
|
failedAttempt: number;
|
|
loading: boolean;
|
|
response: GetSiteResponse | undefined;
|
|
}
|
|
|
|
const initialState: SiteState = {
|
|
failedAttempt: 0,
|
|
loading: false,
|
|
response: undefined,
|
|
};
|
|
|
|
export const siteSlice = createSlice({
|
|
name: "site",
|
|
initialState,
|
|
reducers: {
|
|
loadingSite(state) {
|
|
state.loading = true;
|
|
},
|
|
failedSite(state) {
|
|
state.failedAttempt++;
|
|
state.loading = false;
|
|
},
|
|
receivedSite(state, action: PayloadAction<GetSiteResponse>) {
|
|
state.response = action.payload;
|
|
state.loading = false;
|
|
state.failedAttempt = 0;
|
|
},
|
|
resetSite() {
|
|
return initialState;
|
|
},
|
|
},
|
|
});
|
|
|
|
// Action creators are generated for each case reducer function
|
|
export const { loadingSite, failedSite, receivedSite, resetSite } =
|
|
siteSlice.actions;
|
|
|
|
export default siteSlice.reducer;
|
|
|
|
export const isAdminSelector = (state: RootState) =>
|
|
state.site.response?.my_user?.local_user_view.local_user.admin;
|
|
|
|
export const isDownvoteEnabledSelector = (state: RootState) =>
|
|
state.site.response?.site_view.local_site.enable_downvotes !== false;
|
|
|
|
export const localUserSelector = (state: RootState) =>
|
|
state.site.response?.my_user?.local_user_view.local_user;
|
|
|
|
export const userPersonSelector = (state: RootState) =>
|
|
state.site.response?.my_user?.local_user_view?.person;
|
|
|
|
export const lemmyVersionSelector = (state: RootState) =>
|
|
state.site.response?.version;
|
|
|
|
export const followIdsSelector = createSelector(
|
|
[(state: RootState) => state.site.response?.my_user?.follows],
|
|
(follows) => (follows ?? []).map((follow) => follow.community.id),
|
|
);
|
|
|
|
export const moderatesSelector = (state: RootState) =>
|
|
state.site.response?.my_user?.moderates;
|
|
|
|
/**
|
|
* Used to determine if request is stale (for other lemmy account and/or instance)
|
|
*/
|
|
const siteReqIdSelector = createSelector(
|
|
[(state: RootState) => state.auth.connectedInstance, handleSelector],
|
|
(profile, connectedInstance) =>
|
|
connectedInstance ? getSiteReqId(connectedInstance, profile) : "",
|
|
);
|
|
|
|
export const getSiteIfNeeded =
|
|
() => async (dispatch: AppDispatch, getState: () => RootState) => {
|
|
if (getState().site.response) return;
|
|
if (getState().site.loading) return;
|
|
|
|
dispatch(getSite());
|
|
};
|
|
|
|
export const getSite =
|
|
() => async (dispatch: AppDispatch, getState: () => RootState) => {
|
|
const reqId = siteReqIdSelector(getState());
|
|
let site;
|
|
|
|
dispatch(loadingSite());
|
|
|
|
try {
|
|
site = await clientSelector(getState()).getSite();
|
|
} catch (error) {
|
|
dispatch(failedSite());
|
|
|
|
(async () => {
|
|
await customBackOff(getState().site.failedAttempt);
|
|
|
|
// Site or user changed before site response resolved
|
|
if (reqId !== siteReqIdSelector(getState())) return;
|
|
|
|
dispatch(getSite());
|
|
})();
|
|
|
|
throw error;
|
|
}
|
|
|
|
// Site or user changed before site response resolved
|
|
if (reqId !== siteReqIdSelector(getState())) return;
|
|
|
|
dispatch(receivedSite(site));
|
|
};
|
|
|
|
export const showNsfw =
|
|
(show: boolean) =>
|
|
async (dispatch: AppDispatch, getState: () => RootState) => {
|
|
// https://github.com/LemmyNet/lemmy/issues/3565
|
|
const person = getState().site.response?.my_user?.local_user_view.person;
|
|
|
|
if (!person || userHandleSelector(getState()) !== getRemoteHandle(person))
|
|
throw new Error("user mismatch");
|
|
|
|
await clientSelector(getState())?.saveUserSettings({
|
|
avatar: person?.avatar || "",
|
|
show_nsfw: show,
|
|
});
|
|
|
|
await dispatch(getSite());
|
|
};
|
|
|
|
export const blockInstance =
|
|
(block: boolean, id: number) =>
|
|
async (dispatch: AppDispatch, getState: () => RootState) => {
|
|
if (!id) return;
|
|
|
|
await clientSelector(getState())?.blockInstance({
|
|
instance_id: id,
|
|
block,
|
|
});
|
|
|
|
await dispatch(getSite());
|
|
};
|
|
|
|
function getSiteReqId(instance: string, handle: string | undefined) {
|
|
if (!handle) return instance;
|
|
|
|
return `${instance}-${handle}`;
|
|
}
|