Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions plugins/notion/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { FieldMapping } from "./FieldMapping"
import { NoTableAccess } from "./NoAccess"
import { Progress } from "./Progress"
import { SelectDataSource } from "./SelectDataSource"
import { showAccessErrorUI, showFieldMappingUI, showLoginUI, showProgressUI } from "./ui"
import { closePluginAfterPartialSync, showAccessErrorUI, showFieldMappingUI, showLoginUI, showProgressUI } from "./ui"

interface AppProps {
collection: ManagedCollection
Expand Down Expand Up @@ -59,7 +59,7 @@ export function App({
void showProgressUI()

try {
const { didSync } = await syncExistingCollection(
const sync = await syncExistingCollection(
collection,
previousDatabaseId,
previousSlugFieldId,
Expand All @@ -70,12 +70,14 @@ export function App({
setProgress
)

if (didSync) {
if (!sync.didSync) {
setIsSyncMode(false)
} else if (sync.result.status === "partial") {
closePluginAfterPartialSync(sync.result)
} else {
framer.closePlugin("Synchronization successful", {
variant: "success",
})
} else {
setIsSyncMode(false)
}
} catch (error) {
if (error instanceof FramerPluginClosedError) return
Expand Down
10 changes: 8 additions & 2 deletions plugins/notion/src/FieldMapping.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
syncCollection,
} from "./data"
import { Progress } from "./Progress"
import { closePluginAfterPartialSync } from "./ui"
import { assert, syncMethods } from "./utils"

const labelByFieldTypeOption: Record<VirtualFieldType, string> = {
Expand Down Expand Up @@ -271,7 +272,7 @@ export function FieldMapping({
}

await collection.setFields(fieldsToSync)
await syncCollection(
const result = await syncCollection(
collection,
dataSource,
fieldsToSync,
Expand All @@ -281,7 +282,12 @@ export function FieldMapping({
existingFields,
setSyncProgress
)
framer.closePlugin("Synchronization successful", { variant: "success" })

if (result.status === "partial") {
closePluginAfterPartialSync(result)
} else {
framer.closePlugin("Synchronization successful", { variant: "success" })
}
} catch (error) {
if (error instanceof FramerPluginClosedError) return
console.error(error)
Expand Down
45 changes: 38 additions & 7 deletions plugins/notion/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ export interface SyncError {
error: unknown
}

export type SyncResult =
| { status: "success" }
| {
status: "partial"
succeeded: number
failed: number
total: number

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: do we need all 3, we can derive the total right?

}

export async function syncCollection(
collection: ManagedCollection,
dataSource: DataSource,
Expand All @@ -123,7 +132,7 @@ export async function syncCollection(
lastSynced: string | null,
existingFields?: readonly ManagedCollectionFieldInput[],
onProgress?: (progress: SyncProgress) => void
) {
): Promise<SyncResult> {
const fieldsById = new Map(fields.map(field => [field.id, field]))
const contentFieldEnabled = fieldsById.has(pageContentProperty.id)
const reportProgress = (p: { current: number; total: number; hasFinishedLoading: boolean }) =>
Expand All @@ -144,6 +153,10 @@ export async function syncCollection(

const seenItemIds = new Set<string>()

// Save a conservative checkpoint from before reading Notion. If an item is edited
// while this sync is running, its edit time will be newer than this checkpoint and
// the item will be picked up by the next sync.
const syncStartedAt = new Date().toISOString()
const databaseItems = await getDatabaseItems(dataSource.database, reportProgress)

// Validate slugs before fetching page content
Expand Down Expand Up @@ -371,16 +384,34 @@ export async function syncCollection(
await collection.removeItems(Array.from(itemIdsToDelete))
await collection.addItems(items)

await Promise.all([
const pluginDataUpdates = [
collection.setPluginData(
PLUGIN_KEYS.IGNORED_FIELD_IDS,
ignoredFieldIds.size > 0 ? JSON.stringify(Array.from(ignoredFieldIds)) : null
),
collection.setPluginData(PLUGIN_KEYS.DATABASE_ID, dataSource.database.id),
collection.setPluginData(PLUGIN_KEYS.LAST_SYNCED, new Date().toISOString()),
collection.setPluginData(PLUGIN_KEYS.SLUG_FIELD_ID, slugField.id),
collection.setPluginData(PLUGIN_KEYS.DATABASE_NAME, richTextToPlainText(dataSource.database.title)),
])
]

// Do not move the collection-wide checkpoint past items that failed. Keeping the
// previous checkpoint makes those items eligible for the next sync.
if (syncErrors.length === 0) {
pluginDataUpdates.push(collection.setPluginData(PLUGIN_KEYS.LAST_SYNCED, syncStartedAt))
}

await Promise.all(pluginDataUpdates)

if (syncErrors.length > 0) {
return {
status: "partial",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: it's bit strange that if all items error we still have "partial" status even though none succeeded

succeeded: items.length,
failed: syncErrors.length,
total: databaseItems.length,
}
}

return { status: "success" }
}

const IgnoredFieldIdsSchema = v.array(v.string())
Expand Down Expand Up @@ -415,7 +446,7 @@ export async function syncExistingCollection(
previousDatabaseName: string | null,
databaseIdMap: DatabaseIdMap,
onProgress?: (progress: SyncProgress) => void
): Promise<{ didSync: boolean }> {
): Promise<{ didSync: false } | { didSync: true; result: SyncResult }> {
if (
!shouldSyncExistingCollection({ previousSlugFieldId, previousDatabaseId }) ||
!previousSlugFieldId ||
Expand Down Expand Up @@ -449,7 +480,7 @@ export async function syncExistingCollection(
existingFields.some(existingField => existingField.id === field.id) && !ignoredFieldIds.has(field.id)
)

await syncCollection(
const result = await syncCollection(
collection,
dataSource,
fieldsToSync,
Expand All @@ -459,7 +490,7 @@ export async function syncExistingCollection(
existingFields,
onProgress
)
return { didSync: true }
return { didSync: true, result }
} catch (error) {
console.error(error)
framer.notify(
Expand Down
10 changes: 10 additions & 0 deletions plugins/notion/src/ui.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { framer } from "framer-plugin"
import type { SyncResult } from "./data"

type PartialSyncResult = Extract<SyncResult, { status: "partial" }>

export function closePluginAfterPartialSync(result: PartialSyncResult) {
const pluralSuffix = result.failed === 1 ? "" : "s"
framer.closePlugin(`Failed to sync ${result.failed} item${pluralSuffix}. Please try again.`, {
variant: "error",
})
}

export async function showAccessErrorUI() {
await framer.showUI({
Expand Down
Loading