Add restore feeds button

This commit is contained in:
Bojan Mojsilovic 2023-07-10 13:57:39 +02:00
parent c9bc34433b
commit ed567bb9f7
6 changed files with 270 additions and 16 deletions

View File

@ -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%;
}
}
}

View File

@ -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 (
<Modal open={props.open}>
<div class={styles.feedsRestoreModal}>
<div class={styles.feedConfirmationTitle}>
{props.title || intl.formatMessage(t.title)}
</div>
<div class={styles.feedConfirmationDescription}>
{props.description}
</div>
<div class={styles.feedConfirmationActions}>
<button
class={styles.feedRestoreConfirm}
onClick={props.onConfirm}
>
{props.confirmLabel || intl.formatMessage(t.confirm)}
</button>
<button
class={styles.feedRestoreAbort}
onClick={props.onAbort}
>
{props.abortLablel || intl.formatMessage(t.abort)}
</button>
</div>
</div>
</Modal>
);
}
export default ConfirmModal;

View File

@ -33,6 +33,7 @@ import { getDefaultSettings, getSettings, sendSettings } from "../lib/settings";
import { APP_ID } from "../App"; import { APP_ID } from "../App";
import { useIntl } from "@cookbook/solid-intl"; import { useIntl } from "@cookbook/solid-intl";
import { hexToNpub } from "../lib/keys"; import { hexToNpub } from "../lib/keys";
import { settings as t } from "../translations";
export type SettingsContextStore = { export type SettingsContextStore = {
locale: string, locale: string,
@ -55,6 +56,7 @@ export type SettingsContextStore = {
setDefaultZapAmount: (amount: number) => void, setDefaultZapAmount: (amount: number) => void,
setZapOptions: (amount:number, index: number) => void, setZapOptions: (amount:number, index: number) => void,
updateNotificationSettings: (key: string, value: boolean, temp?: boolean) => void, updateNotificationSettings: (key: string, value: boolean, temp?: boolean) => void,
restoreDefaultFeeds: () => void,
} }
} }
@ -175,6 +177,54 @@ export const SettingsProvider = (props: { children: ContextChildren }) => {
!temp && saveSettings(); !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 saveSettings = () => {
const settings = { const settings = {
theme: store.theme, theme: store.theme,
@ -215,14 +265,6 @@ export const SettingsProvider = (props: { children: ContextChildren }) => {
const feeds = settings.feeds as PrimalFeed[]; const feeds = settings.feeds as PrimalFeed[];
const notificationSettings = settings.notifications as Record<string, boolean>; const notificationSettings = settings.notifications as Record<string, boolean>;
// 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', updateStore('availableFeeds',
() => replaceAvailableFeeds(account?.publicKey, feeds), () => replaceAvailableFeeds(account?.publicKey, feeds),
); );
@ -342,11 +384,7 @@ export const SettingsProvider = (props: { children: ContextChildren }) => {
// This is here as to not trigger the effect // This is here as to not trigger the effect
// TODO Solve this. // TODO Solve this.
const feedLabel = intl.formatMessage({ const feedLabel = intl.formatMessage(t.feedLatest);
id: 'feeds.latestFollowing',
defaultMessage: 'Latest, following',
description: 'Label for the `latest;following` (active user\'s) feed',
});
// Initial setup for a user with a public key // Initial setup for a user with a public key
@ -426,6 +464,7 @@ export const SettingsProvider = (props: { children: ContextChildren }) => {
renameAvailableFeed, renameAvailableFeed,
saveSettings, saveSettings,
loadSettings, loadSettings,
restoreDefaultFeeds,
setDefaultZapAmount, setDefaultZapAmount,
setZapOptions, setZapOptions,
updateNotificationSettings, updateNotificationSettings,

View File

@ -35,6 +35,33 @@
margin-bottom: 20px; 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 { .devider {
width: 100%; width: 100%;
border-bottom: solid 1px var(--subtile-devider); border-bottom: solid 1px var(--subtile-devider);

View File

@ -1,4 +1,4 @@
import { Component } from 'solid-js'; import { Component, createSignal, Show } from 'solid-js';
import Branding from '../components/Branding/Branding'; import Branding from '../components/Branding/Branding';
import styles from './Settings.module.scss'; import styles from './Settings.module.scss';
@ -10,10 +10,21 @@ import SettingsZap from '../components/SettingsZap/SettingsZap';
import Search from '../components/Search/Search'; import Search from '../components/Search/Search';
import SettingsNotifications from '../components/SettingsNotifications/SettingsNotifications'; import SettingsNotifications from '../components/SettingsNotifications/SettingsNotifications';
import { settings as t } from '../translations'; 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 Settings: Component = () => {
const intl = useIntl(); const intl = useIntl();
const settings = useSettingsContext();
const [isRestoringFeeds, setIsRestoringFeeds] = createSignal(false);
const onRestoreFeeds = () => {
settings?.actions.restoreDefaultFeeds();
setIsRestoringFeeds(false);
};
return ( return (
<div class={styles.settingsContainer}> <div class={styles.settingsContainer}>
@ -40,10 +51,27 @@ const Settings: Component = () => {
<div class={styles.devider}></div> <div class={styles.devider}></div>
<div class={styles.settingsCaption}> <div class={styles.feedCaption}>
{intl.formatMessage(t.feeds)} <div class={styles.settingsCaption}>
{intl.formatMessage(t.feeds)}
</div>
<button
class={styles.restoreFeedsButton}
onClick={() => setIsRestoringFeeds(true)}
>
{intl.formatMessage(t.feedsRestore)}
</button>
<ConfirmModal
open={isRestoringFeeds()}
description={intl.formatMessage(t.feedsRestoreConfirm)}
onConfirm={onRestoreFeeds}
onAbort={() => setIsRestoringFeeds(false)}
></ConfirmModal>
</div> </div>
<div class={styles.feedSettings}> <div class={styles.feedSettings}>
<FeedSorter /> <FeedSorter />
</div> </div>

View File

@ -119,6 +119,24 @@ export const downloads = {
}, },
}; };
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 = { export const exploreSidebarCaption = {
id: 'explore.sidebar.caption', id: 'explore.sidebar.caption',
defaultMessage: 'trending users', defaultMessage: 'trending users',
@ -616,6 +634,21 @@ export const settings = {
defaultMessage: 'Home page feeds', defaultMessage: 'Home page feeds',
description: 'Title of the feeds section on the settings page', 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: { zaps: {
id: 'settings.sections.zaps', id: 'settings.sections.zaps',
defaultMessage: 'Zaps', defaultMessage: 'Zaps',