Browse Source

change defaults and fix toasts

imwald
Silberengel 2 weeks ago
parent
commit
c431c6eed5
  1. 11
      src/components/NoteList/index.tsx
  2. 4
      src/constants.ts
  3. 11
      src/i18n/locales/de.ts
  4. 11
      src/i18n/locales/en.ts
  5. 13
      src/lib/publishing-feedback.tsx
  6. 33
      src/pages/secondary/PostSettingsPage/PublishSuccessToastSetting.tsx
  7. 64
      src/pages/secondary/PostSettingsPage/PublishingFeedbackSettings.tsx
  8. 4
      src/pages/secondary/PostSettingsPage/index.tsx
  9. 28
      src/services/local-storage.service.ts

11
src/components/NoteList/index.tsx

@ -52,6 +52,7 @@ import dayjs from 'dayjs'
import { type Event, type Filter, kinds } from 'nostr-tools' import { type Event, type Filter, kinds } from 'nostr-tools'
import { decode } from 'nostr-tools/nip19' import { decode } from 'nostr-tools/nip19'
import RelayStatusDisplay from '@/components/RelayStatusDisplay' import RelayStatusDisplay from '@/components/RelayStatusDisplay'
import { detailedPublishToastsEnabled } from '@/lib/publishing-feedback'
import { import {
relayOpTerminalRowsToTimelineRelayUiStatuses, relayOpTerminalRowsToTimelineRelayUiStatuses,
type RelayOpTerminalRow type RelayOpTerminalRow
@ -69,7 +70,6 @@ import {
type ReactNode, type ReactNode,
type SetStateAction type SetStateAction
} from 'react' } from 'react'
import { CircleAlert } from 'lucide-react'
import { useLongPressAction } from '@/hooks/use-long-press-action' import { useLongPressAction } from '@/hooks/use-long-press-action'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import PullToRefresh from 'react-simple-pull-to-refresh' import PullToRefresh from 'react-simple-pull-to-refresh'
@ -3926,15 +3926,12 @@ const NoteList = forwardRef(
const title = t( const title = t(
'Relays returned no events for this feed. They may be offline, slow, or not indexing these notes.' 'Relays returned no events for this feed. They may be offline, slow, or not indexing these notes.'
) )
if (uiStatuses.length === 0) { if (uiStatuses.length === 0 || !detailedPublishToastsEnabled()) {
toast.error(title, { duration: 8000 }) toast.error(title, { duration: 8000 })
} else { } else {
toast.error( toast.error(
<div className="w-full min-w-0"> <div className="w-full min-w-0">
<div className="flex items-center gap-2 mb-3"> <div className="font-semibold mb-3">{title}</div>
<CircleAlert className="w-5 h-5 text-red-500 shrink-0" />
<div className="font-semibold">{title}</div>
</div>
<div className="text-xs text-muted-foreground mb-2"> <div className="text-xs text-muted-foreground mb-2">
{t('Per-relay timeline results ({{count}} connections)', { {t('Per-relay timeline results ({{count}} connections)', {
count: uiStatuses.length count: uiStatuses.length
@ -3947,7 +3944,7 @@ const NoteList = forwardRef(
aggregateSummary={false} aggregateSummary={false}
/> />
</div>, </div>,
{ duration: 12_000, className: 'max-w-lg w-full' } { duration: 12_000, className: 'max-w-lg w-full', icon: null }
) )
} }
}, debounceMs) }, debounceMs)

4
src/constants.ts

@ -390,8 +390,10 @@ export const StorageKey = {
ADD_RANDOM_RELAYS_TO_PUBLISH: 'addRandomRelaysToPublish', ADD_RANDOM_RELAYS_TO_PUBLISH: 'addRandomRelaysToPublish',
/** When `'true'`, only connect to relays on the viewer's NIP-65 / favorites / cache / HTTP lists. */ /** When `'true'`, only connect to relays on the viewer's NIP-65 / favorites / cache / HTTP lists. */
RESTRICT_RELAYS_TO_METADATA_LISTS: 'restrictRelaysToMetadataLists', RESTRICT_RELAYS_TO_METADATA_LISTS: 'restrictRelaysToMetadataLists',
/** When not `'false'`, show green Sonner toasts after successful publishes (default on). */ /** When `'true'`, show Sonner toasts after successful publishes (default off). */
SHOW_PUBLISH_SUCCESS_TOASTS: 'showPublishSuccessToasts', SHOW_PUBLISH_SUCCESS_TOASTS: 'showPublishSuccessToasts',
/** When not `'false'`, publish/feed toasts include per-relay breakdown when success toasts are on (default on). */
SHOW_DETAILED_PUBLISH_TOASTS: 'showDetailedPublishToasts',
/** When not `'false'`, show NIP-53 live activity banner (default on). */ /** When not `'false'`, show NIP-53 live activity banner (default on). */
SHOW_LIVE_ACTIVITIES_BANNER: 'showLiveActivitiesBanner', SHOW_LIVE_ACTIVITIES_BANNER: 'showLiveActivitiesBanner',
/** Max approximate archive size (MB). `0` in UI means “use platform default”. */ /** Max approximate archive size (MB). `0` in UI means “use platform default”. */

11
src/i18n/locales/de.ts

@ -757,9 +757,14 @@ export default {
'Favorited by': 'Favorisiert von', 'Favorited by': 'Favorisiert von',
'Post settings': 'Beitragseinstellungen', 'Post settings': 'Beitragseinstellungen',
'Publishing feedback': 'Rückmeldungen beim Veröffentlichen', 'Publishing feedback': 'Rückmeldungen beim Veröffentlichen',
'Publish success toasts': 'Erfolgs-Benachrichtigungen beim Veröffentlichen', 'Publish success toasts': 'Erfolg beim Veröffentlichen anzeigen',
'Show green notifications when posts, replies, reactions, and other publishes succeed. When off, a small checkmark appears briefly at the bottom-right instead. Errors and failures still use a toast.': 'Publish success toasts hint':
'Grüne Hinweise anzeigen, wenn Beiträge, Antworten, Reaktionen und andere Veröffentlichungen gelingen. Wenn aus, erscheint kurz ein kleines Häkchen unten rechts. Fehler weiterhin als Hinweis.', 'Wenn an, bestätigt ein Hinweis gelungene Beiträge, Antworten, Reaktionen und ähnliche Aktionen. Wenn aus, erscheint kurz ein kleines Häkchen unten rechts.',
'Publish toast per-relay details': 'Pro-Relay-Aufschlüsselung in Hinweisen',
'Publish toast per-relay details hint':
'Wenn an, listet der Hinweis jedes Relay (angenommen, fehlgeschlagen, Fehlertext). Wenn aus, nur eine kurze Zusammenfassung.',
'Publishing feedback errors note':
'Fehlgeschlagene Veröffentlichungen und andere Fehler zeigen immer einen Hinweis — mit Kurzfassung oder Pro-Relay-Aufschlüsselung wie oben.',
'Publish successful': 'Veröffentlichung erfolgreich', 'Publish successful': 'Veröffentlichung erfolgreich',
'Media upload service': 'Medien-Upload-Service', 'Media upload service': 'Medien-Upload-Service',
BlossomUploadYourListOption: 'Blossom (eigene Liste)', BlossomUploadYourListOption: 'Blossom (eigene Liste)',

11
src/i18n/locales/en.ts

@ -756,9 +756,14 @@ export default {
'Favorited by': 'Favorited by', 'Favorited by': 'Favorited by',
'Post settings': 'Post settings', 'Post settings': 'Post settings',
'Publishing feedback': 'Publishing feedback', 'Publishing feedback': 'Publishing feedback',
'Publish success toasts': 'Publish success toasts', 'Publish success toasts': 'Success notifications when publishing',
'Show green notifications when posts, replies, reactions, and other publishes succeed. When off, a small checkmark appears briefly at the bottom-right instead. Errors and failures still use a toast.': 'Publish success toasts hint':
'Show green notifications when posts, replies, reactions, and other publishes succeed. When off, a small checkmark appears briefly at the bottom-right instead. Errors and failures still use a toast.', 'When on, a toast confirms successful posts, replies, reactions, and similar actions. When off, a small checkmark appears briefly at the bottom-right instead.',
'Publish toast per-relay details': 'Per-relay breakdown in toasts',
'Publish toast per-relay details hint':
'When on, those toasts list each relay (accepted, failed, errors). When off, only a short summary line.',
'Publishing feedback errors note':
'Failed publishes and other errors always show a toast, using the same summary or per-relay style as above.',
'Publish successful': 'Publish successful', 'Publish successful': 'Publish successful',
'Media upload service': 'Media upload service', 'Media upload service': 'Media upload service',
BlossomUploadYourListOption: 'Blossom (your list)', BlossomUploadYourListOption: 'Blossom (your list)',

13
src/lib/publishing-feedback.tsx

@ -23,6 +23,11 @@ function publishSuccessToastsEnabled(): boolean {
return storage.getShowPublishSuccessToasts() return storage.getShowPublishSuccessToasts()
} }
/** Per-relay toast panels only when success toasts are on and the nested setting is enabled. */
export function detailedPublishToastsEnabled(): boolean {
return publishSuccessToastsEnabled() && storage.getShowDetailedPublishToasts()
}
function resolvePromiseSuccessLabel(success: string | (() => ReactNode)): string | undefined { function resolvePromiseSuccessLabel(success: string | (() => ReactNode)): string | undefined {
if (typeof success === 'string') return success if (typeof success === 'string') return success
try { try {
@ -124,9 +129,15 @@ export function showPublishingFeedback(
const toastFunction = isSuccess ? toast.success : toast.error const toastFunction = isSuccess ? toast.success : toast.error
if (!detailedPublishToastsEnabled()) {
toastFunction(message, { duration: isSuccess ? 2000 : duration })
return
}
toastFunction(<PublishToastRelayPanel message={message} result={result} />, { toastFunction(<PublishToastRelayPanel message={message} result={result} />, {
duration, duration,
className: 'max-w-lg w-full' className: 'max-w-lg w-full',
icon: null
}) })
} }

33
src/pages/secondary/PostSettingsPage/PublishSuccessToastSetting.tsx

@ -1,33 +0,0 @@
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import storage from '@/services/local-storage.service'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function PublishSuccessToastSetting() {
const { t } = useTranslation()
const [enabled, setEnabled] = useState(true)
useEffect(() => {
setEnabled(storage.getShowPublishSuccessToasts())
}, [])
const onChange = (checked: boolean) => {
setEnabled(checked)
storage.setShowPublishSuccessToasts(checked)
}
return (
<div className="space-y-2">
<div className="flex items-center space-x-2">
<Label htmlFor="publish-success-toasts">{t('Publish success toasts')}</Label>
<Switch id="publish-success-toasts" checked={enabled} onCheckedChange={onChange} />
</div>
<div className="text-muted-foreground text-xs max-w-xl">
{t(
'Show green notifications when posts, replies, reactions, and other publishes succeed. When off, a small checkmark appears briefly at the bottom-right instead. Errors and failures still use a toast.'
)}
</div>
</div>
)
}

64
src/pages/secondary/PostSettingsPage/PublishingFeedbackSettings.tsx

@ -0,0 +1,64 @@
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import storage from '@/services/local-storage.service'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function PublishingFeedbackSettings() {
const { t } = useTranslation()
const [successToasts, setSuccessToasts] = useState(false)
const [detailedToasts, setDetailedToasts] = useState(true)
useEffect(() => {
setSuccessToasts(storage.getShowPublishSuccessToasts())
setDetailedToasts(storage.getShowDetailedPublishToasts())
}, [])
const onSuccessChange = (checked: boolean) => {
setSuccessToasts(checked)
storage.setShowPublishSuccessToasts(checked)
}
const onDetailedChange = (checked: boolean) => {
setDetailedToasts(checked)
storage.setShowDetailedPublishToasts(checked)
}
return (
<div className="space-y-4">
<div className="space-y-2">
<div className="flex items-center space-x-2">
<Label htmlFor="publish-success-toasts" className="text-base font-normal">
{t('Publish success toasts')}
</Label>
<Switch id="publish-success-toasts" checked={successToasts} onCheckedChange={onSuccessChange} />
</div>
<p className="text-muted-foreground text-xs max-w-xl">
{t('Publish success toasts hint')}
</p>
</div>
{successToasts ? (
<div className="space-y-2 pl-4 border-l-2 border-border">
<div className="flex items-center space-x-2">
<Label htmlFor="detailed-publish-toasts" className="text-base font-normal">
{t('Publish toast per-relay details')}
</Label>
<Switch
id="detailed-publish-toasts"
checked={detailedToasts}
onCheckedChange={onDetailedChange}
/>
</div>
<p className="text-muted-foreground text-xs max-w-xl">
{t('Publish toast per-relay details hint')}
</p>
</div>
) : null}
<p className="text-muted-foreground text-xs max-w-xl border-t border-border pt-3">
{t('Publishing feedback errors note')}
</p>
</div>
)
}

4
src/pages/secondary/PostSettingsPage/index.tsx

@ -5,7 +5,7 @@ import { forwardRef, useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import MediaUploadServiceSetting from './MediaUploadServiceSetting' import MediaUploadServiceSetting from './MediaUploadServiceSetting'
import ExpirationSettings from './ExpirationSettings' import ExpirationSettings from './ExpirationSettings'
import PublishSuccessToastSetting from './PublishSuccessToastSetting' import PublishingFeedbackSettings from './PublishingFeedbackSettings'
const PostSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index?: number; hideTitlebar?: boolean }, ref) => { const PostSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index?: number; hideTitlebar?: boolean }, ref) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -33,7 +33,7 @@ const PostSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index?:
<MediaUploadServiceSetting /> <MediaUploadServiceSetting />
<div className="space-y-4"> <div className="space-y-4">
<h3 className="text-lg font-medium">{t('Publishing feedback')}</h3> <h3 className="text-lg font-medium">{t('Publishing feedback')}</h3>
<PublishSuccessToastSetting /> <PublishingFeedbackSettings />
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<h3 className="text-lg font-medium">{t('Expiration Tags')}</h3> <h3 className="text-lg font-medium">{t('Expiration Tags')}</h3>

28
src/services/local-storage.service.ts

@ -75,6 +75,7 @@ const SETTINGS_KEYS = [
StorageKey.SHOW_RECOMMENDED_RELAYS_PANEL, StorageKey.SHOW_RECOMMENDED_RELAYS_PANEL,
StorageKey.ADD_RANDOM_RELAYS_TO_PUBLISH, StorageKey.ADD_RANDOM_RELAYS_TO_PUBLISH,
StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS, StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS,
StorageKey.SHOW_DETAILED_PUBLISH_TOASTS,
StorageKey.SHOW_LIVE_ACTIVITIES_BANNER, StorageKey.SHOW_LIVE_ACTIVITIES_BANNER,
StorageKey.DEFAULT_EXPIRATION_ENABLED, StorageKey.DEFAULT_EXPIRATION_ENABLED,
StorageKey.DEFAULT_EXPIRATION_MONTHS, StorageKey.DEFAULT_EXPIRATION_MONTHS,
@ -119,8 +120,9 @@ class LocalStorageService {
private defaultExpirationMonths: number = 6 private defaultExpirationMonths: number = 6
private showRssFeed: boolean = true private showRssFeed: boolean = true
private panelMode: 'single' | 'double' = 'single' private panelMode: 'single' | 'double' = 'single'
private addRandomRelaysToPublish: boolean = false private addRandomRelaysToPublish: boolean = true
private showPublishSuccessToasts: boolean = true private showPublishSuccessToasts: boolean = false
private showDetailedPublishToasts: boolean = true
private showLiveActivitiesBanner: boolean = true private showLiveActivitiesBanner: boolean = true
private restrictRelaysToMetadataLists: boolean = false private restrictRelaysToMetadataLists: boolean = false
@ -405,10 +407,14 @@ class LocalStorageService {
this.panelMode = panelModeStr === 'double' ? 'double' : 'single' // Default to 'single' this.panelMode = panelModeStr === 'double' ? 'double' : 'single' // Default to 'single'
const addRandomRelaysStr = window.localStorage.getItem(StorageKey.ADD_RANDOM_RELAYS_TO_PUBLISH) const addRandomRelaysStr = window.localStorage.getItem(StorageKey.ADD_RANDOM_RELAYS_TO_PUBLISH)
this.addRandomRelaysToPublish = addRandomRelaysStr === null ? false : addRandomRelaysStr === 'true' this.addRandomRelaysToPublish = addRandomRelaysStr === null ? true : addRandomRelaysStr === 'true'
const showPublishSuccessStr = window.localStorage.getItem(StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS) const showPublishSuccessStr = window.localStorage.getItem(StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS)
this.showPublishSuccessToasts = showPublishSuccessStr !== 'false' this.showPublishSuccessToasts = showPublishSuccessStr === 'true'
const showDetailedPublishStr = window.localStorage.getItem(StorageKey.SHOW_DETAILED_PUBLISH_TOASTS)
this.showDetailedPublishToasts =
showDetailedPublishStr === null ? true : showDetailedPublishStr === 'true'
const showLiveActivitiesStr = window.localStorage.getItem(StorageKey.SHOW_LIVE_ACTIVITIES_BANNER) const showLiveActivitiesStr = window.localStorage.getItem(StorageKey.SHOW_LIVE_ACTIVITIES_BANNER)
this.showLiveActivitiesBanner = showLiveActivitiesStr !== 'false' this.showLiveActivitiesBanner = showLiveActivitiesStr !== 'false'
@ -572,9 +578,12 @@ class LocalStorageService {
this.defaultShowNsfw = get(StorageKey.DEFAULT_SHOW_NSFW) === 'true' this.defaultShowNsfw = get(StorageKey.DEFAULT_SHOW_NSFW) === 'true'
this.dismissedTooManyRelaysAlert = get(StorageKey.DISMISSED_TOO_MANY_RELAYS_ALERT) === 'true' this.dismissedTooManyRelaysAlert = get(StorageKey.DISMISSED_TOO_MANY_RELAYS_ALERT) === 'true'
this.showRecommendedRelaysPanel = get(StorageKey.SHOW_RECOMMENDED_RELAYS_PANEL) === 'true' this.showRecommendedRelaysPanel = get(StorageKey.SHOW_RECOMMENDED_RELAYS_PANEL) === 'true'
this.addRandomRelaysToPublish = get(StorageKey.ADD_RANDOM_RELAYS_TO_PUBLISH) === 'true' const addRandomRelaysStr = get(StorageKey.ADD_RANDOM_RELAYS_TO_PUBLISH)
if (addRandomRelaysStr != null) this.addRandomRelaysToPublish = addRandomRelaysStr === 'true'
const showPublishSuccessStr = get(StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS) const showPublishSuccessStr = get(StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS)
if (showPublishSuccessStr != null) this.showPublishSuccessToasts = showPublishSuccessStr !== 'false' if (showPublishSuccessStr != null) this.showPublishSuccessToasts = showPublishSuccessStr !== 'false'
const showDetailedPublishStr = get(StorageKey.SHOW_DETAILED_PUBLISH_TOASTS)
if (showDetailedPublishStr != null) this.showDetailedPublishToasts = showDetailedPublishStr === 'true'
const showLiveActivitiesStr = get(StorageKey.SHOW_LIVE_ACTIVITIES_BANNER) const showLiveActivitiesStr = get(StorageKey.SHOW_LIVE_ACTIVITIES_BANNER)
if (showLiveActivitiesStr != null) this.showLiveActivitiesBanner = showLiveActivitiesStr !== 'false' if (showLiveActivitiesStr != null) this.showLiveActivitiesBanner = showLiveActivitiesStr !== 'false'
const showKindsStr = get(StorageKey.SHOW_KINDS) const showKindsStr = get(StorageKey.SHOW_KINDS)
@ -1004,6 +1013,15 @@ class LocalStorageService {
this.persistSetting(StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS, show.toString()) this.persistSetting(StorageKey.SHOW_PUBLISH_SUCCESS_TOASTS, show.toString())
} }
getShowDetailedPublishToasts(): boolean {
return this.showDetailedPublishToasts
}
setShowDetailedPublishToasts(show: boolean) {
this.showDetailedPublishToasts = show
this.persistSetting(StorageKey.SHOW_DETAILED_PUBLISH_TOASTS, show.toString())
}
getPanelMode(): 'single' | 'double' { getPanelMode(): 'single' | 'double' {
return this.panelMode return this.panelMode
} }

Loading…
Cancel
Save