mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-30 00:41:09 +00:00
Add Account settings
This commit is contained in:
parent
feff1269c4
commit
f05149cf53
@ -32,6 +32,7 @@ const Mutelist = lazy(() => import('./pages/Mutelist'));
|
|||||||
const CreateAccount = lazy(() => import('./pages/CreateAccount'));
|
const CreateAccount = lazy(() => import('./pages/CreateAccount'));
|
||||||
|
|
||||||
const NotifSettings = lazy(() => import('./pages/Settings/Notifications'));
|
const NotifSettings = lazy(() => import('./pages/Settings/Notifications'));
|
||||||
|
const Account = lazy(() => import('./pages/Settings/Account'));
|
||||||
const Appearance = lazy(() => import('./pages/Settings/Appearance'));
|
const Appearance = lazy(() => import('./pages/Settings/Appearance'));
|
||||||
const HomeFeeds = lazy(() => import('./pages/Settings/HomeFeeds'));
|
const HomeFeeds = lazy(() => import('./pages/Settings/HomeFeeds'));
|
||||||
const ZapSettings = lazy(() => import('./pages/Settings/Zaps'));
|
const ZapSettings = lazy(() => import('./pages/Settings/Zaps'));
|
||||||
@ -104,6 +105,7 @@ const Router: Component = () => {
|
|||||||
<Route path="/download" element={<Navigate href='/downloads' />} />;
|
<Route path="/download" element={<Navigate href='/downloads' />} />;
|
||||||
<Route path="/settings" component={Settings}>
|
<Route path="/settings" component={Settings}>
|
||||||
<Route path="/" component={Menu} />
|
<Route path="/" component={Menu} />
|
||||||
|
<Route path="/account" component={Account} />
|
||||||
<Route path="/appearance" component={Appearance} />
|
<Route path="/appearance" component={Appearance} />
|
||||||
<Route path="/feeds" component={HomeFeeds} />
|
<Route path="/feeds" component={HomeFeeds} />
|
||||||
<Route path="/notifications" component={NotifSettings} />
|
<Route path="/notifications" component={NotifSettings} />
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin: 0px 8px;
|
margin: 0px 8px;
|
||||||
padding: 0px;
|
padding-inline: 10px;
|
||||||
|
padding-block: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -53,6 +53,7 @@ const NavMenu: Component< { id?: string } > = (props) => {
|
|||||||
label: intl.formatMessage(t.settings),
|
label: intl.formatMessage(t.settings),
|
||||||
icon: 'settingsIcon',
|
icon: 'settingsIcon',
|
||||||
hiddenOnSmallScreens: true,
|
hiddenOnSmallScreens: true,
|
||||||
|
bubble: () => account?.sec ? 1 : 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: '/help',
|
to: '/help',
|
||||||
|
@ -25,7 +25,7 @@ import { sendContacts, sendLike, sendMuteList, triggerImportEvents } from "../li
|
|||||||
import { generatePrivateKey, Relay, getPublicKey as nostrGetPubkey, nip19 } from "nostr-tools";
|
import { generatePrivateKey, Relay, getPublicKey as nostrGetPubkey, nip19 } from "nostr-tools";
|
||||||
import { APP_ID } from "../App";
|
import { APP_ID } from "../App";
|
||||||
import { getLikes, getFilterlists, getProfileContactList, getProfileMuteList, getUserProfiles, sendFilterlists, getAllowlist, sendAllowList } from "../lib/profile";
|
import { getLikes, getFilterlists, getProfileContactList, getProfileMuteList, getUserProfiles, sendFilterlists, getAllowlist, sendAllowList } from "../lib/profile";
|
||||||
import { getStorage, readSecFromStorage, saveFollowing, saveLikes, saveMuted, saveMuteList, saveRelaySettings, storeSec } from "../lib/localStore";
|
import { clearSec, getStorage, readSecFromStorage, saveFollowing, saveLikes, saveMuted, saveMuteList, saveRelaySettings, storeSec } from "../lib/localStore";
|
||||||
import { connectRelays, connectToRelay, getDefaultRelays, getPreConfiguredRelays } from "../lib/relays";
|
import { connectRelays, connectToRelay, getDefaultRelays, getPreConfiguredRelays } from "../lib/relays";
|
||||||
import { getPublicKey } from "../lib/nostrAPI";
|
import { getPublicKey } from "../lib/nostrAPI";
|
||||||
import { generateKeys } from "../lib/PrimalNostr";
|
import { generateKeys } from "../lib/PrimalNostr";
|
||||||
@ -79,6 +79,7 @@ export type AccountContextStore = {
|
|||||||
addToAllowlist: (pubkey: string | undefined, then?: () => void) => void,
|
addToAllowlist: (pubkey: string | undefined, then?: () => void) => void,
|
||||||
removeFromAllowlist: (pubkey: string | undefined) => void,
|
removeFromAllowlist: (pubkey: string | undefined) => void,
|
||||||
setSec: (sec: string | undefined) => void,
|
setSec: (sec: string | undefined) => void,
|
||||||
|
logout: () => void,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +121,12 @@ export function AccountProvider(props: { children: JSXElement }) {
|
|||||||
|
|
||||||
let connectedRelaysCopy: Relay[] = [];
|
let connectedRelaysCopy: Relay[] = [];
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
updateStore('sec', () => undefined);
|
||||||
|
updateStore('publicKey', () => undefined);
|
||||||
|
clearSec();
|
||||||
|
};
|
||||||
|
|
||||||
const setSec = (sec: string | undefined) => {
|
const setSec = (sec: string | undefined) => {
|
||||||
const decoded = nip19.decode(sec);
|
const decoded = nip19.decode(sec);
|
||||||
|
|
||||||
@ -1215,6 +1222,7 @@ const [store, updateStore] = createStore<AccountContextStore>({
|
|||||||
addToAllowlist,
|
addToAllowlist,
|
||||||
removeFromAllowlist,
|
removeFromAllowlist,
|
||||||
setSec,
|
setSec,
|
||||||
|
logout,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { createStore } from "solid-js/store";
|
import { createStore, reconcile } from "solid-js/store";
|
||||||
import { Kind } from "../constants";
|
import { Kind } from "../constants";
|
||||||
import {
|
import {
|
||||||
createContext,
|
createContext,
|
||||||
@ -31,7 +31,7 @@ import {
|
|||||||
UserRelation,
|
UserRelation,
|
||||||
} from "../types/primal";
|
} from "../types/primal";
|
||||||
import { APP_ID } from "../App";
|
import { APP_ID } from "../App";
|
||||||
import { getMessageCounts, getNewMessages, getOldMessages, markAllAsRead, resetMessageCount, subscribeToMessagesStats } from "../lib/messages";
|
import { getMessageCounts, getNewMessages, getOldMessages, markAllAsRead, resetMessageCount, subscribeToMessagesStats, unsubscribeToMessagesStats } from "../lib/messages";
|
||||||
import { useAccountContext } from "./AccountContext";
|
import { useAccountContext } from "./AccountContext";
|
||||||
import { convertToUser } from "../stores/profile";
|
import { convertToUser } from "../stores/profile";
|
||||||
import { getUserProfiles } from "../lib/profile";
|
import { getUserProfiles } from "../lib/profile";
|
||||||
@ -742,6 +742,14 @@ export const MessagesProvider = (props: { children: ContextChildren }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!account?.sec) {
|
||||||
|
unsubscribeToMessagesStats(subidMsgCount);
|
||||||
|
updateStore('messageCount', () => 0);
|
||||||
|
updateStore('messageCountPerSender', reconcile({}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
removeSocketListeners(
|
removeSocketListeners(
|
||||||
socket(),
|
socket(),
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
NostrEvent,
|
NostrEvent,
|
||||||
} from "../types/primal";
|
} from "../types/primal";
|
||||||
import { APP_ID } from "../App";
|
import { APP_ID } from "../App";
|
||||||
import { subscribeToNotificationStats } from "../lib/notifications";
|
import { subscribeToNotificationStats, unsubscribeToNotificationStats } from "../lib/notifications";
|
||||||
import { useAccountContext } from "./AccountContext";
|
import { useAccountContext } from "./AccountContext";
|
||||||
|
|
||||||
export type NotificationsContextStore = {
|
export type NotificationsContextStore = {
|
||||||
@ -124,7 +124,12 @@ export const NotificationsProvider = (props: { children: ContextChildren }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
createEffect(() => {});
|
createEffect(() => {
|
||||||
|
if (!account?.sec) {
|
||||||
|
unsubscribeToNotificationStats(subid);
|
||||||
|
updateStore('notificationCount', () => 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
removeSocketListeners(
|
removeSocketListeners(
|
||||||
|
@ -155,3 +155,7 @@ export const storeSec = (sec: string | undefined) => {
|
|||||||
localStorage.setItem('primalSec', sec);
|
localStorage.setItem('primalSec', sec);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const clearSec = () => {
|
||||||
|
localStorage.removeItem('primalSec');
|
||||||
|
};
|
||||||
|
@ -12,6 +12,14 @@ export const subscribeToMessagesStats = (pubkey: string, subid: string) => {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const unsubscribeToMessagesStats = (subid: string) => {
|
||||||
|
sendMessage(JSON.stringify([
|
||||||
|
"CLOSE",
|
||||||
|
subid,
|
||||||
|
{cache: ["directmsg_count"]},
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
export const resetMessageCount = async (sender: string, subid: string) => {
|
export const resetMessageCount = async (sender: string, subid: string) => {
|
||||||
const event = {
|
const event = {
|
||||||
content: `{ "description": "reset messages from '${sender}'"}`,
|
content: `{ "description": "reset messages from '${sender}'"}`,
|
||||||
|
@ -101,6 +101,14 @@ export const subscribeToNotificationStats = (pubkey: string, subid: string) => {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const unsubscribeToNotificationStats = (subid: string) => {
|
||||||
|
sendMessage(JSON.stringify([
|
||||||
|
"CLOSE",
|
||||||
|
subid,
|
||||||
|
{cache: ["notification_counts", { subid }]},
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
export const truncateNumber = (amount: number, from?: 1 | 2 | 3 | 4) => {
|
export const truncateNumber = (amount: number, from?: 1 | 2 | 3 | 4) => {
|
||||||
const t = 1_000;
|
const t = 1_000;
|
||||||
const s = from || 1;
|
const s = from || 1;
|
||||||
|
110
src/pages/Settings/Account.tsx
Normal file
110
src/pages/Settings/Account.tsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { Component, createSignal, Show } from 'solid-js';
|
||||||
|
import styles from './Settings.module.scss';
|
||||||
|
|
||||||
|
import ThemeChooser from '../../components/ThemeChooser/ThemeChooser';
|
||||||
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
|
import { settings as t, actions as tActions } from '../../translations';
|
||||||
|
import PageCaption from '../../components/PageCaption/PageCaption';
|
||||||
|
import { Link } from '@solidjs/router';
|
||||||
|
import PageTitle from '../../components/PageTitle/PageTitle';
|
||||||
|
import { useAccountContext } from '../../contexts/AccountContext';
|
||||||
|
import Avatar from '../../components/Avatar/Avatar';
|
||||||
|
|
||||||
|
const Account: Component = () => {
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
const account = useAccountContext();
|
||||||
|
|
||||||
|
const [isCoppied, setIsCoppied] = createSignal('');
|
||||||
|
|
||||||
|
const onCopy = (text: string) => {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
setIsCoppied(text);
|
||||||
|
|
||||||
|
setTimeout(() => setIsCoppied(''), 2_000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageTitle title={`${intl.formatMessage(t.account.title)} ${intl.formatMessage(t.title)}`} />
|
||||||
|
|
||||||
|
<PageCaption>
|
||||||
|
<Link href='/settings' >{intl.formatMessage(t.index.title)}</Link>:
|
||||||
|
<div>{intl.formatMessage(t.account.title)}</div>
|
||||||
|
</PageCaption>
|
||||||
|
|
||||||
|
<div class={styles.securityWarning}>
|
||||||
|
<div class={styles.securityIcon}></div>
|
||||||
|
<div class={styles.securityMessage}>
|
||||||
|
{intl.formatMessage(t.account.description, {
|
||||||
|
link: <a href="https://getalby.com" target='_blank'>Alby</a>,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.settingsAccountCaption}>
|
||||||
|
<div class={styles.caption}>
|
||||||
|
{intl.formatMessage(t.account.pubkey)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class={styles.copy}
|
||||||
|
onClick={() => onCopy(account?.activeUser?.npub || '')}
|
||||||
|
>
|
||||||
|
<Show when={isCoppied() === account?.activeUser?.npub}>
|
||||||
|
<div class={styles.checkIcon}></div>
|
||||||
|
</Show>
|
||||||
|
{intl.formatMessage(tActions.copyPubkey)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.settingsKeyArea}>
|
||||||
|
<Avatar
|
||||||
|
src={account?.activeUser?.picture}
|
||||||
|
size="vs"
|
||||||
|
/>
|
||||||
|
<div class={styles.key}>
|
||||||
|
{account?.activeUser?.npub}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.settingsDescription}>
|
||||||
|
{intl.formatMessage(t.account.pubkeyDesc)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class={styles.settingsAccountCaption}>
|
||||||
|
<div class={styles.caption}>
|
||||||
|
{intl.formatMessage(t.account.privkey)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class={styles.copy}
|
||||||
|
onClick={() => onCopy(account?.sec || '')}
|
||||||
|
>
|
||||||
|
<Show when={isCoppied() === account?.sec}>
|
||||||
|
<div class={styles.checkIcon}></div>
|
||||||
|
</Show>
|
||||||
|
{intl.formatMessage(tActions.copyPrivkey)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.settingsKeyArea}>
|
||||||
|
<div class={styles.icon}>
|
||||||
|
<div class={styles.keyIcon}></div>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
class={styles.key}
|
||||||
|
value={account?.sec || ''}
|
||||||
|
readOnly={true}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.settingsDescription}>
|
||||||
|
{intl.formatMessage(t.account.privkeyDesc)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Account;
|
@ -2,10 +2,11 @@ import { Component, Show } from 'solid-js';
|
|||||||
import styles from './Settings.module.scss';
|
import styles from './Settings.module.scss';
|
||||||
|
|
||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import { settings as t } from '../../translations';
|
import { settings as t, actions as tActions } from '../../translations';
|
||||||
import PageCaption from '../../components/PageCaption/PageCaption';
|
import PageCaption from '../../components/PageCaption/PageCaption';
|
||||||
import { Link } from '@solidjs/router';
|
import { Link } from '@solidjs/router';
|
||||||
import { useAccountContext } from '../../contexts/AccountContext';
|
import { useAccountContext } from '../../contexts/AccountContext';
|
||||||
|
import ButtonPrimary from '../../components/Buttons/ButtonPrimary';
|
||||||
|
|
||||||
const Menu: Component = () => {
|
const Menu: Component = () => {
|
||||||
|
|
||||||
@ -19,6 +20,18 @@ const Menu: Component = () => {
|
|||||||
<PageCaption title={intl.formatMessage(t.title)} />
|
<PageCaption title={intl.formatMessage(t.title)} />
|
||||||
|
|
||||||
<div class={styles.subpageLinks}>
|
<div class={styles.subpageLinks}>
|
||||||
|
<Show when={account?.sec != undefined}>
|
||||||
|
<Link href="/settings/account">
|
||||||
|
<div class={styles.caption}>
|
||||||
|
{intl.formatMessage(t.account.title)}
|
||||||
|
<div class={styles.bubble}>
|
||||||
|
<div>{1}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class={styles.chevron}></div>
|
||||||
|
</Link>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<Link href="/settings/appearance">
|
<Link href="/settings/appearance">
|
||||||
{intl.formatMessage(t.appearance.title)}
|
{intl.formatMessage(t.appearance.title)}
|
||||||
<div class={styles.chevron}></div>
|
<div class={styles.chevron}></div>
|
||||||
@ -57,6 +70,14 @@ const Menu: Component = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Show when={account?.sec}>
|
||||||
|
<div class={styles.webVersion}>
|
||||||
|
<ButtonPrimary onClick={account?.actions.logout}>
|
||||||
|
{intl.formatMessage(tActions.logout)}
|
||||||
|
</ButtonPrimary>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<div class={styles.webVersion}>
|
<div class={styles.webVersion}>
|
||||||
<div class={styles.title}>version</div>
|
<div class={styles.title}>version</div>
|
||||||
<div class={styles.value}>{version}</div>
|
<div class={styles.value}>{version}</div>
|
||||||
|
@ -50,6 +50,95 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settingsDescription {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 17px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingsAccountCaption {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 46px;
|
||||||
|
|
||||||
|
.caption {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy {
|
||||||
|
color: var(--accent-1);
|
||||||
|
background: none;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
width: fit-content;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkIcon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-inline: 2px;
|
||||||
|
background-color: var(--success-color);
|
||||||
|
-webkit-mask: url(../../assets/icons/check.svg) no-repeat 0 / 80%;
|
||||||
|
mask: url(../../assets/icons/check.svg) no-repeat 0 / 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingsKeyArea {
|
||||||
|
background-color: var(--background-input);
|
||||||
|
padding-inline: 12px;
|
||||||
|
padding-block: 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.key {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 12px;
|
||||||
|
border: none;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyIcon {
|
||||||
|
min-width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: block;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: var(--text-secondary);
|
||||||
|
-webkit-mask: url(../../assets/icons/key.svg) no-repeat 0 0 / 28px 28px;
|
||||||
|
mask: url(../../assets/icons/key.svg) no-repeat 0 0 / 28px 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.secondCaption {
|
.secondCaption {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
@ -108,7 +197,31 @@
|
|||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
>div {
|
.caption {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-inline: 4px;
|
||||||
|
margin-inline: 8px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
|
||||||
|
background: var(--brand-gradient);
|
||||||
|
border: 1px solid var(--background-site);
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
text-shadow: 0.5px 0.5px 0px black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chevron {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background-color: var(--text-tertiary);
|
background-color: var(--text-tertiary);
|
||||||
@ -117,7 +230,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
>div {
|
.chevron {
|
||||||
background-color: var(--text-primary);
|
background-color: var(--text-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -864,3 +977,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.securityWarning {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.securityMessage {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 17px;
|
||||||
|
margin-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.securityIcon {
|
||||||
|
min-width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: var(--text-secondary);
|
||||||
|
-webkit-mask: url(../../assets/icons/report.svg) no-repeat 0 0 / 54px 54px;
|
||||||
|
mask: url(../../assets/icons/report.svg) no-repeat 0 0 / 54px 54px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
import { Component } from 'solid-js';
|
import { Component } from 'solid-js';
|
||||||
import Branding from '../../components/Branding/Branding';
|
|
||||||
import styles from './Settings.module.scss';
|
import styles from './Settings.module.scss';
|
||||||
|
|
||||||
import Wormhole from '../../components/Wormhole/Wormhole';
|
import Wormhole from '../../components/Wormhole/Wormhole';
|
||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import Search from '../../components/Search/Search';
|
import Search from '../../components/Search/Search';
|
||||||
import { settings as t } from '../../translations';
|
import { settings as t } from '../../translations';
|
||||||
import { useSettingsContext } from '../../contexts/SettingsContext';
|
import { Outlet } from '@solidjs/router';
|
||||||
import PageCaption from '../../components/PageCaption/PageCaption';
|
|
||||||
import { Link, Outlet } from '@solidjs/router';
|
|
||||||
import HomeSidebar from '../../components/HomeSidebar/HomeSidebar';
|
|
||||||
import StickySidebar from '../../components/StickySidebar/StickySidebar';
|
import StickySidebar from '../../components/StickySidebar/StickySidebar';
|
||||||
import SettingsSidebar from '../../components/SettingsSidebar/SettingsSidebar';
|
import SettingsSidebar from '../../components/SettingsSidebar/SettingsSidebar';
|
||||||
import PageTitle from '../../components/PageTitle/PageTitle';
|
import PageTitle from '../../components/PageTitle/PageTitle';
|
||||||
|
@ -108,6 +108,11 @@ export const actions = {
|
|||||||
defaultMessage: 'Login now',
|
defaultMessage: 'Login now',
|
||||||
description: 'Login Now action, button label',
|
description: 'Login Now action, button label',
|
||||||
},
|
},
|
||||||
|
logout: {
|
||||||
|
id: 'actions.logout',
|
||||||
|
defaultMessage: 'Logout',
|
||||||
|
description: 'Logout action, button label',
|
||||||
|
},
|
||||||
getStarted: {
|
getStarted: {
|
||||||
id: 'actions.getStarted',
|
id: 'actions.getStarted',
|
||||||
defaultMessage: 'Get Started',
|
defaultMessage: 'Get Started',
|
||||||
@ -123,6 +128,16 @@ export const actions = {
|
|||||||
defaultMessage: 'copy',
|
defaultMessage: 'copy',
|
||||||
description: 'Copy action, button label',
|
description: 'Copy action, button label',
|
||||||
},
|
},
|
||||||
|
copyPubkey: {
|
||||||
|
id: 'actions.copyPubkey',
|
||||||
|
defaultMessage: 'copy public key',
|
||||||
|
description: 'Copy pubkey action, button label',
|
||||||
|
},
|
||||||
|
copyPrivkey: {
|
||||||
|
id: 'actions.copyPrivkey',
|
||||||
|
defaultMessage: 'copy private key',
|
||||||
|
description: 'Copy private key action, button label',
|
||||||
|
},
|
||||||
addFeedToHome: {
|
addFeedToHome: {
|
||||||
id: 'actions.addFeedToHome',
|
id: 'actions.addFeedToHome',
|
||||||
defaultMessage: 'add this feed to my home page',
|
defaultMessage: 'add this feed to my home page',
|
||||||
@ -1046,6 +1061,38 @@ export const settings = {
|
|||||||
description: 'Title of the settings page',
|
description: 'Title of the settings page',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
account: {
|
||||||
|
title: {
|
||||||
|
id: 'settings.account.title',
|
||||||
|
defaultMessage: 'Account',
|
||||||
|
description: 'Title of the account settings sub-page',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
id: 'settings.account.description',
|
||||||
|
defaultMessage: "You can improve your account security by installing a Nostr browser extension, like {link}. By storing your Nostr private key within a browser extension, you will be able to securely sign into any Nostr web app, including Primal.",
|
||||||
|
description: 'Warning about account security',
|
||||||
|
},
|
||||||
|
pubkey: {
|
||||||
|
id: 'settings.account.pubkey',
|
||||||
|
defaultMessage: 'Your Public Key',
|
||||||
|
description: 'Your public key section caption',
|
||||||
|
},
|
||||||
|
pubkeyDesc: {
|
||||||
|
id: 'settings.account.pubkeyDesc',
|
||||||
|
defaultMessage: 'Anyone on Nostr can find you via your public key. Feel free to share anywhere.',
|
||||||
|
description: 'Label describing the public key',
|
||||||
|
},
|
||||||
|
privkey: {
|
||||||
|
id: 'settings.account.privkey',
|
||||||
|
defaultMessage: 'Your Private Key',
|
||||||
|
description: 'Your private key section caption',
|
||||||
|
},
|
||||||
|
privkeyDesc: {
|
||||||
|
id: 'settings.account.privkeyDesc',
|
||||||
|
defaultMessage: 'This key fully controls your Nostr account. Don’t share it with anyone. Only copy this key to store it securely or to login to another Nostr app.',
|
||||||
|
description: 'Label describing the private key',
|
||||||
|
},
|
||||||
|
},
|
||||||
appearance: {
|
appearance: {
|
||||||
title: {
|
title: {
|
||||||
id: 'settings.appearance.title',
|
id: 'settings.appearance.title',
|
||||||
|
Loading…
Reference in New Issue
Block a user