diff --git a/src/components/ConfirmModal/ConfirmModal.module.scss b/src/components/ConfirmModal/ConfirmModal.module.scss new file mode 100644 index 00000000..e1d711c7 --- /dev/null +++ b/src/components/ConfirmModal/ConfirmModal.module.scss @@ -0,0 +1,68 @@ + + +.feedsRestoreModal { + position: fixed; + width: 420px; + color: var(--text-secondary); + background-color: var(--background-site); + background: linear-gradient(var(--background-site), + var(--background-site)) padding-box, + var(--brand-gradient) border-box; + border: 1px solid transparent; + border-radius: 6px; + + display: flex; + flex-direction: column; + padding: 22px; + + .feedConfirmationTitle { + font-weight: 800; + font-size: 18px; + line-height: 18px; + color: var(--text-secondary); + text-transform: uppercase; + margin-bottom: 20px; + } + + .feedConfirmationDescription { + color: var(--text-secondary); + font-size: 16px; + font-weight: 400; + line-height: 20px; + margin-bottom: 20px; + } + + .feedConfirmationActions { + display: flex; + justify-content: space-between; + + .feedRestoreConfirm { + background: var(--brand-gradient-vertical); + border: none; + border-radius: 6px; + font-size: 14px; + line-height: 20px; + font-weight: 700; + padding: 8px; + margin: 0px; + max-width: 40%; + } + + .feedRestoreAbort { + font-size: 14px; + line-height: 20px; + font-weight: 700; + color: var(--text-secondary); + background-color: var(--background-site); + background: linear-gradient(var(--background-site), + var(--background-site)) padding-box, + var(--brand-gradient) border-box; + border: 1px solid transparent; + border-radius: 6px; + padding: 8px; + margin: 0; + max-width: 40%; + } + } + +} diff --git a/src/components/ConfirmModal/ConfirmModal.tsx b/src/components/ConfirmModal/ConfirmModal.tsx new file mode 100644 index 00000000..3fddd94b --- /dev/null +++ b/src/components/ConfirmModal/ConfirmModal.tsx @@ -0,0 +1,59 @@ +import { useIntl } from '@cookbook/solid-intl'; +import { Component, createEffect, createSignal, For } from 'solid-js'; +import { useAccountContext } from '../../contexts/AccountContext'; +import { useSettingsContext } from '../../contexts/SettingsContext'; +import { zapNote } from '../../lib/zap'; +import { userName } from '../../stores/profile'; +import { toastZapFail, zapCustomOption } from '../../translations'; +import { PrimalNote } from '../../types/primal'; +import { debounce } from '../../utils'; +import Modal from '../Modal/Modal'; +import { useToastContext } from '../Toaster/Toaster'; + +import { confirmDefaults as t } from '../../translations'; + +import styles from './ConfirmModal.module.scss'; + +const ConfirmModal: Component<{ + open?: boolean, + title?: string, + description?: string, + confirmLabel?: string, + abortLablel?: string + onConfirm: () => void, + onAbort: () => void, +}> = (props) => { + + const intl = useIntl(); + + return ( + +
+
+ {props.title || intl.formatMessage(t.title)} +
+
+ {props.description} +
+
+ + + +
+
+ +
+ ); +} + +export default ConfirmModal; diff --git a/src/contexts/SettingsContext.tsx b/src/contexts/SettingsContext.tsx index 3ac3c3da..adca06f6 100644 --- a/src/contexts/SettingsContext.tsx +++ b/src/contexts/SettingsContext.tsx @@ -33,6 +33,7 @@ import { getDefaultSettings, getSettings, sendSettings } from "../lib/settings"; import { APP_ID } from "../App"; import { useIntl } from "@cookbook/solid-intl"; import { hexToNpub } from "../lib/keys"; +import { settings as t } from "../translations"; export type SettingsContextStore = { locale: string, @@ -55,6 +56,7 @@ export type SettingsContextStore = { setDefaultZapAmount: (amount: number) => void, setZapOptions: (amount:number, index: number) => void, updateNotificationSettings: (key: string, value: boolean, temp?: boolean) => void, + restoreDefaultFeeds: () => void, } } @@ -175,6 +177,54 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { !temp && saveSettings(); }; + const restoreDefaultFeeds = () => { + + const subid = `restore_default_${APP_ID}`; + + const unsub = subscribeTo(subid, async (type, subId, content) => { + + if (type === 'EVENT' && content?.content) { + try { + const settings = JSON.parse(content?.content); + + let feeds = settings.feeds as PrimalFeed[]; + + if (account?.hasPublicKey()) { + feeds.unshift({ + name: feedLabel, + hex: account?.publicKey, + npub: hexToNpub(account?.publicKey), + }); + } + + updateStore('availableFeeds', + () => replaceAvailableFeeds(account?.publicKey, feeds), + ); + + updateStore('defaultFeed', () => store.availableFeeds[0]); + + saveSettings(); + } + catch (e) { + console.log('Error parsing settings response: ', e); + } + } + + if (type === 'NOTICE') { + toaster?.sendWarning(intl.formatMessage({ + id: 'settings.loadFail', + defaultMessage: 'Failed to load settings. Will be using local settings.', + description: 'Toast message after settings have failed to be loaded from the server', + })); + } + + unsub(); + return; + }); + + getDefaultSettings(subid) + }; + const saveSettings = () => { const settings = { theme: store.theme, @@ -215,14 +265,6 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { const feeds = settings.feeds as PrimalFeed[]; const notificationSettings = settings.notifications as Record; - // const availableTopics = store.availableFeeds.map(f => f.hex); - - // const updatedFeeds = feeds.reduce((acc, feed) => { - // return availableTopics.includes(feed.hex) ? - // acc : - // [ ...acc, feed ]; - // }, store.availableFeeds) - updateStore('availableFeeds', () => replaceAvailableFeeds(account?.publicKey, feeds), ); @@ -342,11 +384,7 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { // This is here as to not trigger the effect // TODO Solve this. - const feedLabel = intl.formatMessage({ - id: 'feeds.latestFollowing', - defaultMessage: 'Latest, following', - description: 'Label for the `latest;following` (active user\'s) feed', - }); + const feedLabel = intl.formatMessage(t.feedLatest); // Initial setup for a user with a public key @@ -426,6 +464,7 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { renameAvailableFeed, saveSettings, loadSettings, + restoreDefaultFeeds, setDefaultZapAmount, setZapOptions, updateNotificationSettings, diff --git a/src/pages/Settings.module.scss b/src/pages/Settings.module.scss index 567efbc2..22737624 100644 --- a/src/pages/Settings.module.scss +++ b/src/pages/Settings.module.scss @@ -35,6 +35,33 @@ margin-bottom: 20px; } +.feedCaption { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: -10px; + .settingsCaption { + margin-bottom: 0px; + } + + .restoreFeedsButton { + background-color: var(--background-site); + color: var(--text-tertiary); + width: auto; + font-size: 14px; + font-weight: 400; + line-height: 20px; + border: none; + margin: 0; + padding-block: 10px; + padding-inline: 10px; + + &:hover { + color: var(--text-secondary); + } + } +} + .devider { width: 100%; border-bottom: solid 1px var(--subtile-devider); diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 6db67bc3..df99e963 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -1,4 +1,4 @@ -import { Component } from 'solid-js'; +import { Component, createSignal, Show } from 'solid-js'; import Branding from '../components/Branding/Branding'; import styles from './Settings.module.scss'; @@ -10,10 +10,21 @@ import SettingsZap from '../components/SettingsZap/SettingsZap'; import Search from '../components/Search/Search'; import SettingsNotifications from '../components/SettingsNotifications/SettingsNotifications'; import { settings as t } from '../translations'; +import { useSettingsContext } from '../contexts/SettingsContext'; +import Modal from '../components/Modal/Modal'; +import ConfirmModal from '../components/ConfirmModal/ConfirmModal'; const Settings: Component = () => { const intl = useIntl(); + const settings = useSettingsContext(); + + const [isRestoringFeeds, setIsRestoringFeeds] = createSignal(false); + + const onRestoreFeeds = () => { + settings?.actions.restoreDefaultFeeds(); + setIsRestoringFeeds(false); + }; return (
@@ -40,10 +51,27 @@ const Settings: Component = () => {
-
- {intl.formatMessage(t.feeds)} +
+
+ {intl.formatMessage(t.feeds)} +
+ + + + setIsRestoringFeeds(false)} + >
+
diff --git a/src/translations.ts b/src/translations.ts index 307affb3..01977b1b 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -79,6 +79,24 @@ export const branding = { description: 'Brand name', }; +export const confirmDefaults = { + title: { + id: 'confirm.title', + defaultMessage: 'Are you sure?', + description: 'Default title of the confirmation dialog', + }, + confirm: { + id: 'confirm.yes', + defaultMessage: 'Yes', + description: 'Default label form positive response to the confirmation dialog', + }, + abort: { + id: 'confirm.no', + defaultMessage: 'No', + description: 'Default label form negative response to the confirmation dialog', + }, +}; + export const exploreSidebarCaption = { id: 'explore.sidebar.caption', defaultMessage: 'trending users', @@ -576,6 +594,21 @@ export const settings = { defaultMessage: 'Home page feeds', description: 'Title of the feeds section on the settings page', }, + feedsRestore: { + id: 'settings.feedsRestore', + defaultMessage: 'Reset to default feeds', + description: 'Label for the button for restoring default feeds to the feeds list', + }, + feedsRestoreConfirm: { + id: 'settings.feedsRestoreConfirm', + defaultMessage: 'Restoring default feeds will erase all your custom feed settings', + description: 'Label explaining the impact of restoring default feeds', + }, + feedLatest: { + id: 'feeds.latestFollowing', + defaultMessage: 'Latest', + description: 'Label for the `latest;following` (active user\'s) feed', + }, zaps: { id: 'settings.sections.zaps', defaultMessage: 'Zaps',