Skip to content

Commit 3afe813

Browse files
committed
Primitively implement main cms flow
If the cms is started with ssgOptions, then proceed to initing the editor with the rootDirectory (started from $ writ start) Otherwise ($ writ create or desktop app), try to find the most recent project in a workspace directory (writ projects). If found, init the editor with the project's path. Else, proceed to onboarding. Onboarding makes sure the workspace + a new project now exist. While at it: - Keep watched directory in state so when ssg.watch() is called, we can check if a specific directory is watched or not. And if a different directory was being watched when ssg.watch() is called, stop the watcher and start watching the new one. - Pass the api object itself to its members, so they can sphagettify
1 parent fe9e364 commit 3afe813

8 files changed

Lines changed: 217 additions & 23 deletions

File tree

src/cms/api/index.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
const createAPI = (providers) => {
2-
return {
3-
settings: require('./models/settings')(providers),
4-
fileSystemTree: require('./models/fileSystemTree')(providers),
5-
contentModel: require('./models/contentModel')(providers),
6-
collections: require('./models/collections')(providers),
7-
categories: require('./models/categories')(providers),
8-
category: require('./models/category')(providers),
9-
posts: require('./models/posts')(providers),
10-
post: require('./models/post')(providers),
11-
subpages: require('./models/subpages')(providers),
12-
subpage: require('./models/subpage')(providers),
13-
homepage: require('./models/homepage')(providers),
14-
tags: require('./models/tags')(providers),
15-
tag: require('./models/tag')(providers),
16-
ssg: require('./models/ssg')(providers),
17-
ssgOptions: require('./models/ssgOptions')(providers),
18-
}
2+
const api = {}
3+
api.settings = require('./models/settings')(providers, api)
4+
api.fileSystemTree = require('./models/fileSystemTree')(providers, api)
5+
api.contentModel = require('./models/contentModel')(providers, api)
6+
api.collections = require('./models/collections')(providers, api)
7+
api.categories = require('./models/categories')(providers, api)
8+
api.category = require('./models/category')(providers, api)
9+
api.posts = require('./models/posts')(providers, api)
10+
api.post = require('./models/post')(providers, api)
11+
api.subpages = require('./models/subpages')(providers, api)
12+
api.subpage = require('./models/subpage')(providers, api)
13+
api.homepage = require('./models/homepage')(providers, api)
14+
api.tags = require('./models/tags')(providers, api)
15+
api.tag = require('./models/tag')(providers, api)
16+
api.ssg = require('./models/ssg')(providers, api)
17+
api.ssgOptions = require('./models/ssgOptions')(providers, api)
18+
api.workspace = require('./models/workspace')(providers, api)
19+
return api
1920
}
2021

2122
module.exports = createAPI

src/cms/api/models/ssg.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ const createSSGModel = (state) => {
1515

1616
async watch({ rootDirectory, refreshTheme, debug, cli }) {
1717
if (state.isWatching()) {
18-
return false
18+
if (state.isWatching(rootDirectory)) {
19+
console.log(`is already watching, not gonna do a thing (watching: ${rootDirectory})`)
20+
return false
21+
}
22+
console.log(`was watching another. stopping it so now watch: ${rootDirectory}`)
23+
await this.stopWatcher()
1924
}
2025
const { result, watcher } = await ssg.watch({
2126
rootDirectory,
@@ -24,7 +29,10 @@ const createSSGModel = (state) => {
2429
cli,
2530
onChange: state.setState
2631
})
27-
state.startWatcher(watcher.stop)
32+
state.startWatcher({
33+
directory: rootDirectory,
34+
stop: watcher.stop
35+
})
2836
state.setState(result)
2937
return true
3038
},

src/cms/api/models/workspace.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { homedir } = require('os')
2+
const { join } = require('path')
3+
const { mkdir, readdir, stat } = require('fs/promises')
4+
5+
const createWorkspaceModel = (state) => {
6+
const directoryName = 'writ projects'
7+
const directoryPath = join(homedir(), directoryName)
8+
9+
const createProject = async (name) => {
10+
const path = join(directoryPath, name)
11+
const stats = await stat(path)
12+
return {
13+
name,
14+
path,
15+
dateModified: stats.mtime
16+
}
17+
}
18+
19+
return {
20+
async get() {
21+
try {
22+
const directory = await readdir(directoryPath, { withFileTypes: true })
23+
const isProject = item => item.isDirectory()
24+
const projects = await Promise.all(
25+
directory
26+
.filter(isProject)
27+
.map(item => createProject(item.name))
28+
.sort((a, b) => b.dateModified - a.dateModified)
29+
)
30+
return {
31+
projects
32+
}
33+
} catch (e) {
34+
return {}
35+
}
36+
},
37+
38+
async create() {
39+
try {
40+
console.log('api.workspace.creating workspace folder')
41+
await mkdir(directoryPath)
42+
} catch (e) {
43+
if (e.code !== 'EEXIST') {
44+
console.log('api.workspace.create.error', e)
45+
throw e
46+
}
47+
console.log('workspace already exists')
48+
}
49+
return this.get()
50+
},
51+
52+
async createProject({ name }) {
53+
await this.create()
54+
const path = join(directoryPath, name)
55+
await mkdir(path)
56+
return createProject(name)
57+
}
58+
}
59+
}
60+
61+
module.exports = createWorkspaceModel

src/cms/index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const createCMS = (initialState = {}) => {
99
fileSystemTree: [],
1010
contentModel: {},
1111
watcher: {
12+
directory: undefined,
1213
isRunning: false,
1314
stop: _=>_
1415
}
@@ -19,14 +20,21 @@ const createCMS = (initialState = {}) => {
1920
getFileSystemTree: () => state.fileSystemTree,
2021
getContentModel: () => state.contentModel,
2122
getSSGOptions: () => state.ssgOptions,
22-
isWatching: () => state.watcher.isRunning,
23+
isWatching: (directory) => {
24+
if (directory) {
25+
return state.watcher.isRunning && state.watcher.directory === directory
26+
}
27+
return state.watcher.isRunning
28+
},
2329
stopWatcher: () => {
2430
state.watcher.stop()
2531
state.watcher.isRunning = false
32+
state.watcher.directory = undefined
2633
},
27-
startWatcher: (stopFn) => {
34+
startWatcher: ({ directory, stop }) => {
35+
state.watcher.directory = directory
2836
state.watcher.isRunning = true
29-
state.watcher.stop = stopFn
37+
state.watcher.stop = stop
3038
},
3139

3240
setState: (newState) => {

src/cms/server/public/api.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,39 @@ const api = {
5353
}
5454
},
5555

56+
workspace: {
57+
get: async () => {
58+
const response = await fetch('/api/workspace', {
59+
method: 'get',
60+
headers: {
61+
'content-type': 'application/json'
62+
}
63+
})
64+
return response.json()
65+
},
66+
67+
create: async () => {
68+
const response = await fetch('/api/workspace', {
69+
method: 'post',
70+
headers: {
71+
'content-type': 'application/json'
72+
}
73+
})
74+
return response.json()
75+
},
76+
77+
createProject: async (options) => {
78+
const response = await fetch('/api/workspace/project', {
79+
method: 'post',
80+
headers: {
81+
'content-type': 'application/json'
82+
},
83+
body: JSON.stringify(options)
84+
})
85+
return response.json()
86+
}
87+
},
88+
5689
category: {
5790
get: async (options) => {
5891
const { name } = options

src/cms/server/public/app.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,54 @@
1+
import api from './api.js'
12
import { setIframeSrc } from './common.js'
23

3-
window.addEventListener('DOMContentLoaded', () => {
4+
const findMostRecentProject = async () => {
5+
console.log('checking workspace')
6+
const workspace = await api.workspace.get()
7+
if (!workspace.projects) {
8+
console.log('workspace not found')
9+
return undefined
10+
}
11+
if (!workspace.projects.length) {
12+
console.log('no project not found')
13+
return undefined
14+
}
15+
console.log('workspace projects', workspace.projects)
16+
return workspace.projects[0]
17+
}
18+
19+
const onboarding = async () => {
20+
console.log('first time onboarding')
21+
const newProject = await api.workspace.createProject({
22+
name: prompt('Project name')
23+
})
24+
return editProject({
25+
ssgOptions: {
26+
rootDirectory: newProject.path
27+
}
28+
})
29+
}
30+
31+
const editProject = async ({ ssgOptions }) => {
32+
console.log('starting editor with ssgOptions', ssgOptions)
33+
await api.ssg.watch(ssgOptions)
434
setIframeSrc()
35+
}
36+
37+
window.addEventListener('DOMContentLoaded', async () => {
38+
const ssgOptions = await api.ssgOptions.get()
39+
if (ssgOptions.rootDirectory) {
40+
return editProject({
41+
ssgOptions
42+
})
43+
}
44+
const mostRecentProject = await findMostRecentProject()
45+
if (mostRecentProject) {
46+
console.log('running most recent project', mostRecentProject)
47+
return editProject({
48+
ssgOptions: {
49+
rootDirectory: mostRecentProject.path
50+
}
51+
})
52+
}
53+
onboarding()
554
})

src/cms/server/router/api/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ module.exports = express.Router()
1616
.use('/tag', require('./tag'))
1717
.use('/ssg', require('./ssg'))
1818
.use('/ssgOptions', require('./ssgOptions'))
19+
.use('/workspace', require('./workspace'))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const express = require('express')
2+
3+
module.exports = express.Router()
4+
.get('/', async (req, res, next) => {
5+
try {
6+
res.status(200).json(
7+
await req.api.workspace.get()
8+
)
9+
} catch (e) {
10+
console.log('Error running workspace.get', e)
11+
res.status(500).send(e)
12+
}
13+
})
14+
.post('/', async (req, res, next) => {
15+
try {
16+
res.status(200).json(
17+
await req.api.workspace.create()
18+
)
19+
} catch (e) {
20+
console.log('Error running workspace.create', e)
21+
res.status(500).send(e)
22+
}
23+
})
24+
.post('/project', async (req, res, next) => {
25+
try {
26+
res.status(200).json(
27+
await req.api.workspace.createProject(req.body)
28+
)
29+
} catch (e) {
30+
console.log('Error running workspace.createProject', e)
31+
res.status(500).send(e)
32+
}
33+
})

0 commit comments

Comments
 (0)