@fiduswriter/image-manager 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/copyright_dialog/index.d.ts +10 -13
  2. package/dist/copyright_dialog/index.d.ts.map +1 -1
  3. package/dist/copyright_dialog/index.js +32 -14
  4. package/dist/copyright_dialog/index.js.map +1 -1
  5. package/dist/copyright_dialog/templates.d.ts +16 -12
  6. package/dist/copyright_dialog/templates.d.ts.map +1 -1
  7. package/dist/copyright_dialog/templates.js.map +1 -1
  8. package/dist/database.d.ts +7 -6
  9. package/dist/database.d.ts.map +1 -1
  10. package/dist/database.js +17 -11
  11. package/dist/database.js.map +1 -1
  12. package/dist/edit_dialog/index.d.ts +22 -19
  13. package/dist/edit_dialog/index.d.ts.map +1 -1
  14. package/dist/edit_dialog/index.js +65 -33
  15. package/dist/edit_dialog/index.js.map +1 -1
  16. package/dist/edit_dialog/model.d.ts +2 -19
  17. package/dist/edit_dialog/model.d.ts.map +1 -1
  18. package/dist/edit_dialog/model.js +51 -31
  19. package/dist/edit_dialog/model.js.map +1 -1
  20. package/dist/edit_dialog/templates.d.ts +7 -4
  21. package/dist/edit_dialog/templates.d.ts.map +1 -1
  22. package/dist/edit_dialog/templates.js.map +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/overview/categories.d.ts +7 -5
  26. package/dist/overview/categories.d.ts.map +1 -1
  27. package/dist/overview/categories.js +18 -13
  28. package/dist/overview/categories.js.map +1 -1
  29. package/dist/overview/index.d.ts +31 -27
  30. package/dist/overview/index.d.ts.map +1 -1
  31. package/dist/overview/index.js +64 -33
  32. package/dist/overview/index.js.map +1 -1
  33. package/dist/overview/menu.d.ts +4 -47
  34. package/dist/overview/menu.d.ts.map +1 -1
  35. package/dist/overview/menu.js +13 -8
  36. package/dist/overview/menu.js.map +1 -1
  37. package/dist/overview/templates.d.ts +7 -3
  38. package/dist/overview/templates.d.ts.map +1 -1
  39. package/dist/overview/templates.js.map +1 -1
  40. package/dist/selection_dialog/index.d.ts +17 -15
  41. package/dist/selection_dialog/index.d.ts.map +1 -1
  42. package/dist/selection_dialog/index.js +18 -8
  43. package/dist/selection_dialog/index.js.map +1 -1
  44. package/dist/types.d.ts +118 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +2 -0
  47. package/dist/types.js.map +1 -0
  48. package/package.json +3 -1
  49. package/src/copyright_dialog/{index.js → index.ts} +70 -38
  50. package/src/copyright_dialog/{templates.js → templates.ts} +19 -3
  51. package/src/database.ts +83 -0
  52. package/src/edit_dialog/{index.js → index.ts} +110 -45
  53. package/src/edit_dialog/{model.js → model.ts} +95 -46
  54. package/src/edit_dialog/{templates.js → templates.ts} +13 -2
  55. package/src/global.d.ts +37 -0
  56. package/src/overview/{categories.js → categories.ts} +34 -21
  57. package/src/overview/{index.js → index.ts} +157 -75
  58. package/src/overview/menu.ts +85 -0
  59. package/src/overview/{templates.js → templates.ts} +16 -2
  60. package/src/selection_dialog/{index.js → index.ts} +85 -49
  61. package/src/types.ts +133 -0
  62. package/src/database.js +0 -57
  63. package/src/overview/menu.js +0 -75
  64. /package/src/{index.js → index.ts} +0 -0
@@ -1,7 +1,19 @@
1
1
  import {escapeText} from "fwtoolkit"
2
2
 
3
+ import type {ImageCategory} from "../types.js"
4
+
5
+ interface CategoryFormsTemplateData {
6
+ categories: ImageCategory[]
7
+ }
8
+
9
+ interface EditcategoriesTemplateData {
10
+ categories: ImageCategory[]
11
+ }
12
+
3
13
  /** A template for the image category edit form. */
4
- const usermediaCategoryformsTemplate = ({categories}) =>
14
+ const usermediaCategoryformsTemplate = ({
15
+ categories
16
+ }: CategoryFormsTemplateData) =>
5
17
  `${categories
6
18
  .map(
7
19
  cat =>
@@ -22,7 +34,9 @@ const usermediaCategoryformsTemplate = ({categories}) =>
22
34
  </tr>`
23
35
 
24
36
  /** A template to edit image categories. */
25
- export const usermediaEditcategoriesTemplate = ({categories}) =>
37
+ export const usermediaEditcategoriesTemplate = ({
38
+ categories
39
+ }: EditcategoriesTemplateData) =>
26
40
  `<table id="editCategoryList" class="fw-dialog-table">
27
41
  <tbody>
28
42
  ${usermediaCategoryformsTemplate({categories})}
@@ -1,80 +1,113 @@
1
1
  import {Dialog, SelectionDataTable, cancelPromise, escapeText, gettext} from "fwtoolkit"
2
+ import type {DialogButtonSpec} from "fwtoolkit/basic"
3
+ import type {DataTable} from "simple-datatables"
4
+
5
+ import type {ImageDB} from "../database.js"
6
+ import type {
7
+ ImageManagerPage,
8
+ ImageSelectionItem
9
+ } from "../types.js"
2
10
 
3
11
  export class ImageSelectionDialog {
4
- constructor(imageDB, userImageDB, imgId, page) {
12
+ imageDB: ImageDB
13
+
14
+ userImageDB: ImageDB
15
+
16
+ page: ImageManagerPage
17
+
18
+ imgId: number | false
19
+
20
+ imgDb: "document" | "user" = "document"
21
+
22
+ images: ImageSelectionItem[] = []
23
+
24
+ imageDialog!: Dialog
25
+
26
+ selectionTable!: SelectionDataTable
27
+
28
+ table: DataTable | null = null
29
+
30
+ constructor(
31
+ imageDB: ImageDB,
32
+ userImageDB: ImageDB,
33
+ imgId: number | false,
34
+ page: ImageManagerPage
35
+ ) {
5
36
  this.imageDB = imageDB
6
37
  this.userImageDB = userImageDB
7
38
  this.page = page
8
39
  this.imgId = imgId // a preselected image
9
- this.imgDb = "document" // the preselection image will always come from the document
40
+ // the preselection image will always come from the document
10
41
  this.images = [] // images from both databases
11
42
  }
12
43
 
13
- isE2EE() {
44
+ isE2EE(): boolean {
14
45
  return this.page.e2ee?.encrypted === true
15
46
  }
16
47
 
17
- init() {
48
+ init(): Promise<unknown> {
18
49
  console.log(
19
50
  "DEBUG selection init: docImages=",
20
51
  Object.keys(this.imageDB.db).length,
21
52
  "userImages=",
22
53
  Object.keys(this.userImageDB.db).length
23
54
  )
24
- this.images = Object.values(this.imageDB.db).map(image => {
25
- return {
26
- image,
27
- db: "document"
28
- }
29
- })
55
+ this.images = Object.values(this.imageDB.db).map(image => ({
56
+ image,
57
+ db: "document" as const
58
+ }))
30
59
  Object.values(this.userImageDB.db).forEach(image => {
31
60
  if (this.imageDB.db[image.id]) {
32
61
  return
33
62
  }
34
63
  this.images.push({
35
64
  image,
36
- db: "user"
65
+ db: "user" as const
37
66
  })
38
67
  })
39
68
  console.log("DEBUG selection images=", this.images.length)
40
- const buttons = []
69
+ const buttons: DialogButtonSpec[] = []
41
70
  const p = new Promise(resolve => {
42
71
  if (!this.page.app.isOffline()) {
43
72
  buttons.push({
44
73
  text: gettext("Add new image"),
45
74
  icon: "plus-circle",
46
75
  click: () => {
47
- import("../edit_dialog/index.js").then(({ImageEditDialog}) => {
48
- const targetDB = this.isE2EE()
49
- ? this.imageDB
50
- : this.userImageDB
51
- const imageUpload = new ImageEditDialog(
52
- targetDB,
53
- false,
54
- this.page
55
- )
56
-
57
- resolve(
58
- imageUpload.init().then(imageId => {
59
- console.log(
60
- "DEBUG upload resolved imageId=",
61
- imageId
62
- )
63
- this.imgId = imageId
64
- // For E2EE docs the image goes straight
65
- // into the document DB, not the user's.
66
- this.imgDb = this.isE2EE()
67
- ? "document"
68
- : "user"
69
- console.log(
70
- "DEBUG closing selection dialog"
71
- )
72
- this.imageDialog.close()
73
- console.log("DEBUG reinit selection dialog")
74
- return this.init()
75
- })
76
- )
77
- })
76
+ import("../edit_dialog/index.js").then(
77
+ ({ImageEditDialog}) => {
78
+ const targetDB = this.isE2EE()
79
+ ? this.imageDB
80
+ : this.userImageDB
81
+ const imageUpload = new ImageEditDialog(
82
+ targetDB,
83
+ false,
84
+ this.page
85
+ )
86
+
87
+ resolve(
88
+ imageUpload.init().then(imageId => {
89
+ console.log(
90
+ "DEBUG upload resolved imageId=",
91
+ imageId
92
+ )
93
+ this.imgId = imageId || false
94
+ // For E2EE docs the image goes straight
95
+ // into the document DB, not the user's.
96
+ this.imgDb = this.isE2EE()
97
+ ? "document"
98
+ : "user"
99
+ console.log(
100
+ "DEBUG closing selection dialog"
101
+ )
102
+ this.imageDialog.close()
103
+ console.log(
104
+ "DEBUG reinit selection dialog"
105
+ )
106
+ return this.init()
107
+ })
108
+ )
109
+ }
110
+ )
78
111
  }
79
112
  })
80
113
  }
@@ -89,7 +122,7 @@ export class ImageSelectionDialog {
89
122
  })
90
123
 
91
124
  buttons.push({
92
- type: "cancel",
125
+ type: "cancel" as const,
93
126
  click: () => {
94
127
  this.imageDialog.close()
95
128
  resolve(cancelPromise())
@@ -109,14 +142,17 @@ export class ImageSelectionDialog {
109
142
  return p
110
143
  }
111
144
 
112
- initTable() {
145
+ initTable(): void {
113
146
  /* Initialize the overview table */
114
147
  const tableEl = document.createElement("table")
115
148
  tableEl.classList.add("fw-data-table")
116
149
  tableEl.classList.add("fw-small")
117
150
  const host = this.imageDialog.dialogEl.querySelector(
118
151
  "div.image-selection-table"
119
- )
152
+ ) as HTMLElement | null
153
+ if (!host) {
154
+ return
155
+ }
120
156
  host.innerHTML = ""
121
157
  host.appendChild(tableEl)
122
158
 
@@ -154,17 +190,17 @@ export class ImageSelectionDialog {
154
190
  if (selected.length) {
155
191
  const [db, id] = String(selected[0]).split("-")
156
192
  this.imgId = Number.parseInt(id)
157
- this.imgDb = db
193
+ this.imgDb = db as "document" | "user"
158
194
  } else {
159
195
  this.imgId = false
160
196
  }
161
197
  }
162
198
  })
163
199
  this.selectionTable.init()
164
- this.table = this.selectionTable.table
200
+ this.table = this.selectionTable.table!
165
201
  }
166
202
 
167
- createTableRow(image) {
203
+ createTableRow(image: ImageSelectionItem): [string, string, string] {
168
204
  return [
169
205
  `${image.db}-${image.image.id}`,
170
206
  image.image.thumbnail === undefined
package/src/types.ts ADDED
@@ -0,0 +1,133 @@
1
+ import type {DataTable} from "simple-datatables"
2
+ import type {ImageDB} from "./database.js"
3
+
4
+ /** A single license under which an image is available. */
5
+ export interface License {
6
+ url: string
7
+ title: string
8
+ start?: string | false
9
+ }
10
+
11
+ /** Copyright and licensing information for an image. */
12
+ export interface Copyright {
13
+ holder?: string | false
14
+ year?: number | false
15
+ freeToRead: boolean
16
+ licenses: License[]
17
+ }
18
+
19
+ /** A category used to group images. */
20
+ export interface ImageCategory {
21
+ id: number
22
+ category_title: string
23
+ }
24
+
25
+ /** A single image as stored in the client-side image database. */
26
+ export interface Image {
27
+ id: number
28
+ title: string
29
+ file_type: string
30
+ image: string
31
+ thumbnail?: string
32
+ width: number
33
+ height: number
34
+ added: number
35
+ cats: number[]
36
+ copyright: Copyright
37
+ [key: string]: unknown
38
+ }
39
+
40
+ /** End-to-end encryption context passed to dialogs that handle images. */
41
+ export interface E2EEContext {
42
+ encrypted: boolean
43
+ key: CryptoKey
44
+ }
45
+
46
+ /** Subset of the main Fidus Writer app object used by image-manager code. */
47
+ export interface ImageManagerApp {
48
+ imageDB: ImageDB
49
+ isOffline: () => boolean
50
+ settings: {
51
+ APPS: string[]
52
+ MEDIA_MAX_SIZE?: number
53
+ [key: string]: unknown
54
+ }
55
+ name: string
56
+ [key: string]: unknown
57
+ }
58
+
59
+ /** Page object passed to image dialogs. */
60
+ export interface ImageManagerPage {
61
+ app: ImageManagerApp
62
+ e2ee?: E2EEContext
63
+ menu?: unknown
64
+ [key: string]: unknown
65
+ }
66
+
67
+ /** Plugin instance created by the overview's plugin activation. */
68
+ export interface ImageOverviewPlugin {
69
+ init?: () => Promise<unknown> | unknown
70
+ [key: string]: unknown
71
+ }
72
+
73
+ /** Constructor for an image-overview plugin. */
74
+ export interface ImageOverviewPluginConstructor {
75
+ new (overview: unknown): ImageOverviewPlugin
76
+ }
77
+
78
+ /** Row displayed in the image overview data table. */
79
+ export type ImageTableRow = [
80
+ id: number,
81
+ selected: boolean,
82
+ file: string,
83
+ size: string,
84
+ added: string,
85
+ actions: string
86
+ ]
87
+
88
+ /** An image selection item coming from either the document or user DB. */
89
+ export interface ImageSelectionItem {
90
+ image: Image
91
+ db: "document" | "user"
92
+ }
93
+
94
+ /** Response body from `POST /api/usermedia/images/`. */
95
+ export interface ImagesResponse {
96
+ imageCategories: ImageCategory[]
97
+ images: Image[]
98
+ }
99
+
100
+ /** Response body from `POST /api/usermedia/save/`. */
101
+ export interface SaveImageResponse {
102
+ errormsg: Record<string, string> & {error?: string}
103
+ values: Image
104
+ }
105
+
106
+ /** Request body for `POST /api/usermedia/save/`. */
107
+ export interface SaveImageRequest {
108
+ id?: number
109
+ title: string
110
+ copyright: Copyright | string
111
+ cats: (string | number)[]
112
+ image?: File | Blob
113
+ original_file_type?: string
114
+ [key: string]: unknown
115
+ }
116
+
117
+ /** Request body for `POST /api/usermedia/save_category/`. */
118
+ export interface SaveCategoriesRequest {
119
+ ids: (string | number)[]
120
+ titles: string[]
121
+ }
122
+
123
+ /** Response body from `POST /api/usermedia/save_category/`. */
124
+ export interface SaveCategoriesResponse {
125
+ entries: ImageCategory[]
126
+ }
127
+
128
+ /** Request body for `POST /api/usermedia/delete/`. */
129
+ export interface DeleteImagesRequest {
130
+ ids: (string | number)[]
131
+ }
132
+
133
+ export type {DataTable}
package/src/database.js DELETED
@@ -1,57 +0,0 @@
1
- import {activateWait, addAlert, deactivateWait, gettext, postJson} from "fwtoolkit"
2
-
3
- /* A class that holds information about images uploaded by the user. */
4
-
5
- export class ImageDB {
6
- constructor(app) {
7
- this.app = app
8
- this.db = {}
9
- this.cats = []
10
- }
11
-
12
- getDB() {
13
- this.db = {}
14
- this.cats = []
15
-
16
- activateWait()
17
-
18
- return postJson("/api/usermedia/images/").then(({json}) => {
19
- this.cats = json.imageCategories
20
- json.images.forEach(image => {
21
- this.db[image.id] = image
22
- })
23
- deactivateWait()
24
- return
25
- })
26
- }
27
-
28
- saveImage(imageData) {
29
- activateWait()
30
- const {image, ...jsonData} = imageData
31
-
32
- return postJson("/api/usermedia/save/", jsonData, image ? {image} : {})
33
- .then(({json}) => {
34
- deactivateWait()
35
- if (Object.keys(json.errormsg).length) {
36
- return Promise.reject(new Error(json.errormsg.error))
37
- } else {
38
- this.db[json.values.id] = json.values
39
- return json.values.id
40
- }
41
- })
42
- .catch(error => {
43
- if (error.status === 413) {
44
- addAlert(
45
- "error",
46
- `${gettext("Image is larger than the maximum permitted size")}${this.app.settings?.MEDIA_MAX_SIZE ? `: ${Number.parseInt(this.app.settings.MEDIA_MAX_SIZE / 1000000)}MB` : "."}`
47
- )
48
- } else if (error.message) {
49
- addAlert("error", gettext(error.message))
50
- } else {
51
- addAlert("error", gettext(error.statusText))
52
- }
53
- deactivateWait()
54
- throw error
55
- })
56
- }
57
- }
@@ -1,75 +0,0 @@
1
- import {gettext} from "fwtoolkit"
2
-
3
- export const bulkMenuModel = () => ({
4
- content: [
5
- {
6
- title: gettext("Delete selected"),
7
- tooltip: gettext("Delete selected images."),
8
- action: overview => {
9
- const ids = overview.getSelected()
10
- if (ids.length) {
11
- overview.deleteImageDialog(ids)
12
- }
13
- },
14
- disabled: overview =>
15
- !overview.getSelected().length || overview.app.isOffline()
16
- }
17
- ]
18
- })
19
-
20
- export const menuModel = () => ({
21
- content: [
22
- {
23
- type: "dropdown",
24
- id: "cat_selector",
25
- keys: "Alt-c",
26
- content: [
27
- {
28
- title: gettext("All categories"),
29
- action: _overview => {
30
- const trs = document.querySelectorAll(
31
- "#imagelist > tbody > tr"
32
- )
33
- trs.forEach(tr => (tr.style.display = ""))
34
- }
35
- }
36
- ],
37
- order: 1
38
- },
39
- {
40
- type: "text",
41
- title: gettext("Edit categories"),
42
- keys: "Alt-e",
43
- action: overview => overview.mod.categories.editCategoryDialog(),
44
- order: 2,
45
- disabled: overview => overview.app.isOffline()
46
- },
47
- {
48
- type: "text",
49
- title: gettext("Upload new image"),
50
- keys: "Alt-u",
51
- action: overview => {
52
- import("../edit_dialog/index.js").then(({ImageEditDialog}) => {
53
- const imageUpload = new ImageEditDialog(
54
- overview.app.imageDB,
55
- false,
56
- overview
57
- )
58
- imageUpload.init().then(imageId => {
59
- overview.updateTable([imageId])
60
- })
61
- })
62
- },
63
- order: 3,
64
- disabled: overview => overview.app.isOffline()
65
- },
66
- {
67
- type: "search",
68
- icon: "search",
69
- title: gettext("Search images"),
70
- keys: "Alt-s",
71
- input: (overview, text) => overview.table.search(text),
72
- order: 4
73
- }
74
- ]
75
- })
File without changes