Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When archiving, archive children as well #400

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
62 changes: 48 additions & 14 deletions src/backend/Database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,29 +57,63 @@ test('index is correct', async () => {
assert.is(entries[1]._index, second)
})

test('remove child entries', async () => {
test('archive child entries', async () => {
const example = createExample()
const {Page, Container} = example.schema
const parent = Edit.create({type: Container, set: {title: 'Parent'}})
const sub = Edit.create({
const parent = await example.create({type: Container, set: {title: 'Parent'}})
const sub = await example.create({
type: Container,
parentId: parent.id,
parentId: parent._id,
set: {title: 'Sub'}
})
const entry = Edit.create({
const entry = await example.create({
type: Page,
parentId: sub.id,
parentId: sub._id,
set: {title: 'Deepest'}
})
await example.commit(parent)
await example.commit(sub)
await example.commit(entry)
const res1 = await example.get({
id: entry.id
assert.is(entry._parentId, sub._id)
await example.update({id: parent._id, status: EntryStatus.Archived})
assert.not.ok(
await example.first({id: parent._id}),
'Parent entry should be archived'
)
assert.not.ok(
await example.first({id: sub._id}),
'Sub entry should be archived'
)
assert.not.ok(
await example.first({id: entry._id}),
'Deepest entry should be archived'
)
await example.update({id: parent._id, status: EntryStatus.Published})
assert.ok(
await example.first({id: parent._id}),
'Parent entry should be published'
)
assert.ok(await example.first({id: sub._id}), 'Sub entry should be published')
assert.ok(
await example.first({id: entry._id}),
'Deepest entry should be published'
)
})

test('remove child entries', async () => {
const example = createExample()
const {Page, Container} = example.schema
const parent = await example.create({type: Container, set: {title: 'Parent'}})
const sub = await example.create({
type: Container,
parentId: parent._id,
set: {title: 'Sub'}
})
const entry = await example.create({
type: Page,
parentId: sub._id,
set: {title: 'Deepest'}
})
assert.is(res1._parentId, sub.id)
await example.commit(Edit.remove(parent.id))
const res2 = await example.first({id: entry.id})
assert.is(entry._parentId, sub._id)
await example.remove(parent._id)
const res2 = await example.first({id: entry._id})
assert.not.ok(res2)
})

Expand Down
69 changes: 44 additions & 25 deletions src/backend/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,20 +203,21 @@ export class Database implements Syncable {
return children
}

async logEntries() {
async logEntries(
keys: Array<keyof EntryRow> = [
'url',
'id',
'parentId',
'locale',
'status',
'title'
]
) {
const entries = await this.store
.select()
.from(EntryRow)
.orderBy(asc(EntryRow.url), asc(EntryRow.index))
for (const entry of entries) {
console.info(
entry.url.padEnd(35),
entry.id.padEnd(12),
(entry.locale ?? '').padEnd(5),
entry.status.padEnd(12),
entry.title
)
}
console.table(entries, keys)
}

private async applyMutation(
Expand Down Expand Up @@ -246,9 +247,8 @@ export class Database implements Syncable {
await tx.delete(EntryRow).where(condition)
await tx.insert(EntryRow).values(entry)
let children: Array<EntryRow> = []
if (entry.status === EntryStatus.Published) {
if (current) children = await this.updateChildren(tx, current, entry)
}
if (entry.status === EntryStatus.Published && current)
children = await this.updateChildren(tx, current, entry)
return () => {
return this.updateHash(tx, condition).then(self =>
this.updateHash(
Expand Down Expand Up @@ -301,12 +301,21 @@ export class Database implements Syncable {
await tx.delete(EntryRow).where(archived)
await tx
.update(EntryRow)
.set({
status: EntryStatus.Archived,
filePath
})
.set({status: EntryStatus.Archived, filePath})
.where(condition)
return () => this.updateHash(tx, archived)
const children = await tx
.update(EntryRow)
.set({status: EntryStatus.Archived})
.where(
eq(EntryRow.status, EntryStatus.Published),
or(
eq(EntryRow.parentDir, published.childrenDir),
like(EntryRow.childrenDir, published.childrenDir + '/%')
)
)
.returning(EntryRow.id)
return () =>
this.updateHash(tx, or(archived, inArray(EntryRow.id, children)))
}
case MutationType.Publish: {
const promoting = await tx
Expand Down Expand Up @@ -673,7 +682,6 @@ export class Database implements Syncable {
seeded: EntryRow.seeded
})
.from(EntryRow)

.where(
eq(EntryRow.filePath, file.filePath),
eq(EntryRow.workspace, file.workspace),
Expand All @@ -697,9 +705,7 @@ export class Database implements Syncable {
try {
const raw = JsonLoader.parse(this.config.schema, file.contents)
const {meta, data, v0Id} = parseRecord(raw)
if (v0Id) {
v0Ids.set(v0Id, meta.id)
}
if (v0Id) v0Ids.set(v0Id, meta.id)
const seeded = meta.seeded
const key = seedKey(
file.workspace,
Expand Down Expand Up @@ -743,6 +749,7 @@ export class Database implements Syncable {
seenVersions.push([entry.id, entry.locale ?? 'null', entry.status])
inserted.push([entry.id, entry.locale ?? 'null', entry.status])
} catch (e: any) {
// Reminder: this runs browser side too so cannot use reportHalt here
console.warn(`${e.message} @ ${file.filePath}`)
process.exit(1)
}
Expand Down Expand Up @@ -842,14 +849,26 @@ export class Database implements Syncable {
EntryRow.locale,
sql`'null'`
)}, ${EntryRow.status}) in ${values(...inserted)}`
const archivedPaths = await tx
.select(EntryRow.childrenDir)
.from(EntryRow)
.where(eq(EntryRow.status, EntryStatus.Archived))
for (const archivedPath of archivedPaths) {
const isChildOf = or(
eq(EntryRow.parentDir, archivedPath),
like(EntryRow.childrenDir, archivedPath + '/%')
)
await tx
.update(EntryRow)
.set({status: EntryStatus.Archived})
.where(isChildOf, eq(EntryRow.status, EntryStatus.Published))
}
const entries = await tx.select().from(EntryRow).where(isInserted)
for (const entry of entries) {
const rowHash = await createRowHash(entry)
await tx
.update(EntryRow)
.set({
rowHash
})
.set({rowHash})
.where(
eq(EntryRow.id, entry.id),
is(EntryRow.locale, entry.locale),
Expand Down
4 changes: 2 additions & 2 deletions src/backend/Store.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {Database} from 'rado'
import {SyncDatabase} from 'rado'

export type Store = Database
export type Store = SyncDatabase<'sqlite'>
20 changes: 16 additions & 4 deletions src/dashboard/atoms/EntryEditorAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ export const entryEditorAtoms = atomFamily(
parents: {},
select: {
id: Entry.id,
path: Entry.path
path: Entry.path,
status: Entry.status
}
}
},
Expand All @@ -178,7 +179,14 @@ export const entryEditorAtoms = atomFamily(
locale: searchLocale,
status: 'preferDraft'
}))
const untranslated = Boolean(
entry.locale && searchLocale !== entry.locale
)
const parentNeedsTranslation = entry.parentId ? !parentLink : false
const parents = withParents?.parents ?? []
const canPublish = parents.every(
parent => parent.status === EntryStatus.Published
)
if (versions.length === 0) return undefined
const statuses = fromEntries(
versions.map(version => [version.status, version])
Expand All @@ -187,8 +195,10 @@ export const entryEditorAtoms = atomFamily(
status => statuses[status] !== undefined
)
return createEntryEditor({
parents: withParents?.parents ?? [],
parents,
canPublish,
translations,
untranslated,
parentNeedsTranslation,
client,
config,
Expand All @@ -204,14 +214,16 @@ export const entryEditorAtoms = atomFamily(
)

export interface EntryData {
parents: Array<{id: string; path: string}>
parents: Array<{id: string; path: string; status: EntryStatus}>
client: Connection
config: Config
entryId: string
versions: Array<Version>
statuses: Record<EntryStatus, Version>
availableStatuses: Array<EntryStatus>
translations: Array<{locale: string; entryId: string}>
untranslated: boolean
canPublish: boolean
parentNeedsTranslation: boolean
edits: Edits
}
Expand Down Expand Up @@ -725,7 +737,7 @@ export function createEntryEditor(entryData: EntryData) {
})
const form = atom(get => {
const doc = get(currentDoc)
const readOnly = doc !== edits.doc ? true : undefined
const readOnly = doc !== edits.doc ? true : !entryData.canPublish
return new FormAtoms(type, doc.getMap(DOC_KEY), '', {readOnly})
})

Expand Down
7 changes: 3 additions & 4 deletions src/dashboard/view/EntryEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,9 @@ export function EntryEdit({editor}: EntryEditProps) {
useEffect(() => {
ref.current?.scrollTo({top: 0})
}, [editor.entryId, mode, selectedStatus])
const untranslated = locale && locale !== editor.activeVersion.locale
const {isBlocking, nextRoute, confirm, cancel} = useRouteBlocker(
'Are you sure you want to discard changes?',
!untranslated && hasChanges
!editor.untranslated && hasChanges
)
const isNavigationChange =
(nextRoute?.data.editor as EntryEditor)?.entryId !== editor.entryId
Expand All @@ -93,7 +92,7 @@ export function EntryEdit({editor}: EntryEditProps) {
alert('todo')
return
}
if (untranslated && hasChanges) {
if (editor.untranslated && hasChanges) {
translate()
} else if (config.enableDrafts) {
if (hasChanges) saveDraft()
Expand Down Expand Up @@ -219,7 +218,7 @@ export function EntryEdit({editor}: EntryEditProps) {
)}
</EntryTitle>
<Main.Container>
{untranslated && (
{editor.untranslated && (
<div>
<EntryNotice
icon={IcRoundTranslate}
Expand Down
21 changes: 12 additions & 9 deletions src/dashboard/view/entry/EntryHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface EntryHeaderProps {
export function EntryHeader({editor, editable = true}: EntryHeaderProps) {
const config = useConfig()
const locale = useLocale()
const {canPublish, untranslated, parentNeedsTranslation} = editor
const statusInUrl = useAtomValue(editor.statusInUrl)
const selectedStatus = useAtomValue(editor.selectedStatus)
const previewRevision = useAtomValue(editor.previewRevision)
Expand All @@ -83,7 +84,6 @@ export function EntryHeader({editor, editable = true}: EntryHeaderProps) {
const isMediaLibrary = editor.activeVersion.type === 'MediaLibrary'
const hasChanges = useAtomValue(editor.hasChanges)
const currentTransition = useAtomValue(editor.transition)?.transition
const untranslated = locale && locale !== editor.activeVersion.locale
const variant = currentTransition
? 'transition'
: previewRevision
Expand Down Expand Up @@ -192,12 +192,14 @@ export function EntryHeader({editor, editable = true}: EntryHeaderProps) {
)
) : variant === EntryStatus.Archived ? (
<>
<DropdownMenu.Item
className={styles.root.action()}
onClick={publishArchived}
>
Publish
</DropdownMenu.Item>
{canPublish && (
<DropdownMenu.Item
className={styles.root.action()}
onClick={publishArchived}
>
Publish
</DropdownMenu.Item>
)}
<DropdownMenu.Item
className={styles.root.action()}
onClick={deleteArchived}
Expand Down Expand Up @@ -265,7 +267,8 @@ export function EntryHeader({editor, editable = true}: EntryHeaderProps) {
!hasChanges &&
isActiveStatus &&
!untranslated &&
!previewRevision && (
!previewRevision &&
canPublish && (
<>
<span className={styles.root.description.separator()} />
<div className={styles.root.description.action()}>
Expand All @@ -288,7 +291,7 @@ export function EntryHeader({editor, editable = true}: EntryHeaderProps) {

{!currentTransition &&
untranslated &&
!editor.parentNeedsTranslation &&
!parentNeedsTranslation &&
!hasChanges && (
<>
<span className={styles.root.description.separator()} />
Expand Down
Loading