import type { CarReader } from '@ipld/car' import type { ISubscription } from '@wovin/core/pubsub' import type { Accessor, Component } from 'solid-js' import { useSearchParams } from '@solidjs/router' import { carFromBlob, tryParseCID } from '@wovin/core' import { isoDateStrCompare } from '@wovin/core/applog' import { cidToString } from '@wovin/core/ipfs' import { toJS } from '@wovin/core/mobx' import { Logger } from 'besonders-logger' import { Collapse } from 'solid-collapse' import { createEffect, createMemo, createResource, createSignal, For, Match, Show, Suspense, Switch } from 'solid-js' import { boundInput, useAgent } from '../../data/agent/AgentState' import { DefaultAgentBanner, getSubOrShare } from '../../data/agent/utils-agent' import { initUseAgent } from '../../data/lazy-agent' import { decodePubCarWithExtras, retrievePubDataWithExtras } from '../../ipfs/store-sync' import { doSync } from '../../ipfs/sync-service' import { PreviewInfoPanel } from '../../ui/preview' import { useRawThread } from '../../ui/reactive' import { createAsyncButtonHandler, makeNote3Url, stopPropagation, tryParseNote3URL, useLocationNavigate, useSingleUrlParam, } from '../../ui/utils-ui' import { BackLink } from '../BackLink' import { CidInput } from '../CidInput' import { CopyableID, DeleteWithConfirm, FileSelect, Iconify, NameValueBadge, PubInfoBox, Spinner } from '../mini-components' const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line unused-imports/no-unused-vars export const SubscriptionsSettings: Component<{ activeSection?: Accessor params?: Accessor }> = ( props, ) => { DEBUG('create ', props) // const agent = useAgent() initUseAgent() const focussedSub = createMemo(() => props.params()?.[0]) const disabledProp = {} // agent.isAgentStringSetup ? {} : { disabled: 'Disabled until you setup your agent string' } // ? why return (

) } export const SubscriptionsList: Component<{ focussedSub?: Accessor }> = (props) => { DEBUG('create ') const agent = useAgent() const { subscriptions } = agent const focusedOrList = createMemo(() => { let list = props.focussedSub() ? subscriptions.filter(p => p.id === props.focussedSub()) : subscriptions list = list.slice().sort((a, b) => isoDateStrCompare(a.createdAt, b.createdAt, 'desc')) // newest first DEBUG(`focusedOrList:`, list) return list }) createEffect(() => DEBUG(`focussed:`, props.focussedSub())) createEffect(() => DEBUG(`subs:`, toJS(subscriptions))) return (
Subscription not found:
{props.focussedSub()}
No subscriptions (yet) )} > {(sub: ISubscription, _nodeIdx) => { const isFocussed = createMemo(() => props.focussedSub() === sub.id) return }}
) } export const SubscriptionAdder: Component<{ // disabled?: string }> = () => { DEBUG('create ') const [urlSearchParams, _setSearchParams] = useSearchParams() const [pubToPull] = useSingleUrlParam('pub') const appThread = useRawThread() // const agent = useAgent() const [retrieveID, setRetrieveID] = createSignal(pubToPull() || '') // const [retrieving, setRetrieving] = createSignal(false) const [subExtras, setSubExtras] = createSignal({}) const [carFile, setCarFile] = createSignal(null) const sanitizedID = createMemo(() => { const parsedUrl = tryParseNote3URL(retrieveID()) if (parsedUrl && parsedUrl.share) { DEBUG('parsedUrl:', { retrieveID: retrieveID(), parsedUrl }) if (parsedUrl.focus) setSubExtras({ selector: `blocks(${parsedUrl.focus})` }) return parsedUrl.share // ? overwrite retrieveID } return retrieveID() }) const validCID = createMemo(() => { const parsed = tryParseCID(sanitizedID()) DEBUG('validCID?', { sanitizedID: sanitizedID(), parsed }) if (parsed.cid) { return cidToString(parsed.cid) } return null }) const parseError = createMemo(() => { if (!retrieveID() || validCID()) return null return tryParseCID(sanitizedID()).errors?.length ? `Invalid URL/CID: '${sanitizedID()}'` : null }) const matchingSubOrShare = createMemo(() => sanitizedID() ? getSubOrShare(sanitizedID()) : null) const matchingIsShare = createMemo(() => !!matchingSubOrShare() && (matchingSubOrShare() as any).lastPush !== undefined) const [previewData, { refetch: refetchPreviewData }] = createResource(() => ({ validCID: validCID(), matchingSubOrShare: matchingSubOrShare(), carFile: carFile(), }), async ({ validCID, matchingSubOrShare, carFile }) => { if (carFile) { const previewData = await decodePubCarWithExtras(appThread, carFile) DEBUG.force('previewData from carFile', previewData) return previewData } else { if (matchingSubOrShare) return if (!validCID) return null return await retrievePubDataWithExtras(appThread, validCID) } }) async function loadCar(files: FileList) { const file = files[0] // HACK: input allows only single select for now LOG(`Loading CAR file`, file) const car = await carFromBlob(file) setCarFile(car) } // async function pullAndSave(sub: ISubscription | null, save: boolean = false, retries = 0) { // // const [encryptedBy, derivationJWKstring] = (encryptAgentSelectRefPull?.value ?? '').split('_|_') ?? [] // setRetrieving(true) // try { // if (carFile()) { // try { // const { cid, applogs, info } = await decodePubFromCar(carFile()) // await tryIntegrateApplogs(applogs, info.logs, agent.ag, useRawThread(), cid.toString(), cid) // notifyToast(`Successfully loaded CAR file`, 'success') // setCarFile(null) // } catch (err) { // ERROR(`Failed to load CAR file`, err) // notifyToast(`Failed to load CAR file`, 'danger') // } // } else { // try { // const ipns = parseW3Name(sub ? sub.id : (validCID() || examplePullHash)) // DEBUG('retrieving:', { ipns, sub /* encryptedBy, derivationJWKstring, */ }) // if (!sub) { // if (matchingIsShare()) throw ERROR(`trying to retrieve from own share`, sub) // sub = matchingSubOrShare() as (ISubscription | null) // } // // const sub = sub as ISubscription|null // const pullData = await retrievePubToThread(appThread, ipns.toString(), { subID: sub?.id }, agent.ag) // // TODO: duplicate with the other place // const updatedFields = { // lastPullAttempt: dateNowIso(), // lastCID: pullData.cid.toString(), // lastApplogCID: pullData.applogsCID.toString(), // } // DEBUG('retrieved:', pullData, 'save?', !sub && save, updatedFields) // if (!sub && save) { // const sub = await upsertSubscription(ipns.toString(), updatedFields) // setRetrieveID('') // navigate('/settings/subscriptions/' + sub.id) // } // } catch (err) { // // throw new Error(`failed to retrieve ${validCID()}`, { cause: err }) // ERROR(`failed to retrieve ${validCID()}`, err) // if (retries++ < 3) await pullAndSave(sub, save, retries) // else notifyToast('Failed to pull: ' + (err.message || JSON.stringify(err, undefined, 2)), 'danger') // } // } // } finally { // setRetrieving(false) // } // } return (
Add subscription

Share ID from URL:

setRetrieveID((e.target as HTMLInputElement).value)} />
{parseError()}
{/* target='_blank' */} Preview )} /> { /*
{/* target='_blank' * /} Preview {/* * /} pullAndSave(null, true)} loading={retrieving()} disabled={(!validCID() && !carFile()) || matchingSubOrShare()} > {carFile() ? 'Load' : `Pull & Load`} { /* More pull options retrieve(null, false)}>Pull only Preview (TODO) * / }
*/ }
loadCar(files)}>Load from file
) } export const SingleSubscriptionPanel: Component<{ sub: ISubscription isFocussed?: Accessor | undefined }> = (props) => { const { isFocussed, sub } = props const rawDS = useRawThread() const agent = useAgent() const { locnav, location, navigate } = useLocationNavigate() const [isExpanded, setIsExpanded] = createSignal(isFocussed?.()) const [expandedOnce, setExpandedOnce] = createSignal(isExpanded()) createEffect(() => { if (isFocussed?.()) { setIsExpanded(true) setExpandedOnce(true) } // if it was in the list, but not focussed - it will not be recreated }) async function deleteSub(id) { await agent.deleteSub(id) if (isFocussed?.()) navigate(`/settings/subscriptions`) } const pullButtonProps = createAsyncButtonHandler(async () => { DEBUG(`Pulling`, props.sub.name, { sub: props.sub, rawDS }) await doSync({ sub: props.sub.id }) }) const onToggleAutopull = e => agent.updateSub(props.sub.id, { autopull: e.target.checked }) const isAutopull = createMemo(() => { return props.sub.autopull }) // TODO: sub data? // const subData = /* computed(() => */ getPublicationData(rawDS, sub) /* ) */ // const subRoots = withDS(subData, () => useRoots()) // const knownAgentPKlogs = rollingFilter(rawDS, { at: 'agent/jwkd' }) let panelRef, caretRef, buttonRow return (
{ DEBUG(`.onClick`, e, { panelRef }) if (e.target !== panelRef && e.target !== caretRef && e.target !== buttonRow) return e.stopPropagation() setIsExpanded(!isExpanded()) setExpandedOnce(true) }} >
{boundInput('subscriptionsMap', [], { subID: props.sub.id, padding: 'py-1 px-2' })} {props.sub.lastPull ? : 'never pulled'}
{props.sub.encryptedFor ? `for ${props.sub.encryptedFor}` : 'unencrypted'}
deleteSub(props.sub.id)} /> last pull: {' '} never
autopull: {' '} {sub.autopull ? 'on' : 'off'} )} >
collapsed content
}> }>
never ⇒ {' '}
Pull settings
Autopull 
{ /*
Preview ({subData.size} Applogs, ) {(rootID) => { const content = useBlockAt(rootID, 'content') return (
- {content.get() as string} ( kids)
) }}
*/ }
) }