diff --git a/src/components/ConfirmModal/ConfirmModal.module.scss b/src/components/ConfirmModal/ConfirmModal.module.scss index b20f9d4..0636a19 100644 --- a/src/components/ConfirmModal/ConfirmModal.module.scss +++ b/src/components/ConfirmModal/ConfirmModal.module.scss @@ -1,5 +1,3 @@ - - .feedsRestoreModal { position: fixed; width: 420px; diff --git a/src/components/CustomZap/CustomZap.module.scss b/src/components/CustomZap/CustomZap.module.scss index e95d8e4..d5352a7 100644 --- a/src/components/CustomZap/CustomZap.module.scss +++ b/src/components/CustomZap/CustomZap.module.scss @@ -109,6 +109,10 @@ justify-content: flex-start; align-items: center; + .emoji, .amount { + margin-right: 6px; + } + .sats { color: var(--text-tertiary); font-size: 12px; diff --git a/src/components/CustomZap/CustomZap.tsx b/src/components/CustomZap/CustomZap.tsx index 81b89c7..fc62285 100644 --- a/src/components/CustomZap/CustomZap.tsx +++ b/src/components/CustomZap/CustomZap.tsx @@ -1,12 +1,13 @@ import { useIntl } from '@cookbook/solid-intl'; import { Component, createEffect, createSignal, For } from 'solid-js'; +import { defaultZap, defaultZapOptions } from '../../constants'; import { useAccountContext } from '../../contexts/AccountContext'; import { useSettingsContext } from '../../contexts/SettingsContext'; import { hookForDev } from '../../lib/devTools'; import { zapNote } from '../../lib/zap'; import { userName } from '../../stores/profile'; import { toastZapFail, zapCustomOption, actions as tActions, placeholders as tPlaceholders } from '../../translations'; -import { PrimalNote } from '../../types/primal'; +import { PrimalNote, ZapOption } from '../../types/primal'; import { debounce } from '../../utils'; import ButtonPrimary from '../Buttons/ButtonPrimary'; import Modal from '../Modal/Modal'; @@ -19,9 +20,9 @@ const CustomZap: Component<{ id?: string, open?: boolean, note: PrimalNote, - onConfirm: (amount?: number) => void, - onSuccess: (amount?: number) => void, - onFail: (amount?: number) => void + onConfirm: (zapOption?: ZapOption) => void, + onSuccess: (zapOption?: ZapOption) => void, + onFail: (zapOption?: ZapOption) => void }> = (props) => { const toast = useToastContext(); @@ -29,14 +30,17 @@ const CustomZap: Component<{ const intl = useIntl(); const settings = useSettingsContext(); - const [selectedValue, setSelectedValue] = createSignal(settings?.availableZapOptions[0] || 10); - const [comment, setComment] = createSignal(''); + const [selectedValue, setSelectedValue] = createSignal(settings?.availableZapOptions[0] || defaultZapOptions[0]); + const [comment, setComment] = createSignal(defaultZapOptions[0].message || ''); createEffect(() => { - setSelectedValue(settings?.availableZapOptions[0] || 10) + setSelectedValue(settings?.availableZapOptions[0] || defaultZapOptions[0]) }); - const isSelected = (value: number) => value === selectedValue(); + const isSelected = (value: ZapOption) => { + const sel = selectedValue(); + return value.amount === sel.amount && value.emoji === sel.emoji && value.message === sel.message; + }; const truncateNumber = (amount: number) => { const t = 1000; @@ -78,7 +82,7 @@ const CustomZap: Component<{ const success = await zapNote( props.note, account.publicKey, - selectedValue(), + selectedValue().amount || 0, comment(), account.relays, ); @@ -105,7 +109,7 @@ const CustomZap: Component<{ {intl.formatMessage(tActions.zap)} - @@ -115,7 +119,7 @@ const CustomZap: Component<{ })} - {truncateNumber(selectedValue())} + {truncateNumber(selectedValue().amount || 0)} sats @@ -125,10 +129,19 @@ const CustomZap: Component<{ {(value) => } diff --git a/src/components/EmojiPicker/EmojiPicker.module.scss b/src/components/EmojiPicker/EmojiPicker.module.scss new file mode 100644 index 0000000..85e005f --- /dev/null +++ b/src/components/EmojiPicker/EmojiPicker.module.scss @@ -0,0 +1,34 @@ +.emojiSuggestions { + position: relative; + display: grid; + grid-template-columns: 50px 50px 50px 50px 50px 50px; + width: 322px; + max-height: 200px; + overflow-y: scroll; + padding: 4px; + background-color: var(--background-input); + border: none; + border-radius: 8px; + + .emojiOption { + margin-bottom: 5px; + padding: 2px; + background: none; + font-size: 16px; + line-height: 20px; + font-weight: 400; + border: none; + display: flex; + justify-content: center; + align-items: center; + + &:hover, &.highlight { + background-color: var(--text-tertiary-2); + } + + &:focus { + outline: none; + border: none; + } + } +} diff --git a/src/components/EmojiPicker/EmojiPicker.tsx b/src/components/EmojiPicker/EmojiPicker.tsx new file mode 100644 index 0000000..d564f4b --- /dev/null +++ b/src/components/EmojiPicker/EmojiPicker.tsx @@ -0,0 +1,143 @@ +import { Component, createEffect, createSignal, For, onCleanup, onMount } from 'solid-js'; + +import styles from './EmojiPicker.module.scss'; +import { useSettingsContext } from '../../contexts/SettingsContext'; +import { debounce, isVisibleInContainer, uuidv4 } from '../../utils'; +import { useIntl } from '@cookbook/solid-intl'; +import ConfirmModal from '../ConfirmModal/ConfirmModal'; +import { settings as t } from '../../translations'; +import { hookForDev } from '../../lib/devTools'; +import ButtonLink from '../Buttons/ButtonLink'; +import Modal from '../Modal/Modal'; + +import emojiSearch from '@jukben/emoji-search'; +import { createStore } from 'solid-js/store'; +import { EmojiOption } from '../../types/primal'; +import ButtonPrimary from '../Buttons/ButtonPrimary'; + +const EmojiPicker: Component<{ id?: string, filter: string, onSelect: (emoji: EmojiOption) => void }> = (props) => { + const [emojiResults, setEmojiResults] = createStore([]); + const [highlightedEmoji, setHighlightedEmoji] = createSignal(0); + let emojiOptions: HTMLDivElement | undefined; + const instanceId = uuidv4(); + + const emojiChangeKeyboard = (e: KeyboardEvent) => { + if (e.code === 'ArrowDown') { + e.preventDefault(); + setHighlightedEmoji(i => { + if (emojiResults.length === 0) { + return 0; + } + + return i < emojiResults.length - 7 ? i + 6 : 0; + }); + + const emojiHolder = document.getElementById(`${instanceId}-${highlightedEmoji()}`); + + if (emojiHolder && emojiOptions && !isVisibleInContainer(emojiHolder, emojiOptions)) { + emojiHolder.scrollIntoView({ block: 'end', behavior: 'smooth' }); + } + + return; + } + + if (e.code === 'ArrowUp') { + e.preventDefault(); + setHighlightedEmoji(i => { + if (emojiResults.length === 0) { + return 0; + } + + return i >= 6 ? i - 6 : emojiResults.length - 1; + }); + + const emojiHolder = document.getElementById(`${instanceId}-${highlightedEmoji()}`); + + if (emojiHolder && emojiOptions && !isVisibleInContainer(emojiHolder, emojiOptions)) { + emojiHolder.scrollIntoView({ block: 'start', behavior: 'smooth' }); + } + + return; + } + + if (e.code === 'ArrowRight') { + e.preventDefault(); + setHighlightedEmoji(i => { + if (emojiResults.length === 0) { + return 0; + } + + return i < emojiResults.length - 1 ? i + 1 : 0; + }); + + const emojiHolder = document.getElementById(`${instanceId}-${highlightedEmoji()}`); + + if (emojiHolder && emojiOptions && !isVisibleInContainer(emojiHolder, emojiOptions)) { + emojiHolder.scrollIntoView({ block: 'end', behavior: 'smooth' }); + } + + return; + } + + if (e.code === 'ArrowLeft') { + e.preventDefault(); + setHighlightedEmoji(i => { + if (emojiResults.length === 0) { + return 0; + } + + return i > 0 ? i - 1 : emojiResults.length - 1; + }); + + const emojiHolder = document.getElementById(`${instanceId}-${highlightedEmoji()}`); + + if (emojiHolder && emojiOptions && !isVisibleInContainer(emojiHolder, emojiOptions)) { + emojiHolder.scrollIntoView({ block: 'start', behavior: 'smooth' }); + } + + return; + } + + if (['Enter', 'Space'].includes(e.code)) { + props.onSelect(emojiResults[highlightedEmoji()]); + return; + } + }; + + onMount(() => { + window.addEventListener('keydown', emojiChangeKeyboard); + }); + + onCleanup(() => { + window.removeEventListener('keydown', emojiChangeKeyboard); + }); + + createEffect(() => { + const val = props.filter.trim(); + + setEmojiResults(emojiSearch(val)); + }); + + return ( +
+ + {(emoji, index) => ( + + )} + +
+ ); +} + +export default hookForDev(EmojiPicker); diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index c30a0fb..ca90530 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -9,12 +9,17 @@ const Modal: Component<{ open?: boolean, id?: string, opaqueBackdrop?: boolean, + onBackdropClick?: () => void, }> = (props) => { return ( -
+
{props.children}
diff --git a/src/components/NewNote/EditBox/EditBox.tsx b/src/components/NewNote/EditBox/EditBox.tsx index 7f70aec..ef87041 100644 --- a/src/components/NewNote/EditBox/EditBox.tsx +++ b/src/components/NewNote/EditBox/EditBox.tsx @@ -269,6 +269,7 @@ const EditBox: Component<{ if (lastEmojiTrigger < 0 || cursor - lastEmojiTrigger <= 1) { setEmojiInput(false); + setEmojiQuery(''); return false; } diff --git a/src/components/Note/NoteFooter/NoteFooter.tsx b/src/components/Note/NoteFooter/NoteFooter.tsx index 1446a40..b5237a9 100644 --- a/src/components/Note/NoteFooter/NoteFooter.tsx +++ b/src/components/Note/NoteFooter/NoteFooter.tsx @@ -1,5 +1,5 @@ import { Component, createEffect, createSignal, Show } from 'solid-js'; -import { MenuItem, PrimalNote } from '../../../types/primal'; +import { MenuItem, PrimalNote, ZapOption } from '../../../types/primal'; import { sendRepost } from '../../../lib/notes'; import styles from './NoteFooter.module.scss'; @@ -252,17 +252,17 @@ const NoteFooter: Component<{ note: PrimalNote, wide?: boolean, id?: string }> = return; } - setZappedAmount(() => settings?.defaultZapAmount || 0); + setZappedAmount(() => settings?.defaultZap.amount || 0); setZappedNow(true); animateZap(); - const success = await zapNote(props.note, account.publicKey, settings?.defaultZapAmount || 10, '', account.relays); + const success = await zapNote(props.note, account.publicKey, settings?.defaultZap.amount || 10, settings?.defaultZap.message || '', account.relays); setIsZapping(false); if (success) { return; } - setZappedAmount(() => -(settings?.defaultZapAmount || 0)); + setZappedAmount(() => -(settings?.defaultZap.amount || 0)); setZappedNow(true); setZapped(props.note.post.noteActions.zapped); } @@ -413,14 +413,14 @@ const NoteFooter: Component<{ note: PrimalNote, wide?: boolean, id?: string }> = { + onConfirm={(zapOption: ZapOption) => { setIsCustomZap(false); - setZappedAmount(() => amount || 0); + setZappedAmount(() => zapOption.amount || 0); setZappedNow(true); setZapped(true); animateZap(); }} - onSuccess={(amount: number) => { + onSuccess={(zapOption: ZapOption) => { setIsCustomZap(false); setIsZapping(false); setZappedNow(false); @@ -428,8 +428,8 @@ const NoteFooter: Component<{ note: PrimalNote, wide?: boolean, id?: string }> = setHideZapIcon(false); setZapped(true); }} - onFail={(amount: number) => { - setZappedAmount(() => -(amount || 0)); + onFail={(zapOption: ZapOption) => { + setZappedAmount(() => -(zapOption.amount || 0)); setZappedNow(true); setIsCustomZap(false); setIsZapping(false); diff --git a/src/components/SettingsZap/SettingsZap.module.scss b/src/components/SettingsZap/SettingsZap.module.scss index 33fbe82..decbbcf 100644 --- a/src/components/SettingsZap/SettingsZap.module.scss +++ b/src/components/SettingsZap/SettingsZap.module.scss @@ -6,11 +6,12 @@ margin-bottom: 20px; } .zapOptions { - width: 396px; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-column-gap: 12px; - grid-row-gap: 12px; + display: flex; + flex-direction: column; + width: 100%; + .zapInput { + margin-bottom: 8px; + } } } @@ -22,31 +23,180 @@ margin-bottom: 20px; } -input.zapInput { - display: flex; - align-items: center; - justify-content: center; - width: 120px; +.zapInput { + width: 100%; height: 36px; - text-align: center; - margin: 0; - padding: 0; - padding-top: 4px; - font-size: 16px; - font-weight: 600; - line-height: 20px; - color: var(--text-primary); background-color: var(--background-header-input); border: none; border-radius: 18px; + display: flex; + align-items: center; + justify-content: flex-start; - &:focus { - background-color: var(--subtile-devider); + .optEmoji { + display: flex; + align-items: center; + justify-content: center; + width: 120px; + height: 36px; + border-radius: 18px 0 0 18px; + margin: 0; + padding-bottom: 0; + padding-top: 4px; + padding-inline: 16px; + font-size: 16px; + font-weight: 600; + line-height: 20px; + color: var(--text-primary); + background: none; + border: none; + + &:hover { + background-color: var(--subtile-devider); + outline: none; + box-shadow: none; + } + } + + input { + display: flex; + align-items: center; + justify-content: center; + width: 120px; + height: 36px; + text-align: center; + margin: 0; + padding-bottom: 0; + padding-top: 4px; + padding-inline: 16px; + font-size: 16px; + font-weight: 600; + line-height: 20px; + color: var(--text-primary); + background: none; + border: none; + + &.defAmount { + border-radius: 18px 0 0 18px; + } + &.defMessage { + border-radius: 0 18px 18px 0; + width: 100%; + justify-content: flex-start; + text-align: left; + } + + + &.optAmount { + border-radius: 0; + } + &.optMessage { + border-radius: 0 18px 18px 0; + width: 100%; + justify-content: flex-start; + text-align: left; + } + + &:focus, &:hover { + background-color: var(--subtile-devider); + outline: none; + box-shadow: none; + } } } + .restoreZaps { display: flex; justify-content: flex-start; margin-top: 36px; } + +.zapEmojiChangeModal { + position: fixed; + width: 420px; + height: 344px; + color: var(--text-secondary); + background-color: var(--background-input); + border: 1px solid transparent; + border-radius: 6px; + + display: flex; + flex-direction: column; + padding: 22px; + + justify-content: flex-start; + align-items: center; + + input { + width: 220px; + height: 36px; + text-align: left; + margin: 0; + margin-bottom: 24px; + padding-bottom: 0; + padding-top: 4px; + padding-inline: 16px; + font-size: 16px; + font-weight: 600; + line-height: 20px; + color: var(--text-primary); + background: none; + border: none; + border-radius: 18px; + + &:focus { + background-color: var(--subtile-devider); + outline: none; + box-shadow: none; + } + + &::placeholder { + color: var(--text-tertiary); + font-size: 16px; + font-weight: 400; + line-height: 20px; + } + } + + .title { + font-weight: 800; + font-size: 18px; + line-height: 18px; + color: var(--text-secondary); + text-transform: uppercase; + margin-bottom: 20px; + } + + .xClose { + background: none; + border: none; + margin: 0; + padding: 0; + width: fit-content; + + position: absolute; + top: 16px; + right: 18px; + + .iconClose { + width: 14px; + height: 14px; + display: inline-block; + margin: 0px 0px; + background-color: var(--text-secondary); + -webkit-mask: url(../../assets/icons/close.svg) no-repeat center; + mask: url(../../assets/icons/close.svg) no-repeat center; + } + + &:hover { + .iconClose { + background-color: var(--text-primary); + } + } + + &:focus { + box-shadow: none; + } + } +} diff --git a/src/components/SettingsZap/SettingsZap.tsx b/src/components/SettingsZap/SettingsZap.tsx index 2b6801c..e8b8c87 100644 --- a/src/components/SettingsZap/SettingsZap.tsx +++ b/src/components/SettingsZap/SettingsZap.tsx @@ -1,13 +1,20 @@ -import { Component, createSignal, For } from 'solid-js'; +import { Component, createEffect, createSignal, For } from 'solid-js'; import styles from './SettingsZap.module.scss'; import { useSettingsContext } from '../../contexts/SettingsContext'; -import { debounce } from '../../utils'; +import { debounce, isVisibleInContainer, uuidv4 } from '../../utils'; import { useIntl } from '@cookbook/solid-intl'; import ConfirmModal from '../ConfirmModal/ConfirmModal'; import { settings as t } from '../../translations'; import { hookForDev } from '../../lib/devTools'; import ButtonLink from '../Buttons/ButtonLink'; +import Modal from '../Modal/Modal'; + +import emojiSearch from '@jukben/emoji-search'; +import { createStore } from 'solid-js/store'; +import { EmojiOption } from '../../types/primal'; +import ButtonPrimary from '../Buttons/ButtonPrimary'; +import EmojiPicker from '../EmojiPicker/EmojiPicker'; const SettingsZap: Component<{ id?: string }> = (props) => { @@ -17,49 +24,110 @@ const SettingsZap: Component<{ id?: string }> = (props) => { const [isRestoringZaps, setIsRestoringZaps] = createSignal(false); + const [isEmojiChange, setIsEmojiChange] = createSignal(-1); + + const [emojiSearchTerm, setEmojiSearchTerm] = createSignal('smile'); + const onRestoreZaps = () => { settings?.actions.resetZapOptionsToDefault(); setIsRestoringZaps(false); }; - const changeDefaultZap = (e: InputEvent) => { + const changeDefaultZapAmount = (e: InputEvent) => { debounce(() => { const target = e.target as HTMLInputElement; - const val = parseInt(target.value); + const amount = parseInt(target.value); - if (isNaN(val)) { + if (isNaN(amount)) { return; } - settings?.actions.setDefaultZapAmount(val); + settings?.actions.setDefaultZapAmount({ amount }); }, 500) }; - const changeZapOptions = (e: InputEvent, index: number) => { + const changeDefaultZapMessage = (e: InputEvent) => { debounce(() => { const target = e.target as HTMLInputElement; - const val = parseInt(target.value); + const message = target.value; - if (isNaN(val)) { + settings?.actions.setDefaultZapAmount({ message }); + }, 500) + }; + + const changeZapOptionAmount = (e: InputEvent, index: number) => { + debounce(() => { + const target = e.target as HTMLInputElement; + const amount = parseInt(target.value); + + if (isNaN(amount)) { return; } - settings?.actions.setZapOptions(val, index); + settings?.actions.setZapOptions({ amount }, index); }, 500); }; + const changeZapOptionMessage = (e: InputEvent, index: number) => { + debounce(() => { + const target = e.target as HTMLInputElement; + const message = target.value; + + settings?.actions.setZapOptions({ message }, index); + }, 500); + }; + + const changeZapOptionEmoji = (emojiOption: EmojiOption) => { + if (isEmojiChange() < 0) return; + + settings?.actions.setZapOptions({ emoji: emojiOption.name }, isEmojiChange()); + setIsEmojiChange(-1); + }; + + const onKey = (e: KeyboardEvent) => { + if (e.code === 'Escape') { + setIsEmojiChange(-1); + return; + } + }; + + let emojiInput: HTMLInputElement | undefined; + + createEffect(() => { + if (isEmojiChange() >= 0) { + window.addEventListener('keydown', onKey); + setTimeout(() => { + setEmojiSearchTerm(() => 'smile') + emojiInput?.focus(); + }, 10); + } + else { + window.removeEventListener('keydown', onKey); + } + }) + return (
Set default zap amount:
- + > + + +
@@ -67,13 +135,27 @@ const SettingsZap: Component<{ id?: string }> = (props) => {
- {(value, index) => - changeZapOptions(e, index())} - /> + {(option, index) => +
+ + changeZapOptionAmount(e, index())} + /> + changeZapOptionMessage(e, index())} + /> +
}
@@ -93,6 +175,36 @@ const SettingsZap: Component<{ id?: string }> = (props) => { onConfirm={onRestoreZaps} onAbort={() => setIsRestoringZaps(false)} /> + + = 0} + onBackdropClick={() => setIsEmojiChange(-1)} + > +
+
+ {intl.formatMessage(t.zapEmojiFilterTitle)} +
+ + + + { + const target = e.target as HTMLInputElement; + setEmojiSearchTerm(() => target.value); + }} + placeholder={intl.formatMessage(t.zapEmojiFilterPlaceholder)} + > + + + +
+
); } diff --git a/src/constants.ts b/src/constants.ts index 2687614..1d0d5db 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,8 +1,6 @@ -import { ContentModeration, FeedPage, Filterlist, } from "./types/primal"; +import { ContentModeration, FeedPage, } from "./types/primal"; import logoFire from './assets/icons/logo_fire.svg'; import logoIce from './assets/icons/logo_ice.svg'; -import { MessageDescriptor } from "@cookbook/solid-intl"; -import { linkPreviews } from "./lib/notes"; export const second = 1000; export const minute = 60 * second; @@ -308,15 +306,15 @@ export const apkLink = `https://github.com/PrimalHQ/primal-android-app/releases/ // ---------------------------------------- -export const defaultZapAmount = 42; +export const defaultZap = { "amount": 42, "message": "Onward 🫡" }; export const defaultZapOptions = [ - 21, - 420, - 1_000, - 5_000, - 10_000, - 100_000, + { emoji: '👍', amount: 21, message: 'Great post 👍' }, + { emoji: '🚀', amount: 420, message: 'Let\'s go 🚀' }, + { emoji: '☕', amount: 1_000, message: 'Coffie on me ☕' }, + { emoji: '🍻', amount: 5_000, message: 'Cheers 🍻' }, + { emoji: '🍷', amount: 10_000, message: 'Party time 🍷' }, + { emoji: '👑', amount: 100_000, message: 'Generational wealth 👑' }, ]; export const contentScope = 'content'; diff --git a/src/contexts/SettingsContext.tsx b/src/contexts/SettingsContext.tsx index eb96226..a610820 100644 --- a/src/contexts/SettingsContext.tsx +++ b/src/contexts/SettingsContext.tsx @@ -1,6 +1,6 @@ import { createStore } from "solid-js/store"; import { useToastContext } from "../components/Toaster/Toaster"; -import { contentScope, defaultContentModeration, defaultFeeds, defaultNotificationSettings, defaultZapAmount, defaultZapOptions, nostrHighlights, themes, trendingFeed, trendingScope } from "../constants"; +import { contentScope, defaultContentModeration, defaultFeeds, defaultNotificationSettings, defaultZap, defaultZapOptions, nostrHighlights, themes, trendingFeed, trendingScope } from "../constants"; import { createContext, createEffect, @@ -20,6 +20,7 @@ import { ContextChildren, PrimalFeed, PrimalTheme, + ZapOption, } from "../types/primal"; import { initAvailableFeeds, @@ -35,8 +36,6 @@ import { APP_ID } from "../App"; import { useIntl } from "@cookbook/solid-intl"; import { hexToNpub } from "../lib/keys"; import { settings as t } from "../translations"; -import { getFilterlists } from "../lib/profile"; - export type SettingsContextStore = { locale: string, @@ -44,8 +43,8 @@ export type SettingsContextStore = { themes: PrimalTheme[], availableFeeds: PrimalFeed[], defaultFeed: PrimalFeed, - defaultZapAmount: number, - availableZapOptions: number[], + defaultZap: ZapOption, + availableZapOptions: ZapOption[], notificationSettings: Record, applyContentModeration: boolean, contentModeration: ContentModeration[], @@ -58,8 +57,8 @@ export type SettingsContextStore = { renameAvailableFeed: (feed: PrimalFeed, newName: string) => void, saveSettings: () => void, loadSettings: (pubkey: string) => void, - setDefaultZapAmount: (amount: number) => void, - setZapOptions: (amount:number, index: number) => void, + setDefaultZapAmount: (option: ZapOption, temp?: boolean) => void, + setZapOptions: (option: ZapOption, index: number, temp?: boolean) => void, resetZapOptionsToDefault: (temp?: boolean) => void, updateNotificationSettings: (key: string, value: boolean, temp?: boolean) => void, restoreDefaultFeeds: () => void, @@ -74,7 +73,7 @@ export const initialData = { themes, availableFeeds: [], defaultFeed: defaultFeeds[0], - defaultZapAmount: defaultZapAmount, + defaultZap: defaultZap, availableZapOptions: defaultZapOptions, notificationSettings: { ...defaultNotificationSettings }, applyContentModeration: true, @@ -92,13 +91,13 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { // ACTIONS -------------------------------------- - const setDefaultZapAmount = (amount: number, temp?: boolean) => { - updateStore('defaultZapAmount', () => amount); + const setDefaultZapAmount = (option: ZapOption, temp?: boolean) => { + updateStore('defaultZap', () => option); !temp && saveSettings(); }; - const setZapOptions = (amount: number, index: number, temp?: boolean) => { - updateStore('availableZapOptions', index, () => amount); + const setZapOptions = (option: ZapOption, index: number, temp?: boolean) => { + updateStore('availableZapOptions', index, () => ({ ...option })); !temp && saveSettings(); }; @@ -111,11 +110,11 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { try { const settings = JSON.parse(content?.content); - let options = settings.zapOptions as number[]; - let amount = settings.defaultZapAmount as number; + let options = settings.zapConfig; + let amount = settings.zapDefault; updateStore('availableZapOptions', () => options); - updateStore('defaultZapAmount', () => amount); + updateStore('defaultZap', () => amount); !temp && saveSettings(); } @@ -289,8 +288,8 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { const settings = { theme: store.theme, feeds: store.availableFeeds, - defaultZapAmount: store.defaultZapAmount, - zapOptions: store.availableZapOptions, + defaultZap: store.defaultZap, + zapConfig: store.availableZapOptions, notifications: store.notificationSettings, applyContentModeration: store.applyContentModeration, contentModeration: store.contentModeration, @@ -336,11 +335,10 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { updateStore('notificationSettings', () => ({ ...notificationSettings } || { ...defaultNotificationSettings })); updateStore('applyContentModeration', () => true); + let zapOptions = settings.zapConfig; + let zapAmount = settings.zapDefault; - let zapOptions = settings.zapOptions as number[]; - let zapAmount = settings.defaultZapAmount as number; - - updateStore('defaultZapAmount', () => zapAmount); + updateStore('defaultZap', () => zapAmount); updateStore('availableZapOptions', () => zapOptions); } catch (e) { @@ -378,16 +376,16 @@ export const SettingsProvider = (props: { children: ContextChildren }) => { const { theme, feeds, - defaultZapAmount, - zapOptions, + zapDefault, + zapConfig, notifications, applyContentModeration, contentModeration, } = JSON.parse(content?.content); theme && setThemeByName(theme, true); - defaultZapAmount && setDefaultZapAmount(defaultZapAmount, true); - zapOptions && updateStore('availableZapOptions', () => zapOptions); + zapDefault && setDefaultZapAmount(zapDefault, true); + zapConfig && updateStore('availableZapOptions', () => zapConfig); if (notifications) { updateStore('notificationSettings', () => ({ ...notifications })); diff --git a/src/translations.ts b/src/translations.ts index ed84c9e..e424297 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -1429,6 +1429,16 @@ export const settings = { defaultMessage: 'This action will restore all your zap settings to their default values', description: 'Label explaining the impact of restoring default zaps', }, + zapEmojiFilterTitle: { + id: 'settings.zapEmojiFilterTitle', + defaultMessage: 'Select an emoji', + description: 'Title for the select emoji modal', + }, + zapEmojiFilterPlaceholder: { + id: 'settings.zapEmojiFilterPlaceholder', + defaultMessage: 'Type to filter...', + description: 'Placeholder for the emoji modal filter', + }, feedLatest: { id: 'feeds.feedLatest', defaultMessage: 'Latest', diff --git a/src/types/primal.d.ts b/src/types/primal.d.ts index 31e379a..2be1fb3 100644 --- a/src/types/primal.d.ts +++ b/src/types/primal.d.ts @@ -623,3 +623,9 @@ export type SelectionOption = { } export type NotificationGroup = 'all' | 'zaps' | 'replies' | 'mentions' | 'reposts'; + +export type ZapOption = { + emoji?: string, + amount?: number, + message?: string, +};