diff --git a/src/components/challenge-modal/challenge-modal.tsx b/src/components/challenge-modal/challenge-modal.tsx index 8a931e91..0ebb19dc 100644 --- a/src/components/challenge-modal/challenge-modal.tsx +++ b/src/components/challenge-modal/challenge-modal.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { FloatingFocusManager, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react'; import { Challenge as ChallengeType, useComment } from '@plebbit/plebbit-react-hooks'; import { useTranslation } from 'react-i18next'; -import useChallenges from '../../hooks/use-challenges'; +import useChallenges from '../../stores/use-challenges-store'; import styles from './challenge-modal.module.css'; import { getPublicationPreview, getPublicationType, getVotePreview } from '../../lib/utils/challenge-utils'; diff --git a/src/components/comment-edit-form/comment-edit-form.tsx b/src/components/comment-edit-form/comment-edit-form.tsx index 08763082..6b10cd13 100644 --- a/src/components/comment-edit-form/comment-edit-form.tsx +++ b/src/components/comment-edit-form/comment-edit-form.tsx @@ -4,7 +4,7 @@ import { PublishCommentEditOptions, useComment, useEditedComment, usePublishComm import { FormattingHelpTable } from '../reply-form'; import styles from '../reply-form/reply-form.module.css'; import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils'; -import challengesStore from '../../hooks/use-challenges'; +import challengesStore from '../../stores/use-challenges-store'; const { addChallenge } = challengesStore.getState(); diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index f0dabc5b..8fdf99c6 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -37,9 +37,9 @@ import { isProfileUpvotedView, isSettingsPlebbitOptionsView, } from '../../lib/utils/view-utils'; +import useNotFoundStore from '../../stores/use-not-found-store'; import useTheme from '../../hooks/use-theme'; import useWindowWidth from '../../hooks/use-window-width'; -import { useNotFoundStore } from '../../views/not-found'; import styles from './header.module.css'; import SubscribeButton from '../subscribe-button'; diff --git a/src/components/post/comment-tools/edit-menu/edit-menu.tsx b/src/components/post/comment-tools/edit-menu/edit-menu.tsx index 6539db6b..ec4e4cff 100644 --- a/src/components/post/comment-tools/edit-menu/edit-menu.tsx +++ b/src/components/post/comment-tools/edit-menu/edit-menu.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@plebbit/plebbit-react-hooks'; import styles from './edit-menu.module.css'; import { alertChallengeVerificationFailed } from '../../../../lib/utils/challenge-utils'; -import challengesStore from '../../../../hooks/use-challenges'; +import challengesStore from '../../../../stores/use-challenges-store'; const { addChallenge } = challengesStore.getState(); diff --git a/src/components/post/comment-tools/mod-menu/mod-menu.tsx b/src/components/post/comment-tools/mod-menu/mod-menu.tsx index 36992124..552b6e91 100644 --- a/src/components/post/comment-tools/mod-menu/mod-menu.tsx +++ b/src/components/post/comment-tools/mod-menu/mod-menu.tsx @@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@plebbit/plebbit-react-hooks'; import styles from './mod-menu.module.css'; import { alertChallengeVerificationFailed } from '../../../../lib/utils/challenge-utils'; -import challengesStore from '../../../../hooks/use-challenges'; +import challengesStore from '../../../../stores/use-challenges-store'; const { addChallenge } = challengesStore.getState(); diff --git a/src/hooks/use-downvote.ts b/src/hooks/use-downvote.ts index 03629223..8deed05b 100644 --- a/src/hooks/use-downvote.ts +++ b/src/hooks/use-downvote.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { ChallengeVerification, Comment, usePublishVote, useAccountVote } from '@plebbit/plebbit-react-hooks'; import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils'; -import useChallengesStore from './use-challenges'; +import useChallengesStore from '../stores/use-challenges-store'; const useDownvote = (comment: Comment): [boolean, () => void] => { const { addChallenge } = useChallengesStore(); diff --git a/src/hooks/use-reply.ts b/src/hooks/use-reply.ts index 7d8d52f4..c1c65aae 100644 --- a/src/hooks/use-reply.ts +++ b/src/hooks/use-reply.ts @@ -1,67 +1,6 @@ import { useMemo } from 'react'; -import { ChallengeVerification, Comment, PublishCommentOptions, usePublishComment } from '@plebbit/plebbit-react-hooks'; -import { create } from 'zustand'; -import useChallengesStore from './use-challenges'; -import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils'; - -type SetReplyStoreData = { - subplebbitAddress: string; - parentCid: string; - content: string | undefined; - link: string | undefined; - spoiler: boolean; -}; - -type ReplyState = { - content: { [parentCid: string]: string | undefined }; - link: { [parentCid: string]: string | undefined }; - spoiler: { [parentCid: string]: boolean | undefined }; - publishCommentOptions: PublishCommentOptions; - setReplyStore: (data: SetReplyStoreData) => void; - resetReplyStore: (parentCid: string) => void; -}; - -const { addChallenge } = useChallengesStore.getState(); - -const useReplyStore = create((set) => ({ - content: {}, - link: {}, - spoiler: {}, - publishCommentOptions: {}, - setReplyStore: (data: SetReplyStoreData) => - set((state) => { - const { subplebbitAddress, parentCid, content, link, spoiler } = data; - const publishCommentOptions = { - subplebbitAddress, - parentCid, - content, - link, - spoiler, - onChallenge: (...args: any) => addChallenge(args), - onChallengeVerification: (challengeVerification: ChallengeVerification, comment: Comment) => { - alertChallengeVerificationFailed(challengeVerification, comment); - }, - onError: (error: Error) => { - console.error(error); - alert(error.message); - }, - }; - return { - content: { ...state.content, [parentCid]: content }, - link: { ...state.link, [parentCid]: link }, - spoiler: { ...state.spoiler, [parentCid]: spoiler }, - publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: publishCommentOptions }, - }; - }), - - resetReplyStore: (parentCid) => - set((state) => ({ - content: { ...state.content, [parentCid]: undefined }, - link: { ...state.link, [parentCid]: undefined }, - spoiler: { ...state.spoiler, [parentCid]: undefined }, - publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: undefined }, - })), -})); +import { usePublishComment } from '@plebbit/plebbit-react-hooks'; +import useReplyStore from '../stores/use-reply-store'; const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress: string }) => { const parentCid = cid; diff --git a/src/hooks/use-theme.ts b/src/hooks/use-theme.ts index a17bfdce..afe3047e 100644 --- a/src/hooks/use-theme.ts +++ b/src/hooks/use-theme.ts @@ -1,17 +1,4 @@ -import { create, StoreApi } from 'zustand'; - -interface ThemeState { - theme: string; - setTheme: (theme: string) => void; -} - -const useThemeStore = create((set: StoreApi['setState']) => ({ - theme: localStorage.getItem('theme') || 'light', - setTheme: (theme: string) => { - localStorage.setItem('theme', theme); - set({ theme }); - }, -})); +import useThemeStore from '../stores/use-theme-store'; const useTheme = (): [string, (theme: string) => void] => { const { theme, setTheme } = useThemeStore(); diff --git a/src/hooks/use-upvote.ts b/src/hooks/use-upvote.ts index b5abaed9..d90c65d9 100644 --- a/src/hooks/use-upvote.ts +++ b/src/hooks/use-upvote.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { ChallengeVerification, Comment, usePublishVote, useAccountVote } from '@plebbit/plebbit-react-hooks'; -import useChallengesStore from './use-challenges'; +import useChallengesStore from '../stores/use-challenges-store'; import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils'; const useUpvote = (comment: Comment): [boolean, () => void] => { diff --git a/src/hooks/use-challenges.ts b/src/stores/use-challenges-store.ts similarity index 100% rename from src/hooks/use-challenges.ts rename to src/stores/use-challenges-store.ts diff --git a/src/stores/use-not-found-store.ts b/src/stores/use-not-found-store.ts new file mode 100644 index 00000000..2aab1f19 --- /dev/null +++ b/src/stores/use-not-found-store.ts @@ -0,0 +1,13 @@ +import { create } from 'zustand'; + +interface NotFoundState { + isNotFound: boolean; + setNotFound: (isNotFound: boolean) => void; +} + +export const useNotFoundStore = create((set) => ({ + isNotFound: false, + setNotFound: (isNotFound: boolean) => set({ isNotFound }), +})); + +export default useNotFoundStore; diff --git a/src/stores/use-reply-store.ts b/src/stores/use-reply-store.ts new file mode 100644 index 00000000..c84ef443 --- /dev/null +++ b/src/stores/use-reply-store.ts @@ -0,0 +1,66 @@ +import { PublishCommentOptions } from '@plebbit/plebbit-react-hooks'; +import { ChallengeVerification, Comment } from '@plebbit/plebbit-react-hooks'; +import { create } from 'zustand'; +import useChallengesStore from './use-challenges-store'; +import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils'; + +type SetReplyStoreData = { + subplebbitAddress: string; + parentCid: string; + content: string | undefined; + link: string | undefined; + spoiler: boolean; +}; + +type ReplyState = { + content: { [parentCid: string]: string | undefined }; + link: { [parentCid: string]: string | undefined }; + spoiler: { [parentCid: string]: boolean | undefined }; + publishCommentOptions: PublishCommentOptions; + setReplyStore: (data: SetReplyStoreData) => void; + resetReplyStore: (parentCid: string) => void; +}; + +const { addChallenge } = useChallengesStore.getState(); + +const useReplyStore = create((set) => ({ + content: {}, + link: {}, + spoiler: {}, + publishCommentOptions: {}, + setReplyStore: (data: SetReplyStoreData) => + set((state) => { + const { subplebbitAddress, parentCid, content, link, spoiler } = data; + const publishCommentOptions = { + subplebbitAddress, + parentCid, + content, + link, + spoiler, + onChallenge: (...args: any) => addChallenge(args), + onChallengeVerification: (challengeVerification: ChallengeVerification, comment: Comment) => { + alertChallengeVerificationFailed(challengeVerification, comment); + }, + onError: (error: Error) => { + console.error(error); + alert(error.message); + }, + }; + return { + content: { ...state.content, [parentCid]: content }, + link: { ...state.link, [parentCid]: link }, + spoiler: { ...state.spoiler, [parentCid]: spoiler }, + publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: publishCommentOptions }, + }; + }), + + resetReplyStore: (parentCid) => + set((state) => ({ + content: { ...state.content, [parentCid]: undefined }, + link: { ...state.link, [parentCid]: undefined }, + spoiler: { ...state.spoiler, [parentCid]: undefined }, + publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: undefined }, + })), +})); + +export default useReplyStore; diff --git a/src/stores/use-submit-store.ts b/src/stores/use-submit-store.ts new file mode 100644 index 00000000..4b2cf73c --- /dev/null +++ b/src/stores/use-submit-store.ts @@ -0,0 +1,50 @@ +import { create } from 'zustand'; +import challengesStore from './use-challenges-store'; +import { PublishCommentOptions } from '@plebbit/plebbit-react-hooks'; +import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils'; + +type SubmitState = { + subplebbitAddress: string | undefined; + title: string | undefined; + content: string | undefined; + link: string | undefined; + publishCommentOptions: PublishCommentOptions; + spoiler: boolean | undefined; + setSubmitStore: (data: Partial) => void; + resetSubmitStore: () => void; +}; + +const { addChallenge } = challengesStore.getState(); + +const useSubmitStore = create((set) => ({ + subplebbitAddress: undefined, + title: undefined, + content: undefined, + link: undefined, + spoiler: undefined, + publishCommentOptions: {}, + setSubmitStore: ({ subplebbitAddress, title, content, link, spoiler }) => + set((state) => { + const nextState = { ...state }; + if (subplebbitAddress !== undefined) nextState.subplebbitAddress = subplebbitAddress; + if (title !== undefined) nextState.title = title || undefined; + if (content !== undefined) nextState.content = content || undefined; + if (link !== undefined) nextState.link = link || undefined; + if (spoiler !== undefined) nextState.spoiler = spoiler || undefined; + + nextState.publishCommentOptions = { + ...nextState, + onChallenge: (...args: any) => addChallenge(args), + onChallengeVerification: alertChallengeVerificationFailed, + onError: (error: Error) => { + console.error(error); + let errorMessage = error.message; + alert(errorMessage); + }, + }; + return nextState; + }), + resetSubmitStore: () => set({ subplebbitAddress: undefined, title: undefined, content: undefined, link: undefined, spoiler: undefined, publishCommentOptions: {} }), +})); + +export default useSubmitStore; diff --git a/src/stores/use-subplebbit-settings-store.ts b/src/stores/use-subplebbit-settings-store.ts new file mode 100644 index 00000000..593c27a2 --- /dev/null +++ b/src/stores/use-subplebbit-settings-store.ts @@ -0,0 +1,68 @@ +import { PublishSubplebbitEditOptions } from '@plebbit/plebbit-react-hooks'; +import { Roles } from '../lib/utils/user-utils'; +import { create } from 'zustand'; + +export type SubplebbitSettingsState = { + challenges: any[] | undefined; + title: string | undefined; + description: string | undefined; + address: string | undefined; + suggested: any | undefined; + rules: string[] | undefined; + roles: Roles | undefined; + settings: any | undefined; + subplebbitAddress: string | undefined; + publishSubplebbitEditOptions: PublishSubplebbitEditOptions; + setSubplebbitSettingsStore: (data: Partial) => void; + resetSubplebbitSettingsStore: () => void; +}; + +const useSubplebbitSettingsStore = create((set) => ({ + challenges: undefined, + title: undefined, + description: undefined, + address: undefined, + suggested: undefined, + rules: undefined, + roles: undefined, + settings: undefined, + subplebbitAddress: undefined, + publishSubplebbitEditOptions: {}, + setSubplebbitSettingsStore: (props) => + set((state) => { + const nextState = { ...state }; + Object.entries(props).forEach(([key, value]) => { + if (value !== undefined) { + (nextState as any)[key] = value; + } + }); + const editOptions: Partial = {}; + if (nextState.title !== undefined) editOptions.title = nextState.title; + if (nextState.description !== undefined) editOptions.description = nextState.description; + if (nextState.address !== undefined) editOptions.address = nextState.address; + if (nextState.suggested !== undefined) editOptions.suggested = nextState.suggested; + if (nextState.rules !== undefined) editOptions.rules = nextState.rules; + if (nextState.roles !== undefined) editOptions.roles = nextState.roles; + if (nextState.settings !== undefined) editOptions.settings = nextState.settings; + if (nextState.subplebbitAddress !== undefined) editOptions.subplebbitAddress = nextState.subplebbitAddress; + nextState.publishSubplebbitEditOptions = editOptions; + return nextState; + }), + resetSubplebbitSettingsStore: () => + set(() => { + return { + challenges: undefined, + title: undefined, + description: undefined, + address: undefined, + suggested: undefined, + rules: undefined, + roles: undefined, + settings: undefined, + subplebbitAddress: undefined, + publishSubplebbitEditOptions: {}, + }; + }), +})); + +export default useSubplebbitSettingsStore; diff --git a/src/stores/use-theme-store.ts b/src/stores/use-theme-store.ts new file mode 100644 index 00000000..16df77a1 --- /dev/null +++ b/src/stores/use-theme-store.ts @@ -0,0 +1,16 @@ +import { create, StoreApi } from 'zustand'; + +interface ThemeState { + theme: string; + setTheme: (theme: string) => void; +} + +const useThemeStore = create((set: StoreApi['setState']) => ({ + theme: localStorage.getItem('theme') || 'light', + setTheme: (theme: string) => { + localStorage.setItem('theme', theme); + set({ theme }); + }, +})); + +export default useThemeStore; diff --git a/src/views/not-found/index.ts b/src/views/not-found/index.ts index 2fce79cd..9bcb7ca7 100644 --- a/src/views/not-found/index.ts +++ b/src/views/not-found/index.ts @@ -1 +1 @@ -export { default, useNotFoundStore } from './not-found'; +export { default } from './not-found'; diff --git a/src/views/not-found/not-found.tsx b/src/views/not-found/not-found.tsx index 1e07e89e..6f92201a 100644 --- a/src/views/not-found/not-found.tsx +++ b/src/views/not-found/not-found.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { create } from 'zustand'; +import useNotFoundStore from '../../stores/use-not-found-store'; import styles from './not-found.module.css'; const totalNotFoundImages = 2; @@ -14,16 +14,6 @@ const NotFoundImage = () => { return ; }; -interface NotFoundState { - isNotFound: boolean; - setNotFound: (isNotFound: boolean) => void; -} - -export const useNotFoundStore = create((set) => ({ - isNotFound: false, - setNotFound: (isNotFound: boolean) => set({ isNotFound }), -})); - const NotFound = () => { const { t } = useTranslation(); const setNotFound = useNotFoundStore((state) => state.setNotFound); diff --git a/src/views/submit-page/submit-page.module.css b/src/views/submit-page/submit-page.module.css index e5f5d1c9..f5fc0737 100644 --- a/src/views/submit-page/submit-page.module.css +++ b/src/views/submit-page/submit-page.module.css @@ -3,6 +3,19 @@ color: var(--text); } +.infobar { + box-sizing: border-box; + background-color: var(--background-orange); + border-color: var(--border-orange); + border-style: solid; + border-width: 1px; + margin: 0px 5px 5px 0px; + padding: 6px 10px 6px 10px; + color: var(--text); + word-wrap: break-word; + font-size: 14px; +} + h1 { font-size: 18px; font-weight: normal; diff --git a/src/views/submit-page/submit-page.tsx b/src/views/submit-page/submit-page.tsx index aac349f6..a7043e6f 100644 --- a/src/views/submit-page/submit-page.tsx +++ b/src/views/submit-page/submit-page.tsx @@ -1,62 +1,16 @@ import { ChangeEvent, useCallback, useEffect, useState } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; -import { PublishCommentOptions, useAccount, usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; -import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import { Trans, useTranslation } from 'react-i18next'; -import { create } from 'zustand'; +import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; +import { useAccount, usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; +import useSubmitStore from '../../stores/use-submit-store'; import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; -import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils'; import { getLinkMediaInfo } from '../../lib/utils/media-utils'; import { isValidURL } from '../../lib/utils/url-utils'; import { isSubmitView } from '../../lib/utils/view-utils'; -import styles from './submit-page.module.css'; -import challengesStore from '../../hooks/use-challenges'; import Embed from '../../components/post/embed'; import Markdown from '../../components/markdown'; - -type SubmitState = { - subplebbitAddress: string | undefined; - title: string | undefined; - content: string | undefined; - link: string | undefined; - publishCommentOptions: PublishCommentOptions; - spoiler: boolean | undefined; - setSubmitStore: (data: Partial) => void; - resetSubmitStore: () => void; -}; - -const { addChallenge } = challengesStore.getState(); - -const useSubmitStore = create((set) => ({ - subplebbitAddress: undefined, - title: undefined, - content: undefined, - link: undefined, - spoiler: undefined, - publishCommentOptions: {}, - setSubmitStore: ({ subplebbitAddress, title, content, link, spoiler }) => - set((state) => { - const nextState = { ...state }; - if (subplebbitAddress !== undefined) nextState.subplebbitAddress = subplebbitAddress; - if (title !== undefined) nextState.title = title || undefined; - if (content !== undefined) nextState.content = content || undefined; - if (link !== undefined) nextState.link = link || undefined; - if (spoiler !== undefined) nextState.spoiler = spoiler || undefined; - - nextState.publishCommentOptions = { - ...nextState, - onChallenge: (...args: any) => addChallenge(args), - onChallengeVerification: alertChallengeVerificationFailed, - onError: (error: Error) => { - console.error(error); - let errorMessage = error.message; - alert(errorMessage); - }, - }; - return nextState; - }), - resetSubmitStore: () => set({ subplebbitAddress: undefined, title: undefined, content: undefined, link: undefined, spoiler: undefined, publishCommentOptions: {} }), -})); +import styles from './submit-page.module.css'; const UrlField = () => { const { t } = useTranslation(); @@ -133,6 +87,24 @@ const Submit = () => { const { subscriptions } = account || {}; const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); + const [isOffline, setIsOffline] = useState(false); + + useEffect(() => { + const checkOfflineStatus = () => { + if (subplebbit?.updatedAt !== undefined) { + setIsOffline(subplebbit.updatedAt < Date.now() / 1000 - 60 * 60); + } else { + setTimeout(() => { + setIsOffline(subplebbit?.updatedAt === undefined || subplebbit.updatedAt < Date.now() / 1000 - 60 * 60); + }, 5000); + } + }; + + if (subplebbitAddress) { + checkOfflineStatus(); + } + }, [subplebbit?.updatedAt, subplebbitAddress]); + const onPublish = () => { if (!title) { alert(`Missing title`); @@ -260,6 +232,7 @@ const Submit = () => { return (
+ {isOffline &&
test
}

) => void; - resetSubplebbitSettingsStore: () => void; -}; - -const useSubplebbitSettingsStore = create((set) => ({ - challenges: undefined, - title: undefined, - description: undefined, - address: undefined, - suggested: undefined, - rules: undefined, - roles: undefined, - settings: undefined, - subplebbitAddress: undefined, - publishSubplebbitEditOptions: {}, - setSubplebbitSettingsStore: (props) => - set((state) => { - const nextState = { ...state }; - Object.entries(props).forEach(([key, value]) => { - if (value !== undefined) { - (nextState as any)[key] = value; - } - }); - const editOptions: Partial = {}; - if (nextState.title !== undefined) editOptions.title = nextState.title; - if (nextState.description !== undefined) editOptions.description = nextState.description; - if (nextState.address !== undefined) editOptions.address = nextState.address; - if (nextState.suggested !== undefined) editOptions.suggested = nextState.suggested; - if (nextState.rules !== undefined) editOptions.rules = nextState.rules; - if (nextState.roles !== undefined) editOptions.roles = nextState.roles; - if (nextState.settings !== undefined) editOptions.settings = nextState.settings; - if (nextState.subplebbitAddress !== undefined) editOptions.subplebbitAddress = nextState.subplebbitAddress; - nextState.publishSubplebbitEditOptions = editOptions; - return nextState; - }), - resetSubplebbitSettingsStore: () => - set(() => { - return { - challenges: undefined, - title: undefined, - description: undefined, - address: undefined, - suggested: undefined, - rules: undefined, - roles: undefined, - settings: undefined, - subplebbitAddress: undefined, - publishSubplebbitEditOptions: {}, - }; - }), -})); - const Title = ({ isReadOnly = false }: { isReadOnly?: boolean }) => { const { t } = useTranslation(); const { title, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();