From 13e0e0d8013e409af6220d7ac33831fe4e0b4e0a Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Tue, 6 Jan 2026 14:17:55 +0100 Subject: [PATCH] lint fix wip --- components/AlbyInstaller.tsx | 8 +- components/ArticleCard.tsx | 6 +- components/ArticleEditor.tsx | 4 +- components/ArticleEditorForm.tsx | 94 +-- components/ArticleField.tsx | 6 +- components/ArticleFilters.tsx | 8 +- components/ArticleFormButtons.tsx | 2 +- components/ArticlePages.tsx | 55 ++ components/ArticlePreview.tsx | 4 +- components/ArticleReviews.tsx | 135 +++-- components/ArticlesList.tsx | 8 +- components/AuthorCard.tsx | 2 +- components/AuthorFilter.tsx | 6 +- components/AuthorFilterButton.tsx | 10 +- components/AuthorFilterDropdown.tsx | 10 +- components/AuthorFilterHooks.tsx | 10 +- components/AuthorsList.tsx | 8 +- components/CacheUpdateManager.tsx | 106 ++++ components/CategorySelect.tsx | 2 +- components/CategoryTabs.tsx | 2 +- components/ClearButton.tsx | 2 +- components/ConditionalPublishButton.tsx | 8 +- components/ConnectButton.tsx | 18 +- components/ConnectedUserMenu.tsx | 2 +- components/CreateAccountModal.tsx | 30 +- components/CreateAccountModalComponents.tsx | 10 +- components/CreateAccountModalSteps.tsx | 6 +- components/CreateSeriesModal.tsx | 6 +- components/DocsContent.tsx | 2 +- components/DocsSidebar.tsx | 2 +- components/Footer.tsx | 2 +- components/FundingGauge.tsx | 14 +- components/HomeView.tsx | 22 +- components/ImageUploadField.tsx | 25 +- components/KeyIndicator.tsx | 2 +- components/KeyManagementManager.tsx | 14 +- components/LanguageSelector.tsx | 8 +- components/MarkdownEditor.tsx | 10 +- components/MarkdownEditorTwoColumns.tsx | 294 ++++++++++ components/Nip95ConfigManager.tsx | 14 +- components/NotificationActions.tsx | 2 +- components/NotificationBadge.tsx | 2 +- components/NotificationBadgeButton.tsx | 2 +- components/NotificationCenter.tsx | 2 +- components/NotificationContent.tsx | 2 +- components/NotificationItem.tsx | 4 +- components/NotificationPanel.tsx | 4 +- components/NotificationPanelHeader.tsx | 2 +- components/PageHeader.tsx | 63 +- components/PaymentModal.tsx | 10 +- components/PresentationFormHeader.tsx | 2 +- components/ProfileArticlesHeader.tsx | 2 +- components/ProfileArticlesSection.tsx | 2 +- components/ProfileArticlesSummary.tsx | 2 +- components/ProfileBackButton.tsx | 2 +- components/ProfileHeader.tsx | 4 +- components/ProfileSeriesBlock.tsx | 2 +- components/ProfileView.tsx | 10 +- components/ReviewForm.tsx | 166 ++++++ components/ReviewTipForm.tsx | 140 +++++ components/SearchBar.tsx | 2 +- components/SearchIcon.tsx | 2 +- components/SeriesCard.tsx | 2 +- components/SeriesList.tsx | 2 +- components/SeriesSection.tsx | 8 +- components/SeriesStats.tsx | 2 +- components/SponsoringForm.tsx | 161 ++++++ components/UnlockAccountModal.tsx | 27 +- components/UserArticles.tsx | 88 ++- components/UserArticlesEditPanel.tsx | 2 +- components/UserArticlesList.tsx | 22 +- components/UserProfile.tsx | 4 +- components/UserProfileHeader.tsx | 2 +- docs/en/faq.md | 294 ++++++++++ docs/en/fees-and-contributions.md | 19 + docs/en/payment-guide.md | 279 +++++++++ docs/en/publishing-guide.md | 253 ++++++++ docs/en/user-guide.md | 319 ++++++++++ docs/fr/DOCUMENTATION.md | 133 +++++ docs/fr/README-DEPLOYMENT.md | 200 +++++++ docs/fr/README.md | 75 +++ docs/fr/STRICT_CONFIG_SUMMARY.md | 75 +++ docs/fr/deployment.md | 611 ++++++++++++++++++++ docs/fr/faq.md | 294 ++++++++++ docs/fr/fees-and-contributions.md | 16 + docs/fr/payment-guide.md | 279 +++++++++ docs/fr/publishing-guide.md | 253 ++++++++ docs/fr/quick-reference.md | 66 +++ docs/fr/remaining-tasks.md | 26 + docs/fr/scripts-reference.md | 372 ++++++++++++ docs/fr/tag-system-explanation.md | 169 ++++++ docs/fr/technical.md | 143 +++++ docs/fr/user-guide.md | 319 ++++++++++ docs/fr/wording.md | 291 ++++++++++ hooks/useDocs.ts | 5 +- hooks/useNotificationCenter.ts | 14 +- lib/articleInvoice.ts | 1 + lib/articleMutations.ts | 8 + lib/articlePublisherHelpersPresentation.ts | 64 +- lib/articlePublisherTypes.ts | 3 +- lib/articleQueries.ts | 12 +- lib/authorQueries.ts | 110 ++++ lib/metadataExtractor.ts | 4 + lib/nostr.ts | 14 +- lib/nostrEventParsing.ts | 263 ++++++++- lib/nostrSubscription.ts | 8 +- lib/nostrTagSystemBuild.ts | 29 +- lib/nostrTagSystemTypes.ts | 16 +- lib/notifications.ts | 11 +- lib/objectCache.ts | 113 +++- lib/payment.ts | 4 +- lib/paymentNotes.ts | 238 ++++++++ lib/paymentPollingMain.ts | 38 ++ lib/platformConfig.ts | 6 +- lib/purchaseQueries.ts | 172 ++++++ lib/reviewTipQueries.ts | 177 ++++++ lib/reviews.ts | 17 +- lib/seriesQueries.ts | 65 ++- lib/settingsCache.ts | 130 +++++ lib/sponsoring.ts | 8 +- lib/sponsoringQueries.ts | 137 +++++ lib/urlGenerator.ts | 31 +- lib/userContentSync.ts | 241 +++++++- lib/zapReceiptQueries.ts | 45 ++ lib/zapRequestBuilder.ts | 69 +++ locales/en.txt | 5 + locales/fr.txt | 5 + pages/api/docs/[file].ts | 13 +- pages/author/[pubkey].tsx | 107 +++- pages/docs.tsx | 4 +- pages/funding.tsx | 32 + pages/index.tsx | 2 +- pages/legal.tsx | 20 +- pages/presentation.tsx | 6 +- pages/privacy.tsx | 28 +- pages/profile.tsx | 2 +- pages/publish.tsx | 10 +- pages/purchase/[id].tsx | 86 +++ pages/review-tip/[id].tsx | 92 +++ pages/series/[id].tsx | 10 +- pages/settings.tsx | 6 +- pages/sponsoring/[id].tsx | 93 +++ pages/terms.tsx | 26 +- public/locales/en.txt | 77 +++ public/locales/fr.txt | 77 +++ tsconfig.json | 4 + types/nostr.ts | 86 ++- 147 files changed, 8572 insertions(+), 484 deletions(-) create mode 100644 components/ArticlePages.tsx create mode 100644 components/CacheUpdateManager.tsx create mode 100644 components/MarkdownEditorTwoColumns.tsx create mode 100644 components/ReviewForm.tsx create mode 100644 components/ReviewTipForm.tsx create mode 100644 components/SponsoringForm.tsx create mode 100644 docs/en/faq.md create mode 100644 docs/en/fees-and-contributions.md create mode 100644 docs/en/payment-guide.md create mode 100644 docs/en/publishing-guide.md create mode 100644 docs/en/user-guide.md create mode 100644 docs/fr/DOCUMENTATION.md create mode 100644 docs/fr/README-DEPLOYMENT.md create mode 100644 docs/fr/README.md create mode 100644 docs/fr/STRICT_CONFIG_SUMMARY.md create mode 100644 docs/fr/deployment.md create mode 100644 docs/fr/faq.md create mode 100644 docs/fr/fees-and-contributions.md create mode 100644 docs/fr/payment-guide.md create mode 100644 docs/fr/publishing-guide.md create mode 100644 docs/fr/quick-reference.md create mode 100644 docs/fr/remaining-tasks.md create mode 100644 docs/fr/scripts-reference.md create mode 100644 docs/fr/tag-system-explanation.md create mode 100644 docs/fr/technical.md create mode 100644 docs/fr/user-guide.md create mode 100644 docs/fr/wording.md create mode 100644 lib/authorQueries.ts create mode 100644 lib/paymentNotes.ts create mode 100644 lib/purchaseQueries.ts create mode 100644 lib/reviewTipQueries.ts create mode 100644 lib/settingsCache.ts create mode 100644 lib/sponsoringQueries.ts create mode 100644 lib/zapReceiptQueries.ts create mode 100644 lib/zapRequestBuilder.ts create mode 100644 pages/funding.tsx create mode 100644 pages/purchase/[id].tsx create mode 100644 pages/review-tip/[id].tsx create mode 100644 pages/sponsoring/[id].tsx diff --git a/components/AlbyInstaller.tsx b/components/AlbyInstaller.tsx index a425df5..7eb5ac7 100644 --- a/components/AlbyInstaller.tsx +++ b/components/AlbyInstaller.tsx @@ -5,7 +5,7 @@ interface AlbyInstallerProps { onInstalled?: () => void } -function InfoIcon(): JSX.Element { +function InfoIcon(): React.ReactElement { return ( void } -function InstallerActions({ onInstalled, markInstalled }: InstallerActionsProps): JSX.Element { +function InstallerActions({ onInstalled, markInstalled }: InstallerActionsProps): React.ReactElement { const connect = useCallback(() => { const alby = getAlbyService() void alby.enable().then(() => { @@ -60,7 +60,7 @@ function InstallerActions({ onInstalled, markInstalled }: InstallerActionsProps) ) } -function InstallerBody({ onInstalled, markInstalled }: InstallerActionsProps): JSX.Element { +function InstallerBody({ onInstalled, markInstalled }: InstallerActionsProps): React.ReactElement { return (

Alby Extension Required

@@ -108,7 +108,7 @@ function useAlbyStatus(onInstalled?: () => void): { isInstalled: boolean; isChec return { isInstalled, isChecking, markInstalled } } -export function AlbyInstaller({ onInstalled }: AlbyInstallerProps): JSX.Element | null { +export function AlbyInstaller({ onInstalled }: AlbyInstallerProps): React.ReactElement | null { const { isInstalled, isChecking, markInstalled } = useAlbyStatus(onInstalled) if (isChecking || isInstalled) { diff --git a/components/ArticleCard.tsx b/components/ArticleCard.tsx index 31de68e..f34c58e 100644 --- a/components/ArticleCard.tsx +++ b/components/ArticleCard.tsx @@ -11,7 +11,7 @@ interface ArticleCardProps { onUnlock?: (article: Article) => void } -function ArticleHeader({ article }: { article: Article }): JSX.Element { +function ArticleHeader({ article }: { article: Article }): React.ReactElement { return (

{article.title}

@@ -37,7 +37,7 @@ function ArticleMeta({ paymentInvoice: ReturnType['paymentInvoice'] onClose: () => void onPaymentComplete: () => void -}): JSX.Element { +}): React.ReactElement { return ( <> {error &&

{error}

} @@ -55,7 +55,7 @@ function ArticleMeta({ ) } -export function ArticleCard({ article, onUnlock }: ArticleCardProps): JSX.Element { +export function ArticleCard({ article, onUnlock }: ArticleCardProps): React.ReactElement { const { pubkey, connect } = useNostrAuth() const { loading, diff --git a/components/ArticleEditor.tsx b/components/ArticleEditor.tsx index f50c52f..370ee55 100644 --- a/components/ArticleEditor.tsx +++ b/components/ArticleEditor.tsx @@ -12,7 +12,7 @@ interface ArticleEditorProps { } -function SuccessMessage(): JSX.Element { +function SuccessMessage(): React.ReactElement { return (

Article Published!

@@ -21,7 +21,7 @@ function SuccessMessage(): JSX.Element { ) } -export function ArticleEditor({ onPublishSuccess, onCancel, seriesOptions, onSelectSeries }: ArticleEditorProps): JSX.Element { +export function ArticleEditor({ onPublishSuccess, onCancel, seriesOptions, onSelectSeries }: ArticleEditorProps): React.ReactElement { const { connected, pubkey, connect } = useNostrAuth() const { loading, error, success, publishArticle } = useArticlePublishing(pubkey ?? null) const [draft, setDraft] = useState({ diff --git a/components/ArticleEditorForm.tsx b/components/ArticleEditorForm.tsx index d3ec9b1..dc7feb1 100644 --- a/components/ArticleEditorForm.tsx +++ b/components/ArticleEditorForm.tsx @@ -5,6 +5,7 @@ import { ArticleField } from './ArticleField' import { ArticleFormButtons } from './ArticleFormButtons' import { CategorySelect } from './CategorySelect' import { MarkdownEditor } from './MarkdownEditor' +import { MarkdownEditorTwoColumns } from './MarkdownEditorTwoColumns' import type { MediaRef } from '@/types/nostr' import { t } from '@/lib/i18n' @@ -25,7 +26,7 @@ function CategoryField({ }: { value: ArticleDraft['category'] onChange: (value: import('@/types/nostr').ArticleCategory | undefined) => void -}): JSX.Element { +}): React.ReactElement { return ( void seriesOptions?: { id: string; title: string }[] | undefined onSelectSeries?: ((seriesId: string | undefined) => void) | undefined -}): JSX.Element => ( +}): React.ReactElement => (
) -function ArticleTitleField({ draft, onDraftChange }: { draft: ArticleDraft; onDraftChange: (draft: ArticleDraft) => void }): JSX.Element { +function ArticleTitleField({ draft, onDraftChange }: { draft: ArticleDraft; onDraftChange: (draft: ArticleDraft) => void }): React.ReactElement { return ( void -}): JSX.Element { +}): React.ReactElement { return ( void seriesOptions: { id: string; title: string }[] onSelectSeries?: ((seriesId: string | undefined) => void) | undefined -}): JSX.Element { +}): React.ReactElement { const handleChange = buildSeriesChangeHandler(draft, onDraftChange, onSelectSeries) return ( @@ -189,37 +190,58 @@ const ArticleFieldsRight = ({ }: { draft: ArticleDraft onDraftChange: (draft: ArticleDraft) => void -}): JSX.Element => ( -
-
-
{t('article.editor.content.label')}
- onDraftChange({ ...draft, content: value })} - onMediaAdd={(media: MediaRef) => { - const nextMedia = [...(draft.media ?? []), media] - onDraftChange({ ...draft, media: nextMedia }) - }} - onBannerChange={(url: string) => { - onDraftChange({ ...draft, bannerUrl: url }) - }} +}): React.ReactElement => { + // Use two-column editor with pages for series publications + const useTwoColumns = draft.seriesId !== undefined + + return ( +
+
+
{t('article.editor.content.label')}
+ {useTwoColumns ? ( + onDraftChange({ ...draft, content: value })} + pages={draft.pages} + onPagesChange={(pages) => onDraftChange({ ...draft, pages })} + onMediaAdd={(media: MediaRef) => { + const nextMedia = [...(draft.media ?? []), media] + onDraftChange({ ...draft, media: nextMedia }) + }} + onBannerChange={(url: string) => { + onDraftChange({ ...draft, bannerUrl: url }) + }} + /> + ) : ( + onDraftChange({ ...draft, content: value })} + onMediaAdd={(media: MediaRef) => { + const nextMedia = [...(draft.media ?? []), media] + onDraftChange({ ...draft, media: nextMedia }) + }} + onBannerChange={(url: string) => { + onDraftChange({ ...draft, bannerUrl: url }) + }} + /> + )} +

+ {t('article.editor.content.help')} +

+
+ onDraftChange({ ...draft, zapAmount: value as number })} + required + type="number" + min={1} + helpText={t('article.editor.sponsoring.help')} /> -

- {t('article.editor.content.help')} -

- onDraftChange({ ...draft, zapAmount: value as number })} - required - type="number" - min={1} - helpText={t('article.editor.sponsoring.help')} - /> -
-) + ) +} export function ArticleEditorForm({ draft, @@ -230,7 +252,7 @@ export function ArticleEditorForm({ onCancel, seriesOptions, onSelectSeries, -}: ArticleEditorFormProps): JSX.Element { +}: ArticleEditorFormProps): React.ReactElement { return (

{t('article.editor.title')}

diff --git a/components/ArticleField.tsx b/components/ArticleField.tsx index 0de08c3..4eb9a3f 100644 --- a/components/ArticleField.tsx +++ b/components/ArticleField.tsx @@ -30,7 +30,7 @@ function NumberOrTextInput({ min?: number className: string onChange: (value: string | number) => void -}): JSX.Element { +}): React.ReactElement { const inputProps = { id, type, @@ -64,7 +64,7 @@ function TextAreaInput({ rows?: number className: string onChange: (value: string | number) => void -}): JSX.Element { +}): React.ReactElement { const areaProps = { id, value, @@ -81,7 +81,7 @@ function TextAreaInput({ ) } -export function ArticleField(props: ArticleFieldProps): JSX.Element { +export function ArticleField(props: ArticleFieldProps): React.ReactElement { const { id, label, value, onChange, required = false, type = 'text', rows, placeholder, helpText, min } = props const inputClass = diff --git a/components/ArticleFilters.tsx b/components/ArticleFilters.tsx index 43ddfd3..422fed7 100644 --- a/components/ArticleFilters.tsx +++ b/components/ArticleFilters.tsx @@ -46,7 +46,7 @@ function FiltersGrid({ data: FiltersData filters: ArticleFilters onFiltersChange: (filters: ArticleFilters) => void -}): JSX.Element { +}): React.ReactElement { const update = (patch: Partial): void => onFiltersChange({ ...filters, ...patch }) return ( @@ -63,7 +63,7 @@ function FiltersHeader({ }: { hasActiveFilters: boolean onClear: () => void -}): JSX.Element { +}): React.ReactElement { return (

{t('filters.sort')}

@@ -83,7 +83,7 @@ function SortFilter({ }: { value: SortOption onChange: (value: SortOption) => void -}): JSX.Element { +}): React.ReactElement { return (
) } -function ArticleReviewsList({ reviews }: { reviews: Review[] }): JSX.Element { +function ArticleReviewsList({ reviews, onTipReview }: { reviews: Review[]; onTipReview: (reviewId: string) => void }): React.ReactElement { return ( - <> +
{reviews.map((r) => ( -
-
{r.content}
-
- Auteur critique : {formatPubkey(r.reviewerPubkey)} +
+ {r.title && ( +

{r.title}

+ )} +
{r.content}
+ {r.text && ( +
+ {r.text} +
+ )} +
+ {t('review.reviewer')}: {formatPubkey(r.reviewerPubkey)} {formatDate(r.createdAt)} +
))} - +
) } diff --git a/components/ArticlesList.tsx b/components/ArticlesList.tsx index c88de80..1271035 100644 --- a/components/ArticlesList.tsx +++ b/components/ArticlesList.tsx @@ -11,7 +11,7 @@ interface ArticlesListProps { unlockedArticles: Set } -function LoadingState(): JSX.Element { +function LoadingState(): React.ReactElement { // Use generic loading message at startup, then specific message once we know what we're loading return (
@@ -20,7 +20,7 @@ function LoadingState(): JSX.Element { ) } -function ErrorState({ message }: { message: string }): JSX.Element { +function ErrorState({ message }: { message: string }): React.ReactElement { return (

{message}

@@ -28,7 +28,7 @@ function ErrorState({ message }: { message: string }): JSX.Element { ) } -function EmptyState({ hasAny }: { hasAny: boolean }): JSX.Element { +function EmptyState({ hasAny }: { hasAny: boolean }): React.ReactElement { return (

@@ -45,7 +45,7 @@ export function ArticlesList({ error, onUnlock, unlockedArticles, -}: ArticlesListProps): JSX.Element { +}: ArticlesListProps): React.ReactElement { if (loading) { return } diff --git a/components/AuthorCard.tsx b/components/AuthorCard.tsx index 66b6cb6..91703de 100644 --- a/components/AuthorCard.tsx +++ b/components/AuthorCard.tsx @@ -7,7 +7,7 @@ interface AuthorCardProps { presentation: Article } -export function AuthorCard({ presentation }: AuthorCardProps): JSX.Element { +export function AuthorCard({ presentation }: AuthorCardProps): React.ReactElement { const authorName = presentation.title.replace(/^Présentation de /, '') || t('common.author') const totalBTC = (presentation.totalSponsoring ?? 0) / 100_000_000 diff --git a/components/AuthorFilter.tsx b/components/AuthorFilter.tsx index 97a0150..749cbe5 100644 --- a/components/AuthorFilter.tsx +++ b/components/AuthorFilter.tsx @@ -4,7 +4,7 @@ import { useAuthorFilterProps } from './AuthorFilterHooks' import { AuthorFilterButtonWrapper } from './AuthorFilterButton' import { AuthorDropdown } from './AuthorFilterDropdown' -function AuthorFilterLabel(): JSX.Element { +function AuthorFilterLabel(): React.ReactElement { return (

void -}): JSX.Element { +}): React.ReactElement { const props = useAuthorFilterProps(authors, value) return ( diff --git a/components/AuthorFilterButton.tsx b/components/AuthorFilterButton.tsx index aa8d93a..3828073 100644 --- a/components/AuthorFilterButton.tsx +++ b/components/AuthorFilterButton.tsx @@ -1,7 +1,7 @@ import React from 'react' import { AuthorAvatar } from './AuthorFilterDropdown' -export function AuthorMnemonicIcons({ value, getMnemonicIcons }: { value: string; getMnemonicIcons: (pubkey: string) => string[] }): JSX.Element { +export function AuthorMnemonicIcons({ value, getMnemonicIcons }: { value: string; getMnemonicIcons: (pubkey: string) => string[] }): React.ReactElement { return (
{getMnemonicIcons(value).map((icon, idx) => ( @@ -23,7 +23,7 @@ export function AuthorFilterButtonContent({ selectedAuthor: { name?: string; picture?: string } | null | undefined selectedDisplayName: string getMnemonicIcons: (pubkey: string) => string[] -}): JSX.Element { +}): React.ReactElement { return ( <> {value && ( @@ -38,7 +38,7 @@ export function AuthorFilterButtonContent({ ) } -export function DropdownArrowIcon({ isOpen }: { isOpen: boolean }): JSX.Element { +export function DropdownArrowIcon({ isOpen }: { isOpen: boolean }): React.ReactElement { return ( void buttonRef: React.RefObject -}): JSX.Element { +}): React.ReactElement { return (
{t('filters.loading')}
) : ( @@ -201,7 +201,7 @@ export function AuthorDropdown({ getDisplayName: (pubkey: string) => string getPicture: (pubkey: string) => string | undefined getMnemonicIcons: (pubkey: string) => string[] -}): JSX.Element { +}): React.ReactElement { return (
void): { dropdownRef: React.RefObject; buttonRef: React.RefObject } { - const dropdownRef = useRef(null) - const buttonRef = useRef(null) +export function useAuthorFilterDropdown(isOpen: boolean, setIsOpen: (open: boolean) => void): { dropdownRef: React.RefObject; buttonRef: React.RefObject } { + const dropdownRef = useRef(null) + const buttonRef = useRef(null) useEffect(() => { const handleClickOutside = (event: MouseEvent): void => { @@ -62,8 +62,8 @@ export function useAuthorFilterState(authors: string[], value: string | null): { loading: boolean isOpen: boolean setIsOpen: (open: boolean) => void - dropdownRef: React.RefObject - buttonRef: React.RefObject + dropdownRef: React.RefObject + buttonRef: React.RefObject getDisplayName: (pubkey: string) => string getPicture: (pubkey: string) => string | undefined getMnemonicIcons: (pubkey: string) => string[] diff --git a/components/AuthorsList.tsx b/components/AuthorsList.tsx index 704da77..9140d8b 100644 --- a/components/AuthorsList.tsx +++ b/components/AuthorsList.tsx @@ -9,7 +9,7 @@ interface AuthorsListProps { error: string | null } -function LoadingState(): JSX.Element { +function LoadingState(): React.ReactElement { return (

{t('common.loading.authors')}

@@ -17,7 +17,7 @@ function LoadingState(): JSX.Element { ) } -function ErrorState({ message }: { message: string }): JSX.Element { +function ErrorState({ message }: { message: string }): React.ReactElement { return (

{message}

@@ -25,7 +25,7 @@ function ErrorState({ message }: { message: string }): JSX.Element { ) } -function EmptyState({ hasAny }: { hasAny: boolean }): JSX.Element { +function EmptyState({ hasAny }: { hasAny: boolean }): React.ReactElement { return (

@@ -35,7 +35,7 @@ function EmptyState({ hasAny }: { hasAny: boolean }): JSX.Element { ) } -export function AuthorsList({ authors, allAuthors, loading, error }: AuthorsListProps): JSX.Element { +export function AuthorsList({ authors, allAuthors, loading, error }: AuthorsListProps): React.ReactElement { if (loading) { return } diff --git a/components/CacheUpdateManager.tsx b/components/CacheUpdateManager.tsx new file mode 100644 index 0000000..f4b484d --- /dev/null +++ b/components/CacheUpdateManager.tsx @@ -0,0 +1,106 @@ +import { useState } from 'react' +import { nostrAuthService } from '@/lib/nostrAuth' +import { objectCache } from '@/lib/objectCache' +import { syncUserContentToCache } from '@/lib/userContentSync' + +async function updateCache(): Promise { + const state = nostrAuthService.getState() + if (!state.connected || !state.pubkey) { + throw new Error('Vous devez être connecté pour mettre à jour le cache') + } + + await Promise.all([ + objectCache.clear('author'), + objectCache.clear('series'), + objectCache.clear('publication'), + objectCache.clear('review'), + objectCache.clear('purchase'), + objectCache.clear('sponsoring'), + objectCache.clear('review_tip'), + ]) + + await syncUserContentToCache(state.pubkey) +} + +function ErrorMessage({ error }: { error: string }): React.ReactElement { + return ( +

+

{error}

+
+ ) +} + +function SuccessMessage(): React.ReactElement { + return ( +
+

Cache mis à jour avec succès

+
+ ) +} + +function NotConnectedMessage(): React.ReactElement { + return ( +
+

Vous devez être connecté pour mettre à jour le cache

+
+ ) +} + +function createUpdateHandler( + setUpdating: (value: boolean) => void, + setError: (value: string | null) => void, + setSuccess: (value: boolean) => void +): () => Promise { + return async (): Promise => { + try { + setUpdating(true) + setError(null) + setSuccess(false) + await updateCache() + setSuccess(true) + setTimeout(() => { + setSuccess(false) + }, 3000) + } catch (e) { + const errorMessage = e instanceof Error ? e.message : 'Erreur lors de la mise à jour du cache' + setError(errorMessage) + console.error('Error updating cache:', e) + } finally { + setUpdating(false) + } + } +} + +export function CacheUpdateManager(): React.ReactElement { + const [updating, setUpdating] = useState(false) + const [success, setSuccess] = useState(false) + const [error, setError] = useState(null) + const handleUpdateCache = createUpdateHandler(setUpdating, setError, setSuccess) + const state = nostrAuthService.getState() + const isConnected = state.connected && state.pubkey + + return ( +
+

Mise à jour du cache

+ +

+ Videz et re-synchronisez le cache IndexedDB avec les données depuis les relais Nostr. + Cela permet de récupérer les dernières versions de vos publications, séries et profil. +

+ + {error && } + {success && } + {!isConnected && } + + +
+ ) +} diff --git a/components/CategorySelect.tsx b/components/CategorySelect.tsx index 29d0eaa..8733af4 100644 --- a/components/CategorySelect.tsx +++ b/components/CategorySelect.tsx @@ -17,7 +17,7 @@ export function CategorySelect({ onChange, required = false, helpText, -}: CategorySelectProps): JSX.Element { +}: CategorySelectProps): React.ReactElement { return (