mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-01 17:31:13 +00:00
Add restore feeds button
This commit is contained in:
parent
c9bc34433b
commit
ed567bb9f7
68
src/components/ConfirmModal/ConfirmModal.module.scss
Normal file
68
src/components/ConfirmModal/ConfirmModal.module.scss
Normal 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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/components/ConfirmModal/ConfirmModal.tsx
Normal file
59
src/components/ConfirmModal/ConfirmModal.tsx
Normal 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;
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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',
|
||||||
|
Loading…
Reference in New Issue
Block a user