lint fix wip
This commit is contained in:
parent
cc84d85193
commit
5b7b77aa9a
@ -5,9 +5,9 @@ interface ArticlePagesProps {
|
|||||||
pages: Page[]
|
pages: Page[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ArticlePages({ pages }: ArticlePagesProps): React.ReactElement {
|
export function ArticlePages({ pages }: ArticlePagesProps): React.ReactElement | null {
|
||||||
if (!pages || pages.length === 0) {
|
if (!pages || pages.length === 0) {
|
||||||
return <></>
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -232,7 +232,7 @@ function PresentationForm({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{hasExistingPresentation && (
|
{hasExistingPresentation && (
|
||||||
<DeleteButton onDelete={handleDelete} deleting={deleting} />
|
<DeleteButton onDelete={() => { void handleDelete() }} deleting={deleting} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -392,7 +392,7 @@ function NoAccountView(): React.ReactElement {
|
|||||||
</p>
|
</p>
|
||||||
{error && <p className="text-sm text-red-400">{error}</p>}
|
{error && <p className="text-sm text-red-400">{error}</p>}
|
||||||
<NoAccountActionButtons
|
<NoAccountActionButtons
|
||||||
onGenerate={handleGenerate}
|
onGenerate={() => { void handleGenerate() }}
|
||||||
onImport={() => setShowImportModal(true)}
|
onImport={() => setShowImportModal(true)}
|
||||||
/>
|
/>
|
||||||
{generating && (
|
{generating && (
|
||||||
@ -482,7 +482,7 @@ function AuthorPresentationFormView({
|
|||||||
loading={state.loading}
|
loading={state.loading}
|
||||||
handleSubmit={state.handleSubmit}
|
handleSubmit={state.handleSubmit}
|
||||||
deleting={state.deleting}
|
deleting={state.deleting}
|
||||||
handleDelete={state.handleDelete}
|
handleDelete={() => { void state.handleDelete() }}
|
||||||
hasExistingPresentation={existingPresentation !== null && existingPresentation !== undefined}
|
hasExistingPresentation={existingPresentation !== null && existingPresentation !== undefined}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -165,7 +165,7 @@ export function ConnectButton(): React.ReactElement {
|
|||||||
error={error ?? createError}
|
error={error ?? createError}
|
||||||
showUnlockModal={showUnlockModal}
|
showUnlockModal={showUnlockModal}
|
||||||
setShowUnlockModal={setShowUnlockModal}
|
setShowUnlockModal={setShowUnlockModal}
|
||||||
onCreateAccount={handleCreateAccount}
|
onCreateAccount={() => { void handleCreateAccount() }}
|
||||||
/>
|
/>
|
||||||
{showRecoveryStep && (
|
{showRecoveryStep && (
|
||||||
<RecoveryStep
|
<RecoveryStep
|
||||||
|
|||||||
@ -163,10 +163,10 @@ export function CreateAccountModal({ onSuccess, onClose, initialStep = 'choose'
|
|||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
handleContinue,
|
handleContinue,
|
||||||
handleImport,
|
() => { void handleImport() },
|
||||||
setStep,
|
setStep,
|
||||||
setError,
|
setError,
|
||||||
handleGenerate,
|
() => { void handleGenerate() },
|
||||||
onClose
|
onClose
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export function RecoveryStep({
|
|||||||
<div className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto shadow-glow-cyan">
|
<div className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto shadow-glow-cyan">
|
||||||
<h2 className="text-2xl font-bold mb-4 text-neon-cyan">{t('account.create.recovery.title')}</h2>
|
<h2 className="text-2xl font-bold mb-4 text-neon-cyan">{t('account.create.recovery.title')}</h2>
|
||||||
<RecoveryWarning />
|
<RecoveryWarning />
|
||||||
<RecoveryPhraseDisplay recoveryPhrase={recoveryPhrase} copied={copied} onCopy={handleCopy} />
|
<RecoveryPhraseDisplay recoveryPhrase={recoveryPhrase} copied={copied} onCopy={() => { void handleCopy() }} />
|
||||||
<PublicKeyDisplay npub={npub} />
|
<PublicKeyDisplay npub={npub} />
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -27,9 +27,9 @@ function ImagePreview({ value }: { value: string }): React.ReactElement {
|
|||||||
|
|
||||||
function UploadButtonLabel({ uploading, value }: { uploading: boolean; value: string | undefined }): React.ReactElement {
|
function UploadButtonLabel({ uploading, value }: { uploading: boolean; value: string | undefined }): React.ReactElement {
|
||||||
if (uploading) {
|
if (uploading) {
|
||||||
return <>{t('presentation.field.picture.uploading')}</>
|
return <span>{t('presentation.field.picture.uploading')}</span>
|
||||||
}
|
}
|
||||||
return <>{value ? t('presentation.field.picture.change') : t('presentation.field.picture.upload')}</>
|
return <span>{value ? t('presentation.field.picture.change') : t('presentation.field.picture.upload')}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
function RemoveButton({ value, onChange }: { value: string | undefined; onChange: (url: string) => void }): React.ReactElement | null {
|
function RemoveButton({ value, onChange }: { value: string | undefined; onChange: (url: string) => void }): React.ReactElement | null {
|
||||||
|
|||||||
@ -252,8 +252,8 @@ export function KeyManagementManager(): React.ReactElement {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Sync Progress Bar */}
|
{/* Sync Progress Bar - Always show if connected, even if publicKeys not loaded yet */}
|
||||||
{publicKeys && <SyncProgressBar />}
|
<SyncProgressBar />
|
||||||
|
|
||||||
{!publicKeys && !accountExists && (
|
{!publicKeys && !accountExists && (
|
||||||
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4 mb-6">
|
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4 mb-6">
|
||||||
|
|||||||
@ -213,6 +213,7 @@ function UnlockAccountForm({
|
|||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<WordInputs words={words} onWordChange={handleWordChange} />
|
<WordInputs words={words} onWordChange={handleWordChange} />
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
void handlePaste()
|
void handlePaste()
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -29,25 +29,47 @@ export function useArticles(searchQuery: string = '', filters: ArticleFilters |
|
|||||||
const cachedAuthors = await objectCache.getAll('author')
|
const cachedAuthors = await objectCache.getAll('author')
|
||||||
const authors = cachedAuthors as Article[]
|
const authors = cachedAuthors as Article[]
|
||||||
|
|
||||||
// Calculate totalSponsoring for each author
|
// Display authors immediately (with existing totalSponsoring if available)
|
||||||
const authorsWithSponsoring = await Promise.all(
|
if (authors.length > 0) {
|
||||||
authors.map(async (author) => {
|
|
||||||
if (author.isPresentation && author.pubkey) {
|
|
||||||
author.totalSponsoring = await getAuthorSponsoring(author.pubkey)
|
|
||||||
}
|
|
||||||
return author
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
if (authorsWithSponsoring.length > 0) {
|
|
||||||
setArticles((prev) => {
|
setArticles((prev) => {
|
||||||
// Merge with existing articles, avoiding duplicates
|
// Merge with existing articles, avoiding duplicates
|
||||||
const existingIds = new Set(prev.map((a) => a.id))
|
const existingIds = new Set(prev.map((a) => a.id))
|
||||||
const newAuthors = authorsWithSponsoring.filter((a) => !existingIds.has(a.id))
|
const newAuthors = authors.filter((a) => !existingIds.has(a.id))
|
||||||
const merged = [...prev, ...newAuthors].sort((a, b) => b.createdAt - a.createdAt)
|
const merged = [...prev, ...newAuthors].sort((a, b) => b.createdAt - a.createdAt)
|
||||||
hasArticlesRef.current = merged.length > 0
|
hasArticlesRef.current = merged.length > 0
|
||||||
return merged
|
return merged
|
||||||
})
|
})
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate totalSponsoring asynchronously from cache (non-blocking)
|
||||||
|
// Only update authors that don't have totalSponsoring yet
|
||||||
|
const authorsNeedingSponsoring = authors.filter(
|
||||||
|
(author) => author.isPresentation && author.pubkey && author.totalSponsoring === undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
if (authorsNeedingSponsoring.length > 0) {
|
||||||
|
// Load sponsoring from cache in parallel (fast, no network)
|
||||||
|
const sponsoringPromises = authorsNeedingSponsoring.map(async (author) => {
|
||||||
|
if (author.pubkey) {
|
||||||
|
const totalSponsoring = await getAuthorSponsoring(author.pubkey, true)
|
||||||
|
return { authorId: author.id, totalSponsoring }
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
const sponsoringResults = await Promise.all(sponsoringPromises)
|
||||||
|
|
||||||
|
// Update articles with sponsoring amounts
|
||||||
|
setArticles((prev) =>
|
||||||
|
prev.map((article) => {
|
||||||
|
const sponsoringResult = sponsoringResults.find((r) => r?.authorId === article.id)
|
||||||
|
if (sponsoringResult && article.isPresentation) {
|
||||||
|
return { ...article, totalSponsoring: sponsoringResult.totalSponsoring }
|
||||||
|
}
|
||||||
|
return article
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading authors from cache:', error)
|
console.error('Error loading authors from cache:', error)
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export class AlbyService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await webln.enable()
|
await webln.enable()
|
||||||
} catch (_error) {
|
} catch {
|
||||||
throw new Error('User rejected WebLN permission request')
|
throw new Error('User rejected WebLN permission request')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ export class AlbyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(checkPayment, interval)
|
setTimeout(checkPayment, interval)
|
||||||
} catch (_error) {
|
} catch {
|
||||||
resolve({ paid: false, paymentHash })
|
resolve({ paid: false, paymentHash })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@ async function tryUploadEndpoint(endpoint: string, formData: FormData, useProxy:
|
|||||||
try {
|
try {
|
||||||
const text = await response.text()
|
const text = await response.text()
|
||||||
errorMessage = text || `HTTP ${response.status} ${response.statusText}`
|
errorMessage = text || `HTTP ${response.status} ${response.statusText}`
|
||||||
} catch (_e) {
|
} catch {
|
||||||
errorMessage = `HTTP ${response.status} ${response.statusText}`
|
errorMessage = `HTTP ${response.status} ${response.statusText}`
|
||||||
}
|
}
|
||||||
throw new Error(errorMessage)
|
throw new Error(errorMessage)
|
||||||
|
|||||||
@ -37,7 +37,7 @@ class NostrService {
|
|||||||
if (decoded.type === 'nsec' && typeof decoded.data === 'string') {
|
if (decoded.type === 'nsec' && typeof decoded.data === 'string') {
|
||||||
this.privateKey = decoded.data
|
this.privateKey = decoded.data
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Assume it's already a hex string
|
// Assume it's already a hex string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class NostrService {
|
|||||||
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
|
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
|
||||||
this.publicKey = decoded.data
|
this.publicKey = decoded.data
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Assume it's already a hex string
|
// Assume it's already a hex string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,59 @@
|
|||||||
import { getSponsoringByAuthor } from './sponsoringQueries'
|
import { getSponsoringByAuthor } from './sponsoringQueries'
|
||||||
|
import { objectCache } from './objectCache'
|
||||||
import type { Article } from '@/types/nostr'
|
import type { Article } from '@/types/nostr'
|
||||||
|
import type { Sponsoring } from '@/types/nostr'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total sponsoring for an author by their pubkey from IndexedDB cache
|
||||||
|
* This is much faster than network queries
|
||||||
|
* Returns both the amount and whether the cache was empty (to avoid duplicate calls)
|
||||||
|
*/
|
||||||
|
async function getAuthorSponsoringFromCache(pubkey: string): Promise<{ amount: number; cacheWasEmpty: boolean }> {
|
||||||
|
try {
|
||||||
|
const allSponsoring = await objectCache.getAll('sponsoring')
|
||||||
|
const sponsoringList = allSponsoring as Sponsoring[]
|
||||||
|
|
||||||
|
// If cache is empty, return immediately
|
||||||
|
if (sponsoringList.length === 0) {
|
||||||
|
return { amount: 0, cacheWasEmpty: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by author pubkey and sum amounts
|
||||||
|
const amount = sponsoringList
|
||||||
|
.filter((sponsoring) => sponsoring.authorPubkey === pubkey)
|
||||||
|
.reduce((total, sponsoring) => total + sponsoring.amount, 0)
|
||||||
|
|
||||||
|
return { amount, cacheWasEmpty: false }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error calculating author sponsoring from cache:', error)
|
||||||
|
return { amount: 0, cacheWasEmpty: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get total sponsoring for an author by their pubkey
|
* Get total sponsoring for an author by their pubkey
|
||||||
* Calculates from cache (sponsoring queries) instead of tags
|
* First tries cache, then falls back to network query if needed
|
||||||
|
* If cache is empty, returns 0 immediately (very fast, no network request)
|
||||||
*/
|
*/
|
||||||
export async function getAuthorSponsoring(pubkey: string): Promise<number> {
|
export async function getAuthorSponsoring(pubkey: string, useCacheOnly: boolean = true): Promise<number> {
|
||||||
try {
|
try {
|
||||||
|
// Try cache first (much faster)
|
||||||
|
const { amount: cachedAmount, cacheWasEmpty } = await getAuthorSponsoringFromCache(pubkey)
|
||||||
|
|
||||||
|
// If cache is empty, return 0 immediately (no network request)
|
||||||
|
if (cacheWasEmpty) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedAmount > 0 || useCacheOnly) {
|
||||||
|
return cachedAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to network query if cache exists but cacheOnly is false
|
||||||
const sponsoringList = await getSponsoringByAuthor(pubkey, 5000)
|
const sponsoringList = await getSponsoringByAuthor(pubkey, 5000)
|
||||||
// Sum all sponsoring amounts for this author
|
|
||||||
return sponsoringList.reduce((total, sponsoring) => total + sponsoring.amount, 0)
|
return sponsoringList.reduce((total, sponsoring) => total + sponsoring.amount, 0)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error calculating author sponsoring from cache:', error)
|
console.error('Error calculating author sponsoring:', error)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user