feat: revamp note creator

This commit is contained in:
kieran 2024-09-12 13:18:19 +01:00
parent f217ca7e64
commit a1e99c8830
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
18 changed files with 641 additions and 259 deletions

View File

@ -1,101 +0,0 @@
.note-creator-modal .modal-body > div {
display: flex;
flex-direction: column;
gap: 16px;
}
.note-creator-modal .note.card {
padding: 0;
border: none;
min-height: unset;
}
.note-creator-modal .note.card.note-quote {
border: 1px solid var(--gray);
padding: 8px 12px;
}
.note-creator-modal h4 {
font-size: 11px;
font-weight: 600;
letter-spacing: 1.21px;
text-transform: uppercase;
color: var(--gray-light);
margin: 0;
}
.note-creator-relay {
background-color: var(--gray-dark);
border-radius: 12px;
}
.note-creator textarea {
border: none;
outline: none;
resize: none;
padding: 0;
border-radius: 0;
margin: 8px 12px;
min-height: 100px;
width: stretch;
width: -webkit-fill-available;
width: -moz-available;
max-height: 210px;
}
.note-creator textarea::placeholder {
color: var(--font-secondary-color);
font-size: var(--font-size);
line-height: 24px;
}
.note-creator.poll textarea {
min-height: 120px;
}
.note-creator .error {
position: absolute;
left: 16px;
bottom: 12px;
color: var(--error);
margin-right: 12px;
font-size: 16px;
}
.note-creator-icon {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
cursor: pointer;
}
.note-creator-icon.pfp .avatar {
width: 32px;
height: 32px;
}
.note-creator-modal .rti--container {
background-color: unset !important;
box-shadow: unset !important;
border: 2px solid var(--border-color) !important;
border-radius: 12px !important;
padding: 4px 8px !important;
}
.note-creator-modal .rti--tag {
color: black !important;
padding: 4px 10px !important;
border-radius: 12px !important;
display: unset !important;
}
.note-creator-modal .rti--input {
width: 100% !important;
border: unset !important;
}
.note-creator-modal .rti--tag button {
padding: 0 0 0 var(--rti-s);
}

View File

@ -1,8 +1,8 @@
/* eslint-disable max-lines */
import "./NoteCreator.css";
import { fetchNip05Pubkey, unixNow } from "@snort/shared";
import { EventBuilder, EventKind, NostrLink, NostrPrefix, TaggedNostrEvent, tryParseNostrLink } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { Menu, MenuItem } from "@szhsin/react-menu";
import classNames from "classnames";
import { ClipboardEventHandler, DragEvent, useEffect } from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -10,21 +10,24 @@ import { FormattedMessage, useIntl } from "react-intl";
import AsyncButton from "@/Components/Button/AsyncButton";
import { AsyncIcon } from "@/Components/Button/AsyncIcon";
import CloseButton from "@/Components/Button/CloseButton";
import IconButton from "@/Components/Button/IconButton";
import { sendEventToRelays } from "@/Components/Event/Create/util";
import Note from "@/Components/Event/EventComponent";
import Flyout from "@/Components/flyout";
import Icon from "@/Components/Icons/Icon";
import { ToggleSwitch } from "@/Components/Icons/Toggle";
import Modal from "@/Components/Modal/Modal";
import Textarea from "@/Components/Textarea/Textarea";
import { Toastore } from "@/Components/Toaster/Toaster";
import ProfileImage from "@/Components/User/ProfileImage";
import { MediaServerFileList } from "@/Components/Upload/file-picker";
import Avatar from "@/Components/User/Avatar";
import useEventPublisher from "@/Hooks/useEventPublisher";
import useLogin from "@/Hooks/useLogin";
import usePreferences from "@/Hooks/usePreferences";
import useRelays from "@/Hooks/useRelays";
import { useNoteCreator } from "@/State/NoteCreator";
import { openFile, trackEvent } from "@/Utils";
import useFileUpload from "@/Utils/Upload";
import useFileUpload, { addExtensionToNip94Url, nip94TagsToIMeta, readNip94Tags } from "@/Utils/Upload";
import { GetPowWorker } from "@/Utils/wasm";
import { ZapTarget } from "@/Utils/Zapper";
@ -59,6 +62,7 @@ export function NoteCreator() {
const { formatMessage } = useIntl();
const uploader = useFileUpload();
const publicKey = useLogin(s => s.publicKey);
const profile = useUserProfile(publicKey);
const pow = usePreferences(s => s.pow);
const relays = useRelays();
const { system, publisher: pub } = useEventPublisher();
@ -145,6 +149,18 @@ export function NoteCreator() {
extraTags ??= [];
extraTags.push(...note.hashTags.map(a => ["t", a.toLowerCase()]));
}
for (const ex of note.otherEvents ?? []) {
const meta = readNip94Tags(ex.tags);
if (!meta.url) continue;
if (!note.note.endsWith("\n")) {
note.note += "\n";
}
note.note += addExtensionToNip94Url(meta);
extraTags ??= [];
extraTags.push(nip94TagsToIMeta(meta));
}
// add quote repost
if (note.quote) {
if (!note.note.endsWith("\n")) {
@ -211,19 +227,16 @@ export function NoteCreator() {
}
trackEvent("PostNote", props);
const events = (note.otherEvents ?? []).concat(ev);
events.map(a =>
sendEventToRelays(system, a, note.selectedCustomRelays, r => {
if (CONFIG.noteCreatorToast) {
r.forEach(rr => {
Toastore.push({
element: c => <OkResponseRow rsp={rr} close={c} />,
expire: unixNow() + (rr.ok ? 5 : 55555),
});
sendEventToRelays(system, ev, note.selectedCustomRelays, r => {
if (CONFIG.noteCreatorToast) {
r.forEach(rr => {
Toastore.push({
element: c => <OkResponseRow rsp={rr} close={c} />,
expire: unixNow() + (rr.ok ? 5 : 55555),
});
}
}),
);
});
}
});
note.update(n => n.reset());
localStorage.removeItem("msgDraft");
}
@ -252,25 +265,13 @@ export function NoteCreator() {
const rx = await uploader.upload(file, file.name);
note.update(v => {
if (rx.header) {
const link = `nostr:${new NostrLink(NostrPrefix.Event, rx.header.id, rx.header.kind).encode(
CONFIG.eventLinkPrefix,
)}`;
v.note = `${v.note ? `${v.note}\n` : ""}${link}`;
v.otherEvents = [...(v.otherEvents ?? []), rx.header];
v.otherEvents ??= [];
v.otherEvents.push(rx.header);
} else if (rx.url) {
v.note = `${v.note ? `${v.note}\n` : ""}${rx.url}`;
if (rx.metadata) {
v.extraTags ??= [];
const imeta = ["imeta", `url ${rx.url}`];
if (rx.metadata.blurhash) {
imeta.push(`blurhash ${rx.metadata.blurhash}`);
}
if (rx.metadata.width && rx.metadata.height) {
imeta.push(`dim ${rx.metadata.width}x${rx.metadata.height}`);
}
if (rx.metadata.hash) {
imeta.push(`x ${rx.metadata.hash}`);
}
const imeta = nip94TagsToIMeta(rx.metadata);
v.extraTags.push(imeta);
}
} else if (rx?.error) {
@ -369,12 +370,12 @@ export function NoteCreator() {
function renderRelayCustomisation() {
return (
<div className="flex flex-col g8">
<div className="flex flex-col gap-2">
{Object.entries(relays)
.filter(el => el[1].write)
.map(a => a[0])
.map((r, i, a) => (
<div className="p flex justify-between note-creator-relay" key={r}>
<div className="p flex items-center justify-between bg-gray br" key={r}>
<div>{r}</div>
<div>
<input
@ -470,7 +471,7 @@ export function NoteCreator() {
}
/>
</div>
<div className="flex flex-col s g4">
<div className="flex flex-col g4">
<div>&nbsp;</div>
<Icon
name="close"
@ -488,20 +489,14 @@ export function NoteCreator() {
</button>
</div>
<span className="warning">
<FormattedMessage
defaultMessage="Not all clients support this, you may still receive some zaps as if zap splits was not configured"
id="6bgpn+"
/>
<FormattedMessage defaultMessage="Not all clients support this, you may still receive some zaps as if zap splits was not configured" />
</span>
</div>
<div className="flex flex-col g8">
<h4>
<FormattedMessage defaultMessage="Sensitive Content" />
</h4>
<FormattedMessage
defaultMessage="Users must accept the content warning to show the content of your note."
id="UUPFlt"
/>
<FormattedMessage defaultMessage="Users must accept the content warning to show the content of your note." />
<input
className="w-max"
type="text"
@ -525,29 +520,43 @@ export function NoteCreator() {
function noteCreatorFooter() {
return (
<div className="flex justify-between">
<div className="flex items-center g8">
<ProfileImage
pubkey={publicKey ?? ""}
className="note-creator-icon"
link=""
showUsername={false}
showFollowDistance={false}
showProfileCard={false}
/>
<div className="flex items-center gap-4 text-gray-light cursor-pointer">
<Avatar pubkey={publicKey ?? ""} user={profile} size={28} showTitle={true} />
<Menu
menuButton={
<AsyncIcon iconName="attachment" iconSize={24} className="hover:text-gray-superlight transition" />
}
menuClassName="ctx-menu no-icons">
<div className="close-menu-container">
{/* This menu item serves as a "close menu" button;
it allows the user to click anywhere nearby the menu to close it. */}
<MenuItem>
<div className="close-menu" />
</MenuItem>
</div>
<MenuItem onClick={() => note.update(s => (s.filePicker = "compact"))}>
<FormattedMessage defaultMessage="From Server" />
</MenuItem>
<MenuItem onClick={() => attachFile()}>
<FormattedMessage defaultMessage="From File" />
</MenuItem>
</Menu>
{note.pollOptions === undefined && !note.replyTo && (
<AsyncIcon
iconName="list"
iconName="bar-chart"
iconSize={24}
onClick={() => note.update(v => (v.pollOptions = ["A", "B"]))}
className={classNames("note-creator-icon", { active: note.pollOptions !== undefined })}
className={classNames("hover:text-gray-superlight transition", {
"text-white": note.pollOptions !== undefined,
})}
/>
)}
<AsyncIcon iconName="image-plus" iconSize={24} onClick={attachFile} className="note-creator-icon" />
<AsyncIcon
iconName="settings-04"
iconName="settings-outline"
iconSize={24}
onClick={() => note.update(v => (v.advanced = !v.advanced))}
className={classNames("note-creator-icon", { active: note.advanced })}
className={classNames("hover:text-gray-superlight transition", { "text-white": note.advanced })}
/>
<span className="sm:inline hidden">
<FormattedMessage defaultMessage="Preview" />
@ -558,14 +567,9 @@ export function NoteCreator() {
className={classNames({ active: Boolean(note.preview) })}
/>
</div>
<div className="flex g8">
<button className="secondary" onClick={cancel}>
<FormattedMessage defaultMessage="Cancel" />
</button>
<AsyncButton onClick={onSubmit} className="primary">
{note.replyTo ? <FormattedMessage defaultMessage="Reply" /> : <FormattedMessage defaultMessage="Send" />}
</AsyncButton>
</div>
<AsyncButton onClick={onSubmit} className="primary">
{note.replyTo ? <FormattedMessage defaultMessage="Reply" /> : <FormattedMessage defaultMessage="Send" />}
</AsyncButton>
</div>
);
}
@ -634,13 +638,22 @@ export function NoteCreator() {
)}
{note.preview && getPreviewNote()}
{!note.preview && (
<>
<div onPaste={handlePaste} className={classNames("note-creator", { poll: Boolean(note.pollOptions) })}>
<div className="flex flex-col gap-4">
<div className="font-medium flex justify-between items-center">
<FormattedMessage defaultMessage="Compose a note" />
<AsyncIcon
iconName="x"
className="bg-gray rounded-full items-center justify-center flex p-1 cursor-pointer"
onClick={cancel}
/>
</div>
<div onPaste={handlePaste} className={classNames({ poll: Boolean(note.pollOptions) })}>
<Textarea
className="!border-none !resize-none !p-0 !rounded-none !text-sm"
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
autoFocus
autoFocus={true}
onChange={c => onChange(c)}
value={note.note}
onFocus={() => note.update(v => (v.active = true))}
@ -652,12 +665,74 @@ export function NoteCreator() {
/>
{renderPollOptions()}
</div>
</>
</div>
)}
{uploader.progress.length > 0 && <FileUploadProgress progress={uploader.progress} />}
{(note.otherEvents?.length ?? 0) > 0 && !note.preview && (
<div className="flex gap-2 flex-wrap">
{note.otherEvents
?.map(a => ({
event: a,
tags: readNip94Tags(a.tags),
}))
.filter(a => a.tags.url)
.map(a => (
<div key={a.tags.url} className="relative">
<img
className="object-cover w-[80px] h-[80px] !mt-0 rounded-lg"
src={addExtensionToNip94Url(a.tags)}
/>
<Icon
name="x"
className="absolute -top-[0.25rem] -right-[0.25rem] bg-gray rounded-full cursor-pointer"
onClick={() =>
note.update(
n => (n.otherEvents = n.otherEvents?.filter(b => readNip94Tags(b.tags).url !== a.tags.url)),
)
}
/>
</div>
))}
</div>
)}
{noteCreatorFooter()}
{note.error && <span className="error">{note.error}</span>}
{note.advanced && noteCreatorAdvanced()}
<Flyout
show={note.filePicker !== "hidden"}
width={note.filePicker !== "compact" ? "70vw" : undefined}
onClose={() => note.update(v => (v.filePicker = "hidden"))}
side="right"
title={
<div className="text-xl font-medium">
<FormattedMessage defaultMessage="Attach Media" />
</div>
}
actions={
<>
<IconButton
icon={{
name: "expand",
}}
onClick={() => note.update(n => (n.filePicker = n.filePicker === "wide" ? "compact" : "wide"))}
/>
</>
}>
<div className="overflow-y-auto h-[calc(100%-2rem)]">
{note.filePicker !== "hidden" && (
<MediaServerFileList
onPicked={files => {
note.update(n => {
n.otherEvents ??= [];
n.otherEvents?.push(...files);
n.filePicker = "hidden";
});
}}
cols={note.filePicker === "compact" ? 2 : 6}
/>
)}
</div>
</Flyout>
</>
);
}
@ -670,11 +745,7 @@ export function NoteCreator() {
if (!note.show) return null;
return (
<Modal
id="note-creator"
bodyClassName="modal-body flex flex-col gap-4"
className="note-creator-modal"
onClose={reset}>
<Modal id="note-creator" bodyClassName="modal-body gap-4" onClose={reset}>
{noteCreatorForm()}
</Modal>
);

View File

@ -15,10 +15,12 @@ export const NoteCreatorButton = ({
className,
alwaysShow,
showText,
withModal,
}: {
className?: string;
alwaysShow?: boolean;
showText?: boolean;
withModal: boolean;
}) => {
const buttonRef = useRef<HTMLButtonElement>(null);
const location = useLocation();
@ -79,7 +81,7 @@ export const NoteCreatorButton = ({
)}
</button>
)}
<NoteCreator key="global-note-creator" />
{withModal && <NoteCreator key="global-note-creator" />}
</>
);
};

View File

@ -9,8 +9,8 @@
<symbol id="arrowUp" viewBox="0 0 12 12" fill="none">
<path d="M5.99992 10.6673V1.33398M5.99992 1.33398L1.33325 6.00065M5.99992 1.33398L10.6666 6.00065" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</symbol>
<symbol id="attachment" viewBox="0 0 21 22" fill="none">
<path d="M19.1525 9.89945L10.1369 18.9151C8.08662 20.9653 4.7625 20.9653 2.71225 18.9151C0.661997 16.8648 0.661998 13.5407 2.71225 11.4904L11.7279 2.47483C13.0947 1.108 15.3108 1.108 16.6776 2.47483C18.0444 3.84167 18.0444 6.05775 16.6776 7.42458L8.01555 16.0866C7.33213 16.7701 6.22409 16.7701 5.54068 16.0866C4.85726 15.4032 4.85726 14.2952 5.54068 13.6118L13.1421 6.01037" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<symbol id="attachment" viewBox="0 0 24 25" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1667 3.5C8.69391 3.5 7.5 4.69391 7.5 6.16667V17C7.5 19.4853 9.51472 21.5 12 21.5C14.4853 21.5 16.5 19.4853 16.5 17V5.75581C16.5 5.20353 16.9477 4.75581 17.5 4.75581C18.0523 4.75581 18.5 5.20353 18.5 5.75581V17C18.5 20.5899 15.5899 23.5 12 23.5C8.41015 23.5 5.5 20.5899 5.5 17V6.16667C5.5 3.58934 7.58934 1.5 10.1667 1.5C12.744 1.5 14.8333 3.58934 14.8333 6.16667V16.9457C14.8333 18.5105 13.5648 19.7791 12 19.7791C10.4352 19.7791 9.16667 18.5105 9.16667 16.9457V7.15116C9.16667 6.59888 9.61438 6.15116 10.1667 6.15116C10.719 6.15116 11.1667 6.59888 11.1667 7.15116V16.9457C11.1667 17.406 11.5398 17.7791 12 17.7791C12.4602 17.7791 12.8333 17.406 12.8333 16.9457V6.16667C12.8333 4.69391 11.6394 3.5 10.1667 3.5Z" fill="currentColor"/>
</symbol>
<symbol id="badge" viewBox="0 0 16 15" fill="none">
<path d="M6.00004 7.50065L7.33337 8.83398L10.3334 5.83398M11.9342 2.83299C12.0714 3.16501 12.3349 3.42892 12.6667 3.5667L13.8302 4.04864C14.1622 4.18617 14.426 4.44998 14.5636 4.78202C14.7011 5.11407 14.7011 5.48715 14.5636 5.81919L14.082 6.98185C13.9444 7.31404 13.9442 7.6875 14.0824 8.01953L14.5632 9.18185C14.6313 9.34631 14.6664 9.52259 14.6665 9.70062C14.6665 9.87865 14.6315 10.0549 14.5633 10.2194C14.4952 10.3839 14.3953 10.5333 14.2694 10.6592C14.1435 10.7851 13.9941 10.8849 13.8296 10.953L12.6669 11.4346C12.3349 11.5718 12.071 11.8354 11.9333 12.1672L11.4513 13.3307C11.3138 13.6627 11.05 13.9265 10.718 14.0641C10.3859 14.2016 10.0129 14.2016 9.68085 14.0641L8.51823 13.5825C8.18619 13.4453 7.81326 13.4455 7.48143 13.5832L6.31797 14.0645C5.98612 14.2017 5.61338 14.2016 5.28162 14.0642C4.94986 13.9267 4.68621 13.6632 4.54858 13.3316L4.06652 12.1677C3.92924 11.8357 3.66574 11.5718 3.33394 11.434L2.17048 10.9521C1.8386 10.8146 1.57488 10.5509 1.4373 10.2191C1.29971 9.88724 1.29953 9.51434 1.43678 9.18235L1.91835 8.01968C2.05554 7.68763 2.05526 7.31469 1.91757 6.98284L1.43669 5.81851C1.36851 5.65405 1.3334 5.47777 1.33337 5.29974C1.33335 5.12171 1.3684 4.94542 1.43652 4.78094C1.50465 4.61646 1.60452 4.46702 1.73042 4.34115C1.85632 4.21529 2.00579 4.11546 2.17028 4.04739L3.33291 3.5658C3.66462 3.42863 3.92836 3.16545 4.06624 2.83402L4.54816 1.67052C4.68569 1.33848 4.94949 1.07467 5.28152 0.937137C5.61355 0.7996 5.98662 0.7996 6.31865 0.937137L7.48127 1.41873C7.81331 1.55593 8.18624 1.55565 8.51808 1.41795L9.68202 0.937884C10.014 0.800424 10.387 0.800452 10.719 0.937962C11.0509 1.07547 11.3147 1.3392 11.4522 1.67116L11.9343 2.835L11.9342 2.83299Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
@ -402,18 +402,15 @@
<path d="M7.39516 18.3711L7.97961 19.6856C8.15335 20.0768 8.43689 20.4093 8.79583 20.6426C9.15478 20.8759 9.57372 21.0001 10.0018 21C10.4299 21.0001 10.8489 20.8759 11.2078 20.6426C11.5668 20.4093 11.8503 20.0768 12.0241 19.6856L12.6085 18.3711C12.8165 17.9047 13.1665 17.5159 13.6085 17.26C14.0533 17.0034 14.5678 16.8941 15.0785 16.9478L16.5085 17.1C16.9342 17.145 17.3638 17.0656 17.7452 16.8713C18.1266 16.6771 18.4435 16.3763 18.6574 16.0056C18.8716 15.635 18.9736 15.2103 18.9511 14.7829C18.9286 14.3555 18.7826 13.9438 18.5307 13.5978L17.6841 12.4344C17.3826 12.0171 17.2215 11.5148 17.2241 11C17.224 10.4866 17.3866 9.98635 17.6885 9.57111L18.5352 8.40778C18.787 8.06175 18.9331 7.65007 18.9556 7.22267C18.978 6.79528 18.876 6.37054 18.6618 6C18.4479 5.62923 18.1311 5.32849 17.7496 5.13423C17.3682 4.93997 16.9386 4.86053 16.5129 4.90556L15.0829 5.05778C14.5723 5.11141 14.0577 5.00212 13.6129 4.74556C13.1701 4.48825 12.82 4.09736 12.6129 3.62889L12.0241 2.31444C11.8503 1.92317 11.5668 1.59072 11.2078 1.3574C10.8489 1.12408 10.4299 0.99993 10.0018 1C9.57372 0.99993 9.15478 1.12408 8.79583 1.3574C8.43689 1.59072 8.15335 1.92317 7.97961 2.31444L7.39516 3.62889C7.18809 4.09736 6.83804 4.48825 6.39516 4.74556C5.95038 5.00212 5.43583 5.11141 4.92516 5.05778L3.49072 4.90556C3.06505 4.86053 2.63546 4.93997 2.25403 5.13423C1.87261 5.32849 1.55574 5.62923 1.34183 6C1.12765 6.37054 1.02561 6.79528 1.0481 7.22267C1.07058 7.65007 1.21662 8.06175 1.4685 8.40778L2.31516 9.57111C2.61711 9.98635 2.7797 10.4866 2.77961 11C2.7797 11.5134 2.61711 12.0137 2.31516 12.4289L1.4685 13.5922C1.21662 13.9382 1.07058 14.3499 1.0481 14.7773C1.02561 15.2047 1.12765 15.6295 1.34183 16C1.55595 16.3706 1.87286 16.6712 2.25423 16.8654C2.6356 17.0596 3.06508 17.1392 3.49072 17.0944L4.92072 16.9422C5.43139 16.8886 5.94594 16.9979 6.39072 17.2544C6.83525 17.511 7.18693 17.902 7.39516 18.3711Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 14C11.6569 14 13 12.6569 13 11C13 9.34315 11.6569 8 10 8C8.34319 8 7.00004 9.34315 7.00004 11C7.00004 12.6569 8.34319 14 10 14Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="home-solid" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.43553 0.113589C9.15028 0.0363557 8.84962 0.0363557 8.56438 0.113589C8.23324 0.203247 7.95445 0.422125 7.73194 0.596817L7.66975 0.645498L1.95307 5.09173C1.63921 5.3353 1.3627 5.54988 1.15665 5.82847C0.975827 6.07295 0.841122 6.34837 0.759154 6.64121C0.665754 6.97489 0.666132 7.3249 0.666561 7.72218L0.666619 13.8654C0.666604 14.3047 0.666592 14.6837 0.692095 14.9958C0.719012 15.3253 0.778442 15.6529 0.939104 15.9683C1.17879 16.4387 1.56124 16.8211 2.03164 17.0608C2.34696 17.2215 2.67464 17.2809 3.0041 17.3078C3.31624 17.3333 3.6952 17.3333 4.13448 17.3333H13.8654C14.3047 17.3333 14.6837 17.3333 14.9958 17.3078C15.3253 17.2809 15.6529 17.2215 15.9683 17.0608C16.4387 16.8211 16.8211 16.4387 17.0608 15.9683C17.2215 15.6529 17.2809 15.3253 17.3078 14.9958C17.3333 14.6837 17.3333 14.3047 17.3333 13.8654L17.3333 7.72219C17.3338 7.3249 17.3342 6.97489 17.2408 6.64121C17.1588 6.34837 17.0241 6.07295 16.8433 5.82847C16.6372 5.54988 16.3607 5.33529 16.0468 5.09172L10.3302 0.645498L10.268 0.59682C10.0455 0.422128 9.76667 0.203248 9.43553 0.113589ZM6.57867 10.4589C6.46395 10.0132 6.00963 9.74487 5.56392 9.85959C5.11821 9.97431 4.84989 10.4286 4.9646 10.8743C5.4271 12.6713 7.05731 14 8.99995 14C10.9426 14 12.5728 12.6713 13.0353 10.8743C13.15 10.4286 12.8817 9.97431 12.436 9.85959C11.9903 9.74487 11.536 10.0132 11.4212 10.4589C11.1437 11.5374 10.1637 12.3333 8.99995 12.3333C7.8362 12.3333 6.85624 11.5374 6.57867 10.4589Z" fill="currentColor"/>
</symbol>
<symbol id="home-outline" viewBox="0 0 18 19" fill="none">
<path d="M5.77168 11.6668C6.14172 13.1045 7.4468 14.1668 9 14.1668C10.5532 14.1668 11.8583 13.1045 12.2283 11.6668M8.18141 2.30345L2.52949 6.69939C2.15168 6.99324 1.96278 7.14017 1.82669 7.32417C1.70614 7.48716 1.61633 7.67077 1.56169 7.866C1.5 8.08639 1.5 8.3257 1.5 8.80433V14.8334C1.5 15.7669 1.5 16.2336 1.68166 16.5901C1.84144 16.9037 2.09641 17.1587 2.41002 17.3185C2.76654 17.5001 3.23325 17.5001 4.16667 17.5001H13.8333C14.7668 17.5001 15.2335 17.5001 15.59 17.3185C15.9036 17.1587 16.1586 16.9037 16.3183 16.5901C16.5 16.2336 16.5 15.7669 16.5 14.8334V8.80433C16.5 8.3257 16.5 8.08639 16.4383 7.866C16.3837 7.67077 16.2939 7.48716 16.1733 7.32417C16.0372 7.14017 15.8483 6.99324 15.4705 6.69939L9.81859 2.30345C9.52582 2.07574 9.37943 1.96189 9.21779 1.91812C9.07516 1.87951 8.92484 1.87951 8.78221 1.91812C8.62057 1.96189 8.47418 2.07574 8.18141 2.30345Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="sign-in" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 3C6.44772 3 6 3.44772 6 4C6 4.55228 6.44772 5 7 5H18C18.5523 5 19 5.44772 19 6V18C19 18.5523 18.5523 19 18 19H7C6.44772 19 6 19.4477 6 20C6 20.5523 6.44772 21 7 21H18C19.6569 21 21 19.6569 21 18V6C21 4.34315 19.6569 3 18 3H7ZM12.7071 7.29289C12.3166 6.90237 11.6834 6.90237 11.2929 7.29289C10.9024 7.68342 10.9024 8.31658 11.2929 8.70711L13.5858 11H4C3.44772 11 3 11.4477 3 12C3 12.5523 3.44772 13 4 13H13.5858L11.2929 15.2929C10.9024 15.6834 10.9024 16.3166 11.2929 16.7071C11.6834 17.0976 12.3166 17.0976 12.7071 16.7071L16.7071 12.7071C17.0976 12.3166 17.0976 11.6834 16.7071 11.2929L12.7071 7.29289Z" fill="currentColor"/>
</symbol>
<symbol id="deck-solid" viewBox="0 0 20 20" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3011 1.66602H14.8653C15.3046 1.666 15.6836 1.66599 15.9957 1.69149C16.3251 1.71841 16.6528 1.77784 16.9681 1.9385C17.4386 2.17818 17.821 2.56064 18.0607 3.03104C18.2213 3.34636 18.2808 3.67404 18.3077 4.00349C18.3332 4.31564 18.3332 4.69462 18.3332 5.13392V14.8648C18.3332 15.3041 18.3332 15.6831 18.3077 15.9952C18.2808 16.3247 18.2213 16.6523 18.0607 16.9677C17.821 17.4381 17.4386 17.8205 16.9681 18.0602C16.6528 18.2209 16.3251 18.2803 15.9957 18.3072C15.6836 18.3327 15.3046 18.3327 14.8653 18.3327H14.301C13.8618 18.3327 13.4828 18.3327 13.1706 18.3072C12.8412 18.2803 12.5135 18.2209 12.1982 18.0602C11.7278 17.8205 11.3453 17.4381 11.1057 16.9677C10.945 16.6523 10.8856 16.3247 10.8586 15.9952C10.8331 15.6831 10.8332 15.3041 10.8332 14.8648V5.1339C10.8332 4.69461 10.8331 4.31564 10.8586 4.00349C10.8856 3.67404 10.945 3.34636 11.1057 3.03104C11.3453 2.56064 11.7278 2.17818 12.1982 1.9385C12.5135 1.77784 12.8412 1.71841 13.1706 1.69149C13.4828 1.66599 13.8618 1.666 14.3011 1.66602Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.13439 1.66602H5.69863C6.13792 1.666 6.51689 1.66599 6.82903 1.69149C7.15848 1.71841 7.48617 1.77784 7.80148 1.9385C8.27189 2.17818 8.65434 2.56064 8.89402 3.03104C9.05468 3.34636 9.11411 3.67404 9.14103 4.00349C9.16653 4.31564 9.16652 4.69462 9.16651 5.13392V14.8648C9.16652 15.3041 9.16653 15.6831 9.14103 15.9952C9.11411 16.3247 9.05468 16.6523 8.89402 16.9677C8.65434 17.4381 8.27189 17.8205 7.80148 18.0602C7.48617 18.2209 7.15848 18.2803 6.82903 18.3072C6.51689 18.3327 6.13793 18.3327 5.69864 18.3327H5.13437C4.69508 18.3327 4.31612 18.3327 4.00398 18.3072C3.67453 18.2803 3.34685 18.2209 3.03153 18.0602C2.56112 17.8205 2.17867 17.4381 1.93899 16.9677C1.77833 16.6523 1.7189 16.3247 1.69198 15.9952C1.66648 15.6831 1.66649 15.3041 1.6665 14.8648V5.1339C1.66649 4.69461 1.66648 4.31564 1.69198 4.00349C1.7189 3.67404 1.77833 3.34636 1.93899 3.03104C2.17867 2.56064 2.56112 2.17818 3.03153 1.9385C3.34685 1.77784 3.67453 1.71841 4.00398 1.69149C4.31613 1.66599 4.69509 1.666 5.13439 1.66602Z" fill="currentColor"/>
@ -422,28 +419,24 @@
<path d="M4.66667 1.5H4.16667C3.23325 1.5 2.76654 1.5 2.41002 1.68166C2.09641 1.84144 1.84144 2.09641 1.68166 2.41002C1.5 2.76654 1.5 3.23325 1.5 4.16667V13.8333C1.5 14.7668 1.5 15.2335 1.68166 15.59C1.84144 15.9036 2.09641 16.1586 2.41002 16.3183C2.76654 16.5 3.23325 16.5 4.16667 16.5H4.66667C5.60009 16.5 6.0668 16.5 6.42332 16.3183C6.73692 16.1586 6.99189 15.9036 7.15168 15.59C7.33333 15.2335 7.33333 14.7668 7.33333 13.8333V4.16667C7.33333 3.23325 7.33333 2.76654 7.15168 2.41002C6.99189 2.09641 6.73692 1.84144 6.42332 1.68166C6.0668 1.5 5.60009 1.5 4.66667 1.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.8333 1.5H13.3333C12.3999 1.5 11.9332 1.5 11.5767 1.68166C11.2631 1.84144 11.0081 2.09641 10.8483 2.41002C10.6667 2.76654 10.6667 3.23325 10.6667 4.16667V13.8333C10.6667 14.7668 10.6667 15.2335 10.8483 15.59C11.0081 15.9036 11.2631 16.1586 11.5767 16.3183C11.9332 16.5 12.3999 16.5 13.3333 16.5H13.8333C14.7668 16.5 15.2335 16.5 15.59 16.3183C15.9036 16.1586 16.1586 15.9036 16.3183 15.59C16.5 15.2335 16.5 14.7668 16.5 13.8333V4.16667C16.5 3.23325 16.5 2.76654 16.3183 2.41002C16.1586 2.09641 15.9036 1.84144 15.59 1.68166C15.2335 1.5 14.7668 1.5 13.8333 1.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="graph-outline" viewBox="0 0 20 22" fill="none">
<path d="M6.59 12.51L13.42 16.49M13.41 5.51L6.59 9.49M19 4C19 5.65685 17.6569 7 16 7C14.3431 7 13 5.65685 13 4C13 2.34315 14.3431 1 16 1C17.6569 1 19 2.34315 19 4ZM7 11C7 12.6569 5.65685 14 4 14C2.34315 14 1 12.6569 1 11C1 9.34315 2.34315 8 4 8C5.65685 8 7 9.34315 7 11ZM19 18C19 19.6569 17.6569 21 16 21C14.3431 21 13 19.6569 13 18C13 16.3431 14.3431 15 16 15C17.6569 15 19 16.3431 19 18Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="graph-solid" viewBox="0 0 24 24" fill="none">
<path d="M14 5C14 2.79086 15.7909 1 18 1C20.2091 1 22 2.79086 22 5C22 7.20914 20.2091 9 18 9C16.8885 9 15.883 8.54668 15.1581 7.81485L9.85034 10.9123C9.94784 11.2581 10 11.623 10 12C10 12.3768 9.9479 12.7415 9.8505 13.0871L15.1613 16.1819C15.886 15.452 16.8902 15 18 15C20.2091 15 22 16.7909 22 19C22 21.2091 20.2091 23 18 23C15.7909 23 14 21.2091 14 19C14 18.6214 14.0526 18.255 14.1509 17.9079L8.84235 14.8144C8.11742 15.5465 7.11167 16 6 16C3.79086 16 2 14.2091 2 12C2 9.79086 3.79086 8 6 8C7.11146 8 8.11703 8.45332 8.84193 9.18514L14.1497 6.08767C14.0522 5.74185 14 5.37701 14 5Z" fill="currentColor"/>
</symbol>
<symbol id="media" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.80659 0.666994C3.87988 0.667127 4.95525 0.667127 6.02642 0.666994C6.23674 0.666969 6.44157 0.666945 6.61503 0.681118C6.80553 0.696682 7.03031 0.733404 7.25649 0.848652C7.5701 1.00844 7.82506 1.26341 7.98485 1.57701C8.1001 1.8032 8.13682 2.02798 8.15239 2.21847C8.16656 2.39194 8.16654 2.59677 8.16651 2.8071V6.0269C8.16654 6.23722 8.16656 6.44205 8.15239 6.61552C8.13682 6.80602 8.1001 7.03079 7.98485 7.25698C7.82506 7.57058 7.5701 7.82555 7.25649 7.98534C7.03031 8.10059 6.80553 8.13731 6.61503 8.15288C6.44156 8.16705 6.23673 8.16702 6.02641 8.167H2.80661C2.59628 8.16702 2.39145 8.16705 2.21798 8.15288C2.02749 8.13731 1.80271 8.10059 1.57652 7.98534C1.26292 7.82555 1.00795 7.57058 0.848164 7.25698C0.732916 7.03079 0.696193 6.80602 0.680629 6.61552C0.666457 6.44206 0.666481 6.23723 0.666506 6.02691C0.666507 6.01806 0.666508 6.0092 0.666508 6.00033V2.83366C0.666508 2.82479 0.666507 2.81593 0.666506 2.80708C0.666481 2.59676 0.666457 2.39194 0.680629 2.21847C0.696193 2.02798 0.732916 1.8032 0.848164 1.57701C1.00795 1.26341 1.26292 1.00844 1.57652 0.848652C1.80271 0.733404 2.02749 0.696682 2.21798 0.681118C2.39145 0.666945 2.59627 0.666969 2.80659 0.666994Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.80659 9.83366C3.87988 9.83379 4.95525 9.83379 6.02642 9.83366C6.23674 9.83364 6.44157 9.83361 6.61503 9.84778C6.80553 9.86335 7.03031 9.90007 7.25649 10.0153C7.5701 10.1751 7.82506 10.4301 7.98485 10.7437C8.1001 10.9699 8.13682 11.1946 8.15239 11.3851C8.16656 11.5586 8.16654 11.7634 8.16651 11.9738V15.1936C8.16654 15.4039 8.16656 15.6087 8.15239 15.7822C8.13682 15.9727 8.1001 16.1975 7.98485 16.4236C7.82506 16.7373 7.5701 16.9922 7.25649 17.152C7.03031 17.2673 6.80553 17.304 6.61503 17.3195C6.44156 17.3337 6.23673 17.3337 6.02641 17.3337H2.80661C2.59628 17.3337 2.39145 17.3337 2.21798 17.3195C2.02749 17.304 1.80271 17.2673 1.57652 17.152C1.26292 16.9922 1.00795 16.7373 0.848164 16.4236C0.732916 16.1975 0.696193 15.9727 0.680629 15.7822C0.666457 15.6087 0.666481 15.4039 0.666506 15.1936C0.666507 15.1847 0.666508 15.1759 0.666508 15.167V12.0003C0.666508 11.9915 0.666507 11.9826 0.666506 11.9737C0.666481 11.7634 0.666457 11.5586 0.680629 11.3851C0.696193 11.1946 0.732916 10.9699 0.848164 10.7437C1.00795 10.4301 1.26292 10.1751 1.57652 10.0153C1.80271 9.90007 2.02749 9.86335 2.21798 9.84778C2.39145 9.83361 2.59627 9.83364 2.80659 9.83366Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9733 0.666994C13.0465 0.667127 14.1219 0.667127 15.1931 0.666994C15.4034 0.666969 15.6082 0.666945 15.7817 0.681118C15.9722 0.696682 16.197 0.733404 16.4232 0.848652C16.7368 1.00844 16.9917 1.26341 17.1515 1.57701C17.2668 1.8032 17.3035 2.02798 17.3191 2.21847C17.3332 2.39194 17.3332 2.59677 17.3332 2.8071V6.0269C17.3332 6.23722 17.3332 6.44205 17.3191 6.61552C17.3035 6.80602 17.2668 7.03079 17.1515 7.25698C16.9917 7.57058 16.7368 7.82555 16.4232 7.98534C16.197 8.10059 15.9722 8.13731 15.7817 8.15288C15.6082 8.16705 15.4034 8.16702 15.1931 8.167H11.9733C11.763 8.16702 11.5581 8.16705 11.3847 8.15288C11.1942 8.13731 10.9694 8.10059 10.7432 7.98534C10.4296 7.82555 10.1746 7.57058 10.0148 7.25698C9.89958 7.03079 9.86286 6.80602 9.8473 6.61552C9.83312 6.44206 9.83315 6.23723 9.83317 6.02691C9.83317 6.01806 9.83317 6.0092 9.83317 6.00033V2.83366C9.83317 2.82479 9.83317 2.81593 9.83317 2.80708C9.83315 2.59676 9.83312 2.39194 9.8473 2.21847C9.86286 2.02798 9.89958 1.8032 10.0148 1.57701C10.1746 1.26341 10.4296 1.00844 10.7432 0.848652C10.9694 0.733404 11.1942 0.696682 11.3847 0.681118C11.5581 0.666945 11.7629 0.666969 11.9733 0.666994Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9733 9.83366C13.0465 9.83379 14.1219 9.83379 15.1931 9.83366C15.4034 9.83364 15.6082 9.83361 15.7817 9.84778C15.9722 9.86335 16.197 9.90007 16.4232 10.0153C16.7368 10.1751 16.9917 10.4301 17.1515 10.7437C17.2668 10.9699 17.3035 11.1946 17.3191 11.3851C17.3332 11.5586 17.3332 11.7634 17.3332 11.9738V15.1936C17.3332 15.4039 17.3332 15.6087 17.3191 15.7822C17.3035 15.9727 17.2668 16.1975 17.1515 16.4236C16.9917 16.7373 16.7368 16.9922 16.4232 17.152C16.197 17.2673 15.9722 17.304 15.7817 17.3195C15.6082 17.3337 15.4034 17.3337 15.1931 17.3337H11.9733C11.763 17.3337 11.5581 17.3337 11.3847 17.3195C11.1942 17.304 10.9694 17.2673 10.7432 17.152C10.4296 16.9922 10.1746 16.7373 10.0148 16.4236C9.89958 16.1975 9.86286 15.9727 9.8473 15.7822C9.83312 15.6087 9.83315 15.4039 9.83317 15.1936C9.83317 15.1847 9.83317 15.1759 9.83317 15.167V12.0003C9.83317 11.9915 9.83317 11.9826 9.83317 11.9737C9.83315 11.7634 9.83312 11.5586 9.8473 11.3851C9.86286 11.1946 9.89958 10.9699 10.0148 10.7437C10.1746 10.4301 10.4296 10.1751 10.7432 10.0153C10.9694 9.90007 11.1942 9.86335 11.3847 9.84778C11.5581 9.83361 11.7629 9.83364 11.9733 9.83366Z" fill="currentColor"/>
</symbol>
<symbol id="info-solid" viewBox="0 0 22 22" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 0C4.92487 0 0 4.92487 0 11C0 17.0751 4.92487 22 11 22C17.0751 22 22 17.0751 22 11C22 4.92487 17.0751 0 11 0ZM11 6C10.4477 6 10 6.44772 10 7C10 7.55228 10.4477 8 11 8H11.01C11.5623 8 12.01 7.55228 12.01 7C12.01 6.44772 11.5623 6 11.01 6H11ZM12 11C12 10.4477 11.5523 10 11 10C10.4477 10 10 10.4477 10 11V15C10 15.5523 10.4477 16 11 16C11.5523 16 12 15.5523 12 15V11Z" fill="currentColor"/>
</symbol>
<symbol id="info-outline" viewBox="0 0 22 22" fill="none">
<path d="M11 15V11M11 7H11.01M21 11C21 16.5228 16.5228 21 11 21C5.47715 21 1 16.5228 1 11C1 5.47715 5.47715 1 11 1C16.5228 1 21 5.47715 21 11Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="play-square-outline" viewBox="0 0 20 20" fill="none">
<path d="M7.5 6.96533C7.5 6.48805 7.5 6.24941 7.59974 6.11618C7.68666 6.00007 7.81971 5.92744 7.96438 5.9171C8.13038 5.90525 8.33112 6.03429 8.73261 6.29239L13.4532 9.32706C13.8016 9.55102 13.9758 9.663 14.0359 9.80539C14.0885 9.9298 14.0885 10.0702 14.0359 10.1946C13.9758 10.337 13.8016 10.449 13.4532 10.6729L8.73261 13.7076C8.33112 13.9657 8.13038 14.0948 7.96438 14.0829C7.81971 14.0726 7.68666 13.9999 7.59974 13.8838C7.5 13.7506 7.5 13.512 7.5 13.0347V6.96533Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1 5.8C1 4.11984 1 3.27976 1.32698 2.63803C1.6146 2.07354 2.07354 1.6146 2.63803 1.32698C3.27976 1 4.11984 1 5.8 1H14.2C15.8802 1 16.7202 1 17.362 1.32698C17.9265 1.6146 18.3854 2.07354 18.673 2.63803C19 3.27976 19 4.11984 19 5.8V14.2C19 15.8802 19 16.7202 18.673 17.362C18.3854 17.9265 17.9265 18.3854 17.362 18.673C16.7202 19 15.8802 19 14.2 19H5.8C4.11984 19 3.27976 19 2.63803 18.673C2.07354 18.3854 1.6146 17.9265 1.32698 17.362C1 16.7202 1 15.8802 1 14.2V5.8Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
@ -460,6 +453,16 @@
<symbol id="sats" viewBox="0 0 24 25" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 12.5C21 13.6819 20.7672 14.8522 20.3149 15.9442C19.8626 17.0361 19.1997 18.0282 18.364 18.864C17.5282 19.6997 16.5361 20.3626 15.4442 20.8149C14.3522 21.2672 13.1819 21.5 12 21.5C10.8181 21.5 9.64778 21.2672 8.55585 20.8149C7.46392 20.3626 6.47177 19.6997 5.63604 18.864C4.80031 18.0282 4.13738 17.0361 3.68508 15.9442C3.23279 14.8522 3 13.6819 3 12.5C3 10.1131 3.94821 7.82387 5.63604 6.13604C7.32387 4.44821 9.61305 3.5 12 3.5C14.3869 3.5 16.6761 4.44821 18.364 6.13604C20.0518 7.82387 21 10.1131 21 12.5ZM8.693 9.242L16.33 11.305L16.667 9.843L9.029 7.78L8.693 9.242ZM14.219 6.192L13.813 7.966L12.365 7.574L12.772 5.8L14.219 6.192ZM11.227 19.2L11.635 17.426L10.187 17.035L9.779 18.809L11.227 19.2ZM15.648 14.266L8.011 12.2L8.347 10.738L15.984 12.804L15.648 14.266ZM7.332 15.156L14.97 17.22L15.306 15.758L7.668 13.694L7.332 15.156Z" fill="currentColor"/>
</symbol>
<symbol id="face-smile" viewBox="0 0 24 25" fill="none">
<path d="M8 14.5C8 14.5 9.5 16.5 12 16.5C14.5 16.5 16 14.5 16 14.5M15 9.5H15.01M9 9.5H9.01M22 12.5C22 18.0228 17.5228 22.5 12 22.5C6.47715 22.5 2 18.0228 2 12.5C2 6.97715 6.47715 2.5 12 2.5C17.5228 2.5 22 6.97715 22 12.5ZM15.5 9.5C15.5 9.77614 15.2761 10 15 10C14.7239 10 14.5 9.77614 14.5 9.5C14.5 9.22386 14.7239 9 15 9C15.2761 9 15.5 9.22386 15.5 9.5ZM9.5 9.5C9.5 9.77614 9.27614 10 9 10C8.72386 10 8.5 9.77614 8.5 9.5C8.5 9.22386 8.72386 9 9 9C9.27614 9 9.5 9.22386 9.5 9.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="bar-chart" viewBox="0 0 24 25" fill="none" >
<path d="M9 7.5H4.6C4.03995 7.5 3.75992 7.5 3.54601 7.60899C3.35785 7.70487 3.20487 7.85785 3.10899 8.04601C3 8.25992 3 8.53995 3 9.1V19.9C3 20.4601 3 20.7401 3.10899 20.954C3.20487 21.1422 3.35785 21.2951 3.54601 21.391C3.75992 21.5 4.03995 21.5 4.6 21.5H9M9 21.5H15M9 21.5L9 5.1C9 4.53995 9 4.25992 9.10899 4.04601C9.20487 3.85785 9.35785 3.70487 9.54601 3.60899C9.75992 3.5 10.0399 3.5 10.6 3.5L13.4 3.5C13.9601 3.5 14.2401 3.5 14.454 3.60899C14.6422 3.70487 14.7951 3.85785 14.891 4.04601C15 4.25992 15 4.53995 15 5.1V21.5M15 11.5H19.4C19.9601 11.5 20.2401 11.5 20.454 11.609C20.6422 11.7049 20.7951 11.8578 20.891 12.046C21 12.2599 21 12.5399 21 13.1V19.9C21 20.4601 21 20.7401 20.891 20.954C20.7951 21.1422 20.6422 21.2951 20.454 21.391C20.2401 21.5 19.9601 21.5 19.4 21.5H15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="expand" viewBox="0 0 24 24" fill="none">
<path d="M14 3C14 2.44772 14.4477 2 15 2H21C21.5523 2 22 2.44772 22 3V9C22 9.55229 21.5523 10 21 10C20.4477 10 20 9.55229 20 9V5.41421L14.7071 10.7071C14.3166 11.0976 13.6834 11.0976 13.2929 10.7071C12.9024 10.3166 12.9024 9.68342 13.2929 9.29289L18.5858 4H15C14.4477 4 14 3.55228 14 3Z" fill="currentColor" />
<path d="M5.41421 20L10.7071 14.7071C11.0976 14.3166 11.0976 13.6834 10.7071 13.2929C10.3166 12.9024 9.68342 12.9024 9.29289 13.2929L4 18.5858L4 15C4 14.4477 3.55229 14 3 14C2.44772 14 2 14.4477 2 15V21C2 21.5523 2.44772 22 3 22H9C9.55228 22 10 21.5523 10 21C10 20.4477 9.55228 20 9 20H5.41421Z" fill="currentColor" />
</symbol>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 132 KiB

View File

@ -51,7 +51,7 @@ export function ReBroadcaster({ onClose, ev }: { onClose: () => void; ev: Tagged
return (
<>
<Modal id="broadcaster" className="note-creator-modal" onClose={onClose}>
<Modal id="broadcaster" onClose={onClose}>
{renderRelayCustomisation()}
<div className="flex g8">
<button className="secondary" onClick={onClose}>

View File

@ -1,7 +1,7 @@
import "@webscopeio/react-textarea-autocomplete/style.css";
import "./Textarea.css";
import { NostrPrefix } from "@snort/system";
import { NostrLink } from "@snort/system";
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
import { useIntl } from "react-intl";
import TextareaAutosize from "react-textarea-autosize";
@ -10,7 +10,6 @@ import Avatar from "@/Components/User/Avatar";
import Nip05 from "@/Components/User/Nip05";
import { FuzzySearchResult } from "@/Db/FuzzySearch";
import { userSearch } from "@/Hooks/useProfileSearch";
import { hexToBech32 } from "@/Utils";
import searchEmoji from "@/Utils/emoji-search";
import messages from "../messages";
@ -86,7 +85,7 @@ const Textarea = (props: TextareaProps) => {
afterWhitespace: true,
dataProvider: userDataProvider,
component: (props: { entity: FuzzySearchResult }) => <UserItem {...props.entity} />,
output: (item: { pubkey: string }) => `@${hexToBech32(NostrPrefix.PublicKey, item.pubkey)}`,
output: (item: { pubkey: string }) => `@${NostrLink.profile(item.pubkey).encode()}`,
},
}}
/>

View File

@ -0,0 +1,135 @@
import { NostrEvent } from "@snort/system";
import classNames from "classnames";
import { useEffect, useState } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
import useEventPublisher from "@/Hooks/useEventPublisher";
import useImgProxy from "@/Hooks/useImgProxy";
import useLogin from "@/Hooks/useLogin";
import { useMediaServerList } from "@/Hooks/useMediaServerList";
import { findTag } from "@/Utils";
import { Nip96Uploader } from "@/Utils/Upload/Nip96";
import AsyncButton from "../Button/AsyncButton";
export function MediaServerFileList({
onPicked,
cols,
}: {
onPicked: (files: Array<NostrEvent>) => void;
cols?: number;
}) {
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
const { publisher } = useEventPublisher();
const [fileList, setFilesList] = useState<Array<NostrEvent>>([]);
const [pickedFiles, setPickedFiles] = useState<Array<string>>([]);
const servers = useMediaServerList();
async function listFiles() {
const res = [];
if (!publisher) return;
for (const s of servers.servers) {
try {
const sx = new Nip96Uploader(s, publisher);
const files = await sx.listFiles();
if (files?.files) {
res.push(...files.files);
}
} catch (e) {
console.error(e);
}
}
setFilesList(res);
}
function toggleFile(ev: NostrEvent) {
const hash = findTag(ev, "x");
if (!hash) return;
setPickedFiles(a => {
if (a.includes(hash)) {
return a.filter(a => a != hash);
} else {
return [...a, hash];
}
});
}
useEffect(() => {
listFiles().catch(console.error);
}, [servers.servers.length, state?.version]);
return (
<div>
<div
className={classNames("grid gap-4 my-2", {
"grid-cols-2": cols === 2 || cols === undefined,
"grid-cols-6": cols === 6,
})}>
{fileList.map(a => (
<Nip96File
key={a.id}
file={a}
onClick={() => toggleFile(a)}
checked={pickedFiles.includes(findTag(a, "x") ?? "")}
/>
))}
</div>
<AsyncButton
disabled={pickedFiles.length === 0}
onClick={() => onPicked(fileList.filter(a => pickedFiles.includes(findTag(a, "x") ?? "")))}>
<FormattedMessage defaultMessage="Select" />
</AsyncButton>
</div>
);
}
function Nip96File({ file, checked, onClick }: { file: NostrEvent; checked: boolean; onClick: () => void }) {
const mime = findTag(file, "m");
const url = findTag(file, "url");
const size = findTag(file, "size");
const { proxy } = useImgProxy();
function backgroundImage() {
if (url && (mime?.startsWith("image/") || mime?.startsWith("video/"))) {
return `url(${proxy(url, 512)})`;
}
}
return (
<div onClick={() => onClick()}>
<div
className="relative bg-layer-2 rounded-lg overflow-hidden aspect-square cursor-pointer hover:outline outline-highlight bg-cover bg-center"
style={{
backgroundImage: backgroundImage(),
}}>
<div className="absolute w-full h-full opacity-0 bg-black hover:opacity-80 flex flex-col items-center justify-center gap-4">
<div>{file.content.length === 0 ? <FormattedMessage defaultMessage="Untitled" /> : file.content}</div>
<div>
{Number(size) > 1024 * 1024 && (
<FormattedMessage
defaultMessage="{n}MiB"
values={{
n: <FormattedNumber value={Number(size) / 1024 / 1024} />,
}}
/>
)}
{Number(size) < 1024 * 1024 && (
<FormattedMessage
defaultMessage="{n}KiB"
values={{
n: <FormattedNumber value={Number(size) / 1024} />,
}}
/>
)}
</div>
<div>{new Date(file.created_at * 1000).toLocaleString()}</div>
</div>
<div
className={classNames("w-4 h-4 border border-2 rounded-full right-1 top-1 absolute", {
"bg-zap": checked,
})}
/>
</div>
</div>
);
}

View File

@ -0,0 +1,54 @@
import classNames from "classnames";
import { CSSProperties, ReactNode } from "react";
import { createPortal } from "react-dom";
import IconButton from "@/Components/Button/IconButton";
export default function Flyout({
show,
children,
title,
actions,
onClose,
side,
width,
}: {
show: boolean;
title?: ReactNode;
actions?: ReactNode;
children?: ReactNode;
onClose: () => void;
side: "left" | "right";
width?: string;
}) {
const styles = {
"--flyout-w": width ?? "400px",
transition: "all 0.2s ease-in-out",
width: "var(--flyout-w)",
transform:
side === "right"
? `translate(${show ? "0" : "var(--flyout-w)"},0)`
: `translate(${show ? "0" : "calc(-1 * var(--flyout-w))"},0)`,
} as CSSProperties;
return createPortal(
<div
className={classNames("absolute top-0 overflow-hidden z-50", {
"pointer-events-none": !show,
"right-0": side == "right",
"left-0": side === "left",
})}>
<div className="bg-gray-superdark px-3 py-4 h-[100vh] top-0" style={styles}>
<div className="flex justify-between items-center">
{title}
<div className="flex gap-2 items-center">
{actions}
<IconButton icon={{ name: "x" }} onClick={onClose} />
</div>
</div>
{children}
</div>
</div>,
document.body,
) as React.ReactNode;
}

View File

@ -0,0 +1,48 @@
import { removeUndefined, sanitizeRelayUrl } from "@snort/shared";
import { EventKind, UnknownTag } from "@snort/system";
import { useMemo } from "react";
import { Nip96Uploader } from "@/Utils/Upload/Nip96";
import useEventPublisher from "./useEventPublisher";
import useLogin from "./useLogin";
export const DefaultMediaServers = [
//"https://media.zap.stream",
new UnknownTag(["server", "https://nostr.build/"]),
new UnknownTag(["server", "https://nostrcheck.me/"]),
new UnknownTag(["server", "https://files.v0l.io/"]),
];
export function useMediaServerList() {
const { publisher } = useEventPublisher();
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
let servers = state?.getList(EventKind.StorageServerList) ?? [];
if (servers.length === 0) {
servers = DefaultMediaServers;
}
return useMemo(
() => ({
servers: removeUndefined(servers.map(a => a.toEventTag()))
.filter(a => a[0] === "server")
.map(a => a[1]),
addServer: async (s: string) => {
if (!publisher) return;
const u = sanitizeRelayUrl(s);
if (!u) return;
const server = new Nip96Uploader(u, publisher);
await server.loadInfo();
await state?.addToList(EventKind.StorageServerList, new UnknownTag(["server", u]), true);
},
removeServer: async (s: string) => {
const u = sanitizeRelayUrl(s);
if (!u) return;
await state?.removeFromList(EventKind.StorageServerList, new UnknownTag(["server", u]), true);
},
}),
[servers],
);
}

View File

@ -25,7 +25,7 @@ const MENU_ITEMS: MenuItem[] = [
{
el: (
<div className="flex flex-grow items-center justify-center">
<NoteCreatorButton alwaysShow={true} />
<NoteCreatorButton alwaysShow={true} withModal={true} />
</div>
),
hideReadOnly: true,

View File

@ -140,7 +140,7 @@ export default function NavSidebar({ narrow = false }: { narrow?: boolean }) {
})}
{publicKey ? (
<div className="mt-2">
<NoteCreatorButton alwaysShow={true} showText={!narrow} />
<NoteCreatorButton alwaysShow={true} showText={!narrow} withModal={false} />
</div>
) : (
<div className="mt-2">

View File

@ -22,6 +22,7 @@ interface NoteCreatorDataSnapshot {
sending?: Array<NostrEvent>;
sendStarted: boolean;
hashTags: Array<string>;
filePicker: "hidden" | "compact" | "wide";
reset: () => void;
update: (fn: (v: NoteCreatorDataSnapshot) => void) => void;
}
@ -38,6 +39,7 @@ class NoteCreatorStore extends ExternalStore<NoteCreatorDataSnapshot> {
active: false,
advanced: false,
sendStarted: false,
filePicker: "hidden",
hashTags: [],
reset: () => {
this.#reset(this.#data);
@ -68,6 +70,7 @@ class NoteCreatorStore extends ExternalStore<NoteCreatorDataSnapshot> {
d.sending = undefined;
d.extraTags = undefined;
d.hashTags = [];
d.filePicker = "hidden";
}
takeSnapshot(): NoteCreatorDataSnapshot {

View File

@ -1,11 +1,12 @@
import { base64 } from "@scure/base";
import { throwIfOffline } from "@snort/shared";
import { EventKind, EventPublisher } from "@snort/system";
import { EventKind, EventPublisher, NostrEvent } from "@snort/system";
import { FileExtensionRegex } from "../Const";
import { Uploader, UploadResult } from ".";
import { addExtensionToNip94Url, readNip94Tags, UploadResult } from ".";
export class Nip96Uploader {
#info?: Nip96Info;
export class Nip96Uploader implements Uploader {
constructor(
readonly url: string,
readonly publisher: EventPublisher,
@ -21,67 +22,43 @@ export class Nip96Uploader implements Uploader {
const u = new URL(this.url);
const rsp = await fetch(`${u.protocol}//${u.host}/.well-known/nostr/nip96.json`);
return (await rsp.json()) as Nip96Info;
this.#info = (await rsp.json()) as Nip96Info;
return this.#info;
}
async listFiles(page = 0, count = 50) {
const rsp = await this.#req(`?page=${page}&count=${count}`, "GET");
if (rsp.ok) {
return (await rsp.json()) as Nip96FileList;
}
}
async upload(file: File | Blob, filename: string): Promise<UploadResult> {
throwIfOffline();
const auth = async (url: string, method: string) => {
const auth = await this.publisher.generic(eb => {
return eb.kind(EventKind.HttpAuthentication).tag(["u", url]).tag(["method", method]);
});
return `Nostr ${base64.encode(new TextEncoder().encode(JSON.stringify(auth)))}`;
};
const info = await this.loadInfo();
const fd = new FormData();
fd.append("size", file.size.toString());
fd.append("caption", filename);
fd.append("media_type", file.type);
fd.append("file", file);
let u = info.api_url;
if (u.startsWith("/")) {
u = `${this.url}${u.slice(1)}`;
}
const rsp = await fetch(u, {
body: fd,
method: "POST",
headers: {
accept: "application/json",
authorization: await auth(u, "POST"),
},
});
const rsp = await this.#req("", "POST", fd);
if (rsp.ok) {
throwIfOffline();
const data = (await rsp.json()) as Nip96Result;
if (data.status === "success") {
const dim = data.nip94_event.tags
.find(a => a[0] === "dim")
?.at(1)
?.split("x");
const mime = data.nip94_event.tags.find(a => a[0] === "m")?.at(1) ?? "";
let url = data.nip94_event.tags.find(a => a[0] === "url")?.at(1) ?? "";
if (!url.match(FileExtensionRegex) && mime) {
switch (mime) {
case "image/webp": {
url += ".webp";
break;
}
default: {
url += ".jpg";
break;
}
}
const meta = readNip94Tags(data.nip94_event.tags);
if (
meta.dimensions === undefined ||
meta.dimensions.length !== 2 ||
meta.dimensions[0] === 0 ||
meta.dimensions[1] === 0
) {
return {
error: `Invalid dimensions: "${meta.dimensions?.join("x")}"`,
};
}
return {
url,
metadata: {
width: dim?.at(0) ? Number(dim[0]) : undefined,
height: dim?.at(1) ? Number(dim[1]) : undefined,
blurhash: data.nip94_event.tags.find(a => a[0] === "blurhash")?.at(1),
hash: data.nip94_event.tags.find(a => a[0] === "x")?.at(1),
},
url: addExtensionToNip94Url(meta),
header: data.nip94_event,
metadata: meta,
};
}
return {
@ -101,6 +78,31 @@ export class Nip96Uploader implements Uploader {
}
}
}
async #req(path: string, method: "GET" | "POST" | "DELETE", body?: BodyInit) {
throwIfOffline();
const auth = async (url: string, method: string) => {
const auth = await this.publisher.generic(eb => {
return eb.kind(EventKind.HttpAuthentication).tag(["u", url]).tag(["method", method]);
});
return `Nostr ${base64.encode(new TextEncoder().encode(JSON.stringify(auth)))}`;
};
const info = this.#info ?? (await this.loadInfo());
let u = info.api_url;
if (u.startsWith("/")) {
u = `${this.url}${u.slice(1)}`;
}
u += path;
return await fetch(u, {
method,
body,
headers: {
accept: "application/json",
authorization: await auth(u, method),
},
});
}
}
export interface Nip96Info {
@ -112,8 +114,12 @@ export interface Nip96Result {
status: string;
message: string;
processing_url?: string;
nip94_event: {
tags: Array<Array<string>>;
content: string;
};
nip94_event: NostrEvent;
}
export interface Nip96FileList {
count: number;
total: number;
page: number;
files: Array<NostrEvent>;
}

View File

@ -7,13 +7,29 @@ import useEventPublisher from "@/Hooks/useEventPublisher";
import useLogin from "@/Hooks/useLogin";
import usePreferences from "@/Hooks/usePreferences";
import { bech32ToHex, randomSample, unwrap } from "@/Utils";
import { KieranPubKey } from "@/Utils/Const";
import { FileExtensionRegex, KieranPubKey } from "@/Utils/Const";
import NostrBuild from "@/Utils/Upload/NostrBuild";
import NostrImg from "@/Utils/Upload/NostrImg";
import VoidCat from "@/Utils/Upload/VoidCat";
import { Nip96Uploader } from "./Nip96";
export interface Nip94Tags {
url?: string;
mimeType?: string;
hash?: string;
originalHash?: string;
size?: number;
dimensions?: [number, number];
magnet?: string;
blurHash?: string;
thumb?: string;
image?: Array<string>;
summary?: string;
alt?: string;
fallback?: Array<string>;
}
export interface UploadResult {
url?: string;
error?: string;
@ -26,12 +42,7 @@ export interface UploadResult {
/**
* Media metadata
*/
metadata?: {
blurhash?: string;
width?: number;
height?: number;
hash?: string;
};
metadata?: Nip94Tags;
}
/**
@ -181,3 +192,124 @@ export const ProgressStream = (file: File | Blob, progress: (n: number) => void)
);
return rsBase;
};
export function addExtensionToNip94Url(meta: Nip94Tags) {
if (!meta.url?.match(FileExtensionRegex) && meta.mimeType) {
switch (meta.mimeType) {
case "image/webp": {
return `${meta.url}.webp`;
}
case "image/jpeg":
case "image/jpg": {
return `${meta.url}.jpg`;
}
case "video/mp4": {
return `${meta.url}.mp4`;
}
}
}
return meta.url;
}
/**
* Read NIP-94 tags from `imeta` tag
*/
export function readNip94TagsFromIMeta(tag: Array<string>) {
const asTags = tag.slice(1).map(a => a.split(" ", 2));
return readNip94Tags(asTags);
}
/**
* Read NIP-94 tags from event tags
*/
export function readNip94Tags(tags: Array<Array<string>>) {
const res: Nip94Tags = {};
for (const tx of tags) {
const [k, v] = tx;
switch (k) {
case "url": {
res.url = v;
break;
}
case "m": {
res.mimeType = v;
break;
}
case "x": {
res.hash = v;
break;
}
case "ox": {
res.originalHash = v;
break;
}
case "size": {
res.size = Number(v);
break;
}
case "dim": {
res.dimensions = v.split("x").map(Number) as [number, number];
break;
}
case "magnet": {
res.magnet = v;
break;
}
case "blurhash": {
res.blurHash = v;
break;
}
case "thumb": {
res.thumb = v;
break;
}
case "image": {
res.image ??= [];
res.image.push(v);
break;
}
case "summary": {
res.summary = v;
break;
}
case "alt": {
res.alt = v;
break;
}
case "fallback": {
res.fallback ??= [];
res.fallback.push(v);
break;
}
}
}
return res;
}
export function nip94TagsToIMeta(meta: Nip94Tags) {
const ret: Array<string> = ["imeta"];
const ifPush = (key: string, value?: string | number) => {
if (value) {
ret.push(`${key} ${value}`);
}
};
ifPush("url", meta.url);
ifPush("m", meta.mimeType);
ifPush("x", meta.hash);
ifPush("ox", meta.originalHash);
ifPush("size", meta.size);
ifPush("dim", meta.dimensions?.join("x"));
ifPush("magnet", meta.magnet);
ifPush("blurhash", meta.blurHash);
ifPush("thumb", meta.thumb);
ifPush("summary", meta.summary);
ifPush("alt", meta.alt);
if (meta.image) {
meta.image.forEach(a => ifPush("image", a));
}
if (meta.fallback) {
meta.fallback.forEach(a => ifPush("fallback", a));
}
return ret;
}

View File

@ -780,12 +780,6 @@ button.tall {
}
}
.note-creator-modal .rta__textarea {
/* Fix width calculation to account for 32px padding on input */
width: calc(100% - 32px) !important;
font-size: 15px !important;
}
.ctx-menu {
color: var(--font-color) !important;
background: transparent !important;
@ -802,11 +796,14 @@ button.tall {
background: #1e1e1e !important;
padding: 8px 24px !important;
display: grid !important;
grid-template-columns: 2rem auto !important;
font-size: 16px;
font-weight: 600;
}
.ctx-menu:not(.no-icons) li {
grid-template-columns: 2rem auto !important;
}
.light .ctx-menu li {
background: #fff !important;
}

View File

@ -59,6 +59,9 @@
"/d6vEc": {
"defaultMessage": "Make your profile easier to find and share"
},
"/ioUrF": {
"defaultMessage": "From File"
},
"/n5KSF": {
"defaultMessage": "{n} ms"
},
@ -192,6 +195,9 @@
"3gOsZq": {
"defaultMessage": "Translators"
},
"3kbIhS": {
"defaultMessage": "Untitled"
},
"3qnJlS": {
"defaultMessage": "You are voting with {amount} sats"
},
@ -1163,6 +1169,9 @@
"W9355R": {
"defaultMessage": "Unmute"
},
"WeLEuL": {
"defaultMessage": "From Server"
},
"WmZhfL": {
"defaultMessage": "Automatically translate notes to your local language"
},
@ -1226,6 +1235,9 @@
"Z7kkeJ": {
"defaultMessage": "Delegated Event Signing"
},
"ZFe9tl": {
"defaultMessage": "Compose a note"
},
"ZKORll": {
"defaultMessage": "Activate Now"
},
@ -1307,6 +1319,9 @@
"c35bj2": {
"defaultMessage": "If you have an enquiry about your NIP-05 order please DM {link}"
},
"c3LlRO": {
"defaultMessage": "{n}KiB"
},
"c3g2hL": {
"defaultMessage": "Broadcast Again"
},
@ -1455,6 +1470,9 @@
"fsB/4p": {
"defaultMessage": "Saved"
},
"fucxlm": {
"defaultMessage": "Attach Media"
},
"furjvW": {
"defaultMessage": "Watch Stream"
},
@ -1611,6 +1629,9 @@
"kJYo0u": {
"defaultMessage": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}"
},
"kQAf2d": {
"defaultMessage": "Select"
},
"kaaf1E": {
"defaultMessage": "now"
},
@ -1686,6 +1707,9 @@
"n1Whvj": {
"defaultMessage": "Switch"
},
"n8k1SG": {
"defaultMessage": "{n}MiB"
},
"nDejmx": {
"defaultMessage": "Unblock"
},

View File

@ -19,6 +19,7 @@
"/Xf4UW": "Send anonymous usage metrics",
"/clOBU": "Weekly",
"/d6vEc": "Make your profile easier to find and share",
"/ioUrF": "From File",
"/n5KSF": "{n} ms",
"00LcfG": "Load more",
"01iNut": "Nostr address does not belong to you",
@ -63,6 +64,7 @@
"3QwfJR": "~{amount}",
"3cc4Ct": "Light",
"3gOsZq": "Translators",
"3kbIhS": "Untitled",
"3qnJlS": "You are voting with {amount} sats",
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
"3tVy+Z": "{n} Followers",
@ -385,6 +387,7 @@
"W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}",
"W2PiAr": "{n} Blocked",
"W9355R": "Unmute",
"WeLEuL": "From Server",
"WmZhfL": "Automatically translate notes to your local language",
"WvGmZT": "npub / nprofile / nostr address",
"X6tipZ": "Sign in with key",
@ -406,6 +409,7 @@
"Yf3DwC": "Connect a wallet to send instant payments",
"Z4BMCZ": "Enter pairing phrase",
"Z7kkeJ": "Delegated Event Signing",
"ZFe9tl": "Compose a note",
"ZKORll": "Activate Now",
"ZLmyG9": "Contributors",
"ZS+jRE": "Send zap splits to",
@ -433,6 +437,7 @@
"bxv59V": "Just now",
"c+JYNI": "No thanks",
"c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}",
"c3LlRO": "{n}KiB",
"c3g2hL": "Broadcast Again",
"cFbU1B": "Using Alby? Go to {link} to get your NWC config!",
"cG/bKQ": "Native nostr wallet connection",
@ -482,6 +487,7 @@
"flnGvv": "What's on your mind?",
"fqwcJ1": "On-chain Donation",
"fsB/4p": "Saved",
"fucxlm": "Attach Media",
"furjvW": "Watch Stream",
"g5pX+a": "About",
"g985Wp": "Failed to send vote",
@ -534,6 +540,7 @@
"k9SQm1": "Relays which you have connected to before and appear to be reliable.",
"kEZUR8": "Register an Iris username",
"kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}",
"kQAf2d": "Select",
"kaaf1E": "now",
"kc79d3": "Topics",
"kqPQJD": "Configure zap pool",
@ -559,6 +566,7 @@
"mOFG3K": "Start",
"mfe8RW": "Option: {n}",
"n1Whvj": "Switch",
"n8k1SG": "{n}MiB",
"nDejmx": "Unblock",
"nGBrvw": "Bookmarks",
"nGGDsi": "Notifications Allowed",

View File

@ -16,6 +16,7 @@ module.exports = {
warning: "var(--warning)",
error: "var(--error)",
success: "var(--success)",
"gray-superlight": "var(--gray-superlight)",
"gray-light": "var(--gray-light)",
"gray-medium": "var(--gray-medium)",
gray: "var(--gray)",