342 lines
9.4 KiB
TypeScript
342 lines
9.4 KiB
TypeScript
import { useIonModal } from "@ionic/react";
|
|
import React, {
|
|
RefObject,
|
|
createContext,
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
import { useAppDispatch, useAppSelector } from "../../store";
|
|
import { changeAccount } from "../auth/authSlice";
|
|
import {
|
|
Comment,
|
|
CommentView,
|
|
Community,
|
|
Person,
|
|
PostView,
|
|
PrivateMessageView,
|
|
} from "lemmy-js-client";
|
|
import { Report, ReportHandle, ReportableItem } from "../report/Report";
|
|
import PostEditorModal from "../post/new/PostEditorModal";
|
|
import SelectTextModal from "../shared/SelectTextModal";
|
|
import ShareAsImageModal, {
|
|
ShareAsImageData,
|
|
} from "../share/asImage/ShareAsImageModal";
|
|
import AccountSwitcher from "./AccountSwitcher";
|
|
import { jwtSelector } from "./authSelectors";
|
|
import BanUserModal from "../moderation/ban/BanUserModal";
|
|
import CreateCrosspostDialog from "../post/crosspost/create/CreateCrosspostDialog";
|
|
import LoginModal from "./login/LoginModal";
|
|
import GenericMarkdownEditorModal, {
|
|
MarkdownEditorData,
|
|
} from "../shared/markdown/editing/modal/GenericMarkdownEditorModal";
|
|
import { NewPrivateMessage } from "../shared/markdown/editing/modal/contents/PrivateMessagePage";
|
|
import { CommentReplyItem } from "../shared/markdown/editing/modal/contents/CommentReplyPage";
|
|
|
|
export interface BanUserPayload {
|
|
user: Person;
|
|
community: Community;
|
|
}
|
|
|
|
interface IPageContext {
|
|
// used for ion presentingElement
|
|
pageRef: RefObject<HTMLElement | undefined> | undefined;
|
|
|
|
/**
|
|
* @returns true if login dialog was presented
|
|
*/
|
|
presentLoginIfNeeded: () => boolean;
|
|
|
|
/**
|
|
* @returns private message payload if submitted
|
|
*/
|
|
presentPrivateMessageCompose: (
|
|
item: NewPrivateMessage,
|
|
) => Promise<PrivateMessageView | undefined>;
|
|
|
|
/**
|
|
* @returns comment payload if replied
|
|
*/
|
|
presentCommentEdit: (item: Comment) => Promise<CommentView | undefined>;
|
|
|
|
/**
|
|
* @returns comment payload if replied
|
|
*/
|
|
presentCommentReply: (
|
|
item: CommentReplyItem,
|
|
) => Promise<CommentView | undefined>;
|
|
|
|
presentReport: (item: ReportableItem) => void;
|
|
|
|
/**
|
|
* @param postOrCommunity An existing post to be edited, or the community handle
|
|
* to submit the new post to
|
|
*/
|
|
presentPostEditor: (postOrCommunity: PostView | string) => void;
|
|
|
|
presentSelectText: (text: string) => void;
|
|
|
|
presentShareAsImage: (
|
|
post: PostView,
|
|
comment?: CommentView,
|
|
comments?: CommentView[],
|
|
) => void;
|
|
|
|
presentAccountSwitcher: () => void;
|
|
|
|
presentBanUser: (payload: BanUserPayload) => void;
|
|
|
|
presentCreateCrosspost: (post: PostView) => void;
|
|
}
|
|
|
|
export const PageContext = createContext<IPageContext>({
|
|
pageRef: undefined,
|
|
presentLoginIfNeeded: () => false,
|
|
presentCommentEdit: async () => undefined,
|
|
presentCommentReply: async () => undefined,
|
|
presentPrivateMessageCompose: async () => undefined,
|
|
presentReport: () => {},
|
|
presentPostEditor: () => {},
|
|
presentSelectText: () => {},
|
|
presentShareAsImage: () => {},
|
|
presentAccountSwitcher: () => {},
|
|
presentBanUser: () => {},
|
|
presentCreateCrosspost: () => {},
|
|
});
|
|
|
|
interface PageContextProvider {
|
|
value: Pick<IPageContext, "pageRef">;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function PageContextProvider({ value, children }: PageContextProvider) {
|
|
const dispatch = useAppDispatch();
|
|
const jwt = useAppSelector(jwtSelector);
|
|
const reportRef = useRef<ReportHandle>(null);
|
|
const shareAsImageDataRef = useRef<ShareAsImageData | null>(null);
|
|
|
|
const [presentShareAsImageModal, onDismissShareAsImageModal] = useIonModal(
|
|
ShareAsImageModal,
|
|
{
|
|
dataRef: shareAsImageDataRef,
|
|
onDismiss: (data?: string, role?: string) =>
|
|
onDismissShareAsImageModal(data, role),
|
|
},
|
|
);
|
|
|
|
const [isLoginOpen, setIsLoginOpen] = useState(false);
|
|
|
|
const presentLoginIfNeeded = useCallback(() => {
|
|
if (jwt) return false;
|
|
|
|
setIsLoginOpen(true);
|
|
return true;
|
|
}, [jwt]);
|
|
|
|
const presentShareAsImage = useCallback(
|
|
(post: PostView, comment?: CommentView, comments?: CommentView[]) => {
|
|
shareAsImageDataRef.current = {
|
|
post,
|
|
};
|
|
if (comment && comments) {
|
|
shareAsImageDataRef.current = {
|
|
...shareAsImageDataRef.current,
|
|
comment,
|
|
comments,
|
|
};
|
|
}
|
|
presentShareAsImageModal({
|
|
cssClass: "save-as-image-modal",
|
|
initialBreakpoint: 1,
|
|
breakpoints: [0, 1],
|
|
handle: false,
|
|
});
|
|
},
|
|
[presentShareAsImageModal],
|
|
);
|
|
|
|
// Markdown editor start
|
|
const markdownEditorData = useRef<MarkdownEditorData>();
|
|
const [isMarkdownEditorOpen, setIsMarkdownEditorOpen] = useState(false);
|
|
const presentMarkdownEditor = useCallback(
|
|
<T extends MarkdownEditorData>(data: Omit<T, "onSubmit">) =>
|
|
new Promise<Parameters<T["onSubmit"]>[0]>((resolve) => {
|
|
markdownEditorData.current = {
|
|
...data,
|
|
onSubmit: resolve,
|
|
} as T;
|
|
setIsMarkdownEditorOpen(true);
|
|
}),
|
|
[],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (isMarkdownEditorOpen) return;
|
|
|
|
markdownEditorData.current?.onSubmit(undefined);
|
|
markdownEditorData.current = undefined;
|
|
return;
|
|
}, [isMarkdownEditorOpen]);
|
|
|
|
const presentPrivateMessageCompose = useCallback<
|
|
IPageContext["presentPrivateMessageCompose"]
|
|
>(
|
|
(item) =>
|
|
presentMarkdownEditor({
|
|
type: "PRIVATE_MESSAGE",
|
|
item,
|
|
}) as ReturnType<IPageContext["presentPrivateMessageCompose"]>,
|
|
[presentMarkdownEditor],
|
|
);
|
|
|
|
const presentCommentEdit = useCallback<IPageContext["presentCommentEdit"]>(
|
|
(item) =>
|
|
presentMarkdownEditor({
|
|
type: "COMMENT_EDIT",
|
|
item,
|
|
}) as ReturnType<IPageContext["presentCommentEdit"]>,
|
|
[presentMarkdownEditor],
|
|
);
|
|
|
|
const presentCommentReply = useCallback<IPageContext["presentCommentReply"]>(
|
|
(item) =>
|
|
presentMarkdownEditor({
|
|
type: "COMMENT_REPLY",
|
|
item,
|
|
}) as ReturnType<IPageContext["presentCommentReply"]>,
|
|
[presentMarkdownEditor],
|
|
);
|
|
// Markdown editor end
|
|
|
|
// Edit/new post start
|
|
const postItem = useRef<PostView | string>();
|
|
const [isPostOpen, setIsPostOpen] = useState(false);
|
|
const presentPostEditor = useCallback(
|
|
(postOrCommunity: PostView | string) => {
|
|
postItem.current = postOrCommunity;
|
|
setIsPostOpen(true);
|
|
},
|
|
[],
|
|
);
|
|
// Edit/new post end
|
|
|
|
// Select text start
|
|
const selectTextItem = useRef<string | string>();
|
|
const [isSelectTextOpen, setIsSelectTextOpen] = useState(false);
|
|
const presentSelectText = useCallback((text: string) => {
|
|
selectTextItem.current = text;
|
|
setIsSelectTextOpen(true);
|
|
}, []);
|
|
// Select text end
|
|
|
|
// Ban user start
|
|
const banItem = useRef<BanUserPayload>();
|
|
const [isBanUserOpen, setIsBanUserOpen] = useState(false);
|
|
const presentBanUser = useCallback((banUserPayload: BanUserPayload) => {
|
|
banItem.current = banUserPayload;
|
|
setIsBanUserOpen(true);
|
|
}, []);
|
|
// Ban user end
|
|
|
|
const presentReport = useCallback((item: ReportableItem) => {
|
|
reportRef.current?.present(item);
|
|
}, []);
|
|
|
|
const [presentAccountSwitcherModal, onDismissAccountSwitcher] = useIonModal(
|
|
AccountSwitcher,
|
|
{
|
|
onDismiss: (data?: string, role?: string) =>
|
|
onDismissAccountSwitcher(data, role),
|
|
presentLogin: () => {
|
|
onDismissAccountSwitcher();
|
|
setIsLoginOpen(true);
|
|
},
|
|
onSelectAccount: (account: string) => dispatch(changeAccount(account)),
|
|
},
|
|
);
|
|
|
|
const presentAccountSwitcher = useCallback(() => {
|
|
presentAccountSwitcherModal({ cssClass: "small" });
|
|
}, [presentAccountSwitcherModal]);
|
|
|
|
const crosspost = useRef<PostView | undefined>();
|
|
const [presentCrosspost, onDismissCrosspost] = useIonModal(
|
|
CreateCrosspostDialog,
|
|
{
|
|
onDismiss: (data?: string, role?: string) =>
|
|
onDismissCrosspost(data, role),
|
|
post: crosspost.current!,
|
|
},
|
|
);
|
|
|
|
const presentCreateCrosspost = useCallback(
|
|
(post: PostView) => {
|
|
crosspost.current = post;
|
|
presentCrosspost({ cssClass: "transparent-scroll dark" });
|
|
},
|
|
[presentCrosspost],
|
|
);
|
|
|
|
const currentValue = useMemo(
|
|
() => ({
|
|
...value,
|
|
presentLoginIfNeeded,
|
|
presentPrivateMessageCompose,
|
|
presentCommentEdit,
|
|
presentCommentReply,
|
|
presentReport,
|
|
presentPostEditor,
|
|
presentSelectText,
|
|
presentShareAsImage,
|
|
presentAccountSwitcher,
|
|
presentBanUser,
|
|
presentCreateCrosspost,
|
|
}),
|
|
[
|
|
presentPrivateMessageCompose,
|
|
presentCommentEdit,
|
|
presentCommentReply,
|
|
presentLoginIfNeeded,
|
|
presentPostEditor,
|
|
presentReport,
|
|
presentSelectText,
|
|
presentShareAsImage,
|
|
presentAccountSwitcher,
|
|
presentBanUser,
|
|
presentCreateCrosspost,
|
|
value,
|
|
],
|
|
);
|
|
|
|
return (
|
|
<PageContext.Provider value={currentValue}>
|
|
{children}
|
|
|
|
<LoginModal isOpen={isLoginOpen} setIsOpen={setIsLoginOpen} />
|
|
<GenericMarkdownEditorModal
|
|
{...markdownEditorData.current!}
|
|
isOpen={isMarkdownEditorOpen}
|
|
setIsOpen={setIsMarkdownEditorOpen}
|
|
/>
|
|
<Report ref={reportRef} />
|
|
<PostEditorModal
|
|
postOrCommunity={postItem.current!}
|
|
isOpen={isPostOpen}
|
|
setIsOpen={setIsPostOpen}
|
|
/>
|
|
<BanUserModal
|
|
item={banItem.current!}
|
|
isOpen={isBanUserOpen}
|
|
setIsOpen={setIsBanUserOpen}
|
|
/>
|
|
<SelectTextModal
|
|
text={selectTextItem.current!}
|
|
isOpen={isSelectTextOpen}
|
|
setIsOpen={setIsSelectTextOpen}
|
|
/>
|
|
</PageContext.Provider>
|
|
);
|
|
}
|