page.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. import { defineStore } from 'pinia'
  2. import gql from 'graphql-tag'
  3. import { cloneDeep, initial, last, pick, transform } from 'lodash-es'
  4. import { DateTime } from 'luxon'
  5. import { useSiteStore } from './site'
  6. import { useEditorStore } from './editor'
  7. const pagePropsFragment = gql`
  8. fragment PageRead on Page {
  9. allowComments
  10. allowContributions
  11. allowRatings
  12. contentType
  13. createdAt
  14. description
  15. editor
  16. icon
  17. id
  18. isBrowsable
  19. locale
  20. password
  21. path
  22. publishEndDate
  23. publishStartDate
  24. publishState
  25. relations {
  26. id
  27. position
  28. label
  29. caption
  30. icon
  31. target
  32. }
  33. render
  34. scriptJsLoad
  35. scriptJsUnload
  36. scriptCss
  37. showSidebar
  38. showTags
  39. showToc
  40. tags {
  41. tag
  42. title
  43. }
  44. title
  45. toc
  46. tocDepth {
  47. min
  48. max
  49. }
  50. updatedAt
  51. }
  52. `
  53. const gqlQueries = {
  54. pageById: gql`
  55. query loadPage (
  56. $id: UUID!
  57. ) {
  58. pageById(
  59. id: $id
  60. ) {
  61. ...PageRead
  62. }
  63. }
  64. ${pagePropsFragment}
  65. `,
  66. pageByPath: gql`
  67. query loadPage (
  68. $siteId: UUID!
  69. $path: String!
  70. ) {
  71. pageByPath(
  72. siteId: $siteId
  73. path: $path
  74. ) {
  75. ...PageRead
  76. }
  77. }
  78. ${pagePropsFragment}
  79. `,
  80. pageByIdWithContent: gql`
  81. query loadPageWithContent (
  82. $id: UUID!
  83. ) {
  84. pageById(
  85. id: $id
  86. ) {
  87. ...PageRead,
  88. content
  89. }
  90. }
  91. ${pagePropsFragment}
  92. `,
  93. pageByPathWithContent: gql`
  94. query loadPageWithContent (
  95. $siteId: UUID!
  96. $path: String!
  97. ) {
  98. pageByPath(
  99. siteId: $siteId
  100. path: $path
  101. ) {
  102. ...PageRead,
  103. content
  104. }
  105. }
  106. ${pagePropsFragment}
  107. `
  108. }
  109. export const usePageStore = defineStore('page', {
  110. state: () => ({
  111. allowComments: false,
  112. allowContributions: true,
  113. allowRatings: true,
  114. authorId: 0,
  115. authorName: '',
  116. commentsCount: 0,
  117. content: '',
  118. createdAt: '',
  119. description: '',
  120. editor: '',
  121. icon: 'las la-file-alt',
  122. id: '',
  123. isBrowsable: true,
  124. locale: 'en',
  125. password: '',
  126. path: '',
  127. publishEndDate: '',
  128. publishStartDate: '',
  129. publishState: '',
  130. relations: [],
  131. render: '',
  132. scriptJsLoad: '',
  133. scriptJsUnload: '',
  134. scriptCss: '',
  135. showSidebar: true,
  136. showTags: true,
  137. showToc: true,
  138. tags: [],
  139. title: '',
  140. toc: [],
  141. tocDepth: {
  142. min: 1,
  143. max: 2
  144. },
  145. updatedAt: ''
  146. }),
  147. getters: {
  148. breadcrumbs: (state) => {
  149. const siteStore = useSiteStore()
  150. const pathPrefix = siteStore.useLocales ? `/${state.locale}` : ''
  151. return transform(state.path.split('/'), (result, value, key) => {
  152. result.push({
  153. id: key,
  154. title: value,
  155. icon: 'las la-file-alt',
  156. locale: 'en',
  157. path: (last(result)?.path || pathPrefix) + `/${value}`
  158. })
  159. }, [])
  160. },
  161. folderPath: (state) => {
  162. return initial(state.path.split('/')).join('/')
  163. }
  164. },
  165. actions: {
  166. /**
  167. * PAGE - LOAD
  168. */
  169. async pageLoad ({ path, id, withContent = false }) {
  170. const editorStore = useEditorStore()
  171. const siteStore = useSiteStore()
  172. try {
  173. let query
  174. if (withContent) {
  175. query = id ? gqlQueries.pageByIdWithContent : gqlQueries.pageByPathWithContent
  176. } else {
  177. query = id ? gqlQueries.pageById : gqlQueries.pageByPath
  178. }
  179. const resp = await APOLLO_CLIENT.query({
  180. query,
  181. variables: id ? { id } : { siteId: siteStore.id, path },
  182. fetchPolicy: 'network-only'
  183. })
  184. const pageData = cloneDeep((id ? resp?.data?.pageById : resp?.data?.pageByPath) ?? {})
  185. if (!pageData?.id) {
  186. throw new Error('ERR_PAGE_NOT_FOUND')
  187. }
  188. // Update page store
  189. this.$patch({
  190. ...pageData,
  191. relations: pageData.relations.map(r => pick(r, ['id', 'position', 'label', 'caption', 'icon', 'target'])),
  192. tocDepth: pick(pageData.tocDepth, ['min', 'max'])
  193. })
  194. // Update editor state timestamps
  195. const curDate = DateTime.utc()
  196. editorStore.$patch({
  197. lastChangeTimestamp: curDate,
  198. lastSaveTimestamp: curDate
  199. })
  200. } catch (err) {
  201. console.warn(err)
  202. throw err
  203. }
  204. },
  205. /**
  206. * PAGE - CREATE
  207. */
  208. pageCreate ({ editor, locale, path, title = '', description = '', content = '' }) {
  209. const editorStore = useEditorStore()
  210. // if (['markdown', 'api'].includes(editor)) {
  211. // commit('site/SET_SHOW_SIDE_NAV', false, { root: true })
  212. // } else {
  213. // commit('site/SET_SHOW_SIDE_NAV', true, { root: true })
  214. // }
  215. // if (['markdown', 'channel', 'api'].includes(editor)) {
  216. // commit('site/SET_SHOW_SIDEBAR', false, { root: true })
  217. // } else {
  218. // commit('site/SET_SHOW_SIDEBAR', true, { root: true })
  219. // }
  220. // -> Page Data
  221. this.id = 0
  222. this.locale = locale || this.locale
  223. if (path || path === '') {
  224. this.path = path
  225. } else {
  226. this.path = this.path.length < 2 ? 'new-page' : `${this.path}/new-page`
  227. }
  228. this.title = title ?? ''
  229. this.description = description ?? ''
  230. this.icon = 'las la-file-alt'
  231. this.publishState = 'published'
  232. this.relations = []
  233. this.tags = []
  234. this.content = content ?? ''
  235. this.render = ''
  236. // -> View Mode
  237. this.mode = 'edit'
  238. // -> Editor Mode
  239. editorStore.$patch({
  240. isActive: true,
  241. editor,
  242. mode: 'create'
  243. })
  244. },
  245. /**
  246. * PAGE SAVE
  247. */
  248. async pageSave () {
  249. const editorStore = useEditorStore()
  250. const siteStore = useSiteStore()
  251. try {
  252. if (editorStore.mode === 'create') {
  253. const resp = await APOLLO_CLIENT.mutate({
  254. mutation: gql`
  255. mutation createPage (
  256. $allowComments: Boolean
  257. $allowContributions: Boolean
  258. $allowRatings: Boolean
  259. $content: String!
  260. $description: String!
  261. $editor: String!
  262. $icon: String
  263. $isBrowsable: Boolean
  264. $locale: String!
  265. $path: String!
  266. $publishState: PagePublishState!
  267. $publishEndDate: Date
  268. $publishStartDate: Date
  269. $relations: [PageRelationInput!]
  270. $scriptCss: String
  271. $scriptJsLoad: String
  272. $scriptJsUnload: String
  273. $showSidebar: Boolean
  274. $showTags: Boolean
  275. $showToc: Boolean
  276. $siteId: UUID!
  277. $tags: [String!]
  278. $title: String!
  279. $tocDepth: PageTocDepthInput
  280. ) {
  281. createPage (
  282. allowComments: $allowComments
  283. allowContributions: $allowContributions
  284. allowRatings: $allowRatings
  285. content: $content
  286. description: $description
  287. editor: $editor
  288. icon: $icon
  289. isBrowsable: $isBrowsable
  290. locale: $locale
  291. path: $path
  292. publishState: $publishState
  293. publishEndDate: $publishEndDate
  294. publishStartDate: $publishStartDate
  295. relations: $relations
  296. scriptCss: $scriptCss
  297. scriptJsLoad: $scriptJsLoad
  298. scriptJsUnload: $scriptJsUnload
  299. showSidebar: $showSidebar
  300. showTags: $showTags
  301. showToc: $showToc
  302. siteId: $siteId
  303. tags: $tags
  304. title: $title
  305. tocDepth: $tocDepth
  306. ) {
  307. operation {
  308. succeeded
  309. message
  310. }
  311. }
  312. }
  313. `,
  314. variables: {
  315. ...pick(this, [
  316. 'allowComments',
  317. 'allowContributions',
  318. 'allowRatings',
  319. 'content',
  320. 'description',
  321. 'icon',
  322. 'isBrowsable',
  323. 'locale',
  324. 'password',
  325. 'path',
  326. 'publishEndDate',
  327. 'publishStartDate',
  328. 'publishState',
  329. 'relations',
  330. 'scriptJsLoad',
  331. 'scriptJsUnload',
  332. 'scriptCss',
  333. 'showSidebar',
  334. 'showTags',
  335. 'showToc',
  336. 'tags',
  337. 'title',
  338. 'tocDepth'
  339. ]),
  340. editor: editorStore.editor,
  341. siteId: siteStore.id
  342. }
  343. })
  344. const result = resp?.data?.createPage?.operation ?? {}
  345. if (!result.succeeded) {
  346. throw new Error(result.message)
  347. }
  348. this.id = resp.data.createPage.page.id
  349. this.editor = editorStore.editor
  350. } else {
  351. const resp = await APOLLO_CLIENT.mutate({
  352. mutation: gql`
  353. mutation savePage (
  354. $id: UUID!
  355. $patch: PageUpdateInput!
  356. ) {
  357. updatePage (
  358. id: $id
  359. patch: $patch
  360. ) {
  361. operation {
  362. succeeded
  363. message
  364. }
  365. }
  366. }
  367. `,
  368. variables: {
  369. id: this.id,
  370. patch: pick(this, [
  371. 'allowComments',
  372. 'allowContributions',
  373. 'allowRatings',
  374. 'content',
  375. 'description',
  376. 'icon',
  377. 'isBrowsable',
  378. 'locale',
  379. 'password',
  380. 'path',
  381. 'publishEndDate',
  382. 'publishStartDate',
  383. 'publishState',
  384. 'relations',
  385. 'scriptJsLoad',
  386. 'scriptJsUnload',
  387. 'scriptCss',
  388. 'showSidebar',
  389. 'showTags',
  390. 'showToc',
  391. 'tags',
  392. 'title',
  393. 'tocDepth'
  394. ])
  395. }
  396. })
  397. const result = resp?.data?.updatePage?.operation ?? {}
  398. if (!result.succeeded) {
  399. throw new Error(result.message)
  400. }
  401. }
  402. // Update editor state timestamps
  403. const curDate = DateTime.utc()
  404. editorStore.$patch({
  405. lastChangeTimestamp: curDate,
  406. lastSaveTimestamp: curDate
  407. })
  408. } catch (err) {
  409. console.warn(err)
  410. throw err
  411. }
  412. },
  413. generateToc () {
  414. }
  415. }
  416. })