@fiduswriter/image-manager 0.1.13 → 0.1.14

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 (48) hide show
  1. package/dist/copyright_dialog/index.d.ts.map +1 -1
  2. package/dist/copyright_dialog/index.js +19 -20
  3. package/dist/copyright_dialog/index.js.map +1 -1
  4. package/dist/copyright_dialog/templates.d.ts +2 -2
  5. package/dist/copyright_dialog/templates.d.ts.map +1 -1
  6. package/dist/copyright_dialog/templates.js +3 -3
  7. package/dist/copyright_dialog/templates.js.map +1 -1
  8. package/dist/database.d.ts.map +1 -1
  9. package/dist/database.js +7 -8
  10. package/dist/database.js.map +1 -1
  11. package/dist/edit_dialog/index.d.ts.map +1 -1
  12. package/dist/edit_dialog/index.js +25 -23
  13. package/dist/edit_dialog/index.js.map +1 -1
  14. package/dist/edit_dialog/model.d.ts.map +1 -1
  15. package/dist/edit_dialog/model.js +14 -18
  16. package/dist/edit_dialog/model.js.map +1 -1
  17. package/dist/edit_dialog/templates.d.ts.map +1 -1
  18. package/dist/edit_dialog/templates.js.map +1 -1
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/overview/categories.d.ts.map +1 -1
  23. package/dist/overview/categories.js +17 -20
  24. package/dist/overview/categories.js.map +1 -1
  25. package/dist/overview/index.d.ts.map +1 -1
  26. package/dist/overview/index.js +3 -2
  27. package/dist/overview/index.js.map +1 -1
  28. package/dist/overview/menu.d.ts.map +1 -1
  29. package/dist/overview/menu.js +13 -15
  30. package/dist/overview/menu.js.map +1 -1
  31. package/dist/selection_dialog/index.d.ts.map +1 -1
  32. package/dist/selection_dialog/index.js +22 -26
  33. package/dist/selection_dialog/index.js.map +1 -1
  34. package/dist/types.d.ts +10 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/package.json +8 -4
  37. package/src/copyright_dialog/index.ts +177 -186
  38. package/src/copyright_dialog/templates.ts +26 -19
  39. package/src/database.ts +61 -68
  40. package/src/edit_dialog/index.ts +261 -251
  41. package/src/edit_dialog/model.ts +220 -220
  42. package/src/edit_dialog/templates.ts +22 -22
  43. package/src/index.ts +5 -4
  44. package/src/overview/categories.ts +106 -116
  45. package/src/overview/index.ts +2 -2
  46. package/src/overview/menu.ts +77 -77
  47. package/src/selection_dialog/index.ts +197 -207
  48. package/src/types.ts +79 -65
@@ -1,212 +1,202 @@
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"
1
+ import {
2
+ Dialog,
3
+ SelectionDataTable,
4
+ cancelPromise,
5
+ escapeText,
6
+ gettext,
7
+ } from "fwtoolkit";
8
+ import type { DialogButtonSpec } from "fwtoolkit/basic";
9
+ import type { DataTable } from "simple-datatables";
10
+
11
+ import type { ImageDB } from "../database.js";
12
+ import type { ImageManagerPage, ImageSelectionItem } from "../types.js";
10
13
 
11
14
  export class ImageSelectionDialog {
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
- ) {
36
- this.imageDB = imageDB
37
- this.userImageDB = userImageDB
38
- this.page = page
39
- this.imgId = imgId // a preselected image
40
- // the preselection image will always come from the document
41
- this.images = [] // images from both databases
42
- }
43
-
44
- isE2EE(): boolean {
45
- return this.page.e2ee?.encrypted === true
46
- }
47
-
48
- init(): Promise<unknown> {
49
- console.log(
50
- "DEBUG selection init: docImages=",
51
- Object.keys(this.imageDB.db).length,
52
- "userImages=",
53
- Object.keys(this.userImageDB.db).length
54
- )
55
- this.images = Object.values(this.imageDB.db).map(image => ({
56
- image,
57
- db: "document" as const
58
- }))
59
- Object.values(this.userImageDB.db).forEach(image => {
60
- if (this.imageDB.db[image.id]) {
61
- return
62
- }
63
- this.images.push({
64
- image,
65
- db: "user" as const
66
- })
67
- })
68
- console.log("DEBUG selection images=", this.images.length)
69
- const buttons: DialogButtonSpec[] = []
70
- const p = new Promise(resolve => {
71
- if (!this.page.app.isOffline()) {
72
- buttons.push({
73
- text: gettext("Add new image"),
74
- icon: "plus-circle",
75
- click: () => {
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
- )
111
- }
112
- })
113
- }
114
-
115
- buttons.push({
116
- text: gettext("Use image"),
117
- classes: "fw-dark",
118
- click: () => {
119
- this.imageDialog.close()
120
- resolve({id: this.imgId, db: this.imgDb})
121
- }
122
- })
123
-
124
- buttons.push({
125
- type: "cancel" as const,
126
- click: () => {
127
- this.imageDialog.close()
128
- resolve(cancelPromise())
129
- }
130
- })
131
- })
132
- this.imageDialog = new Dialog({
133
- buttons,
134
- width: 300,
135
- body: '<div class="image-selection-table"></div>',
136
- title: gettext("Images"),
137
- id: "select-image-dialog"
138
- })
139
- this.imageDialog.open()
140
- this.initTable()
141
- this.imageDialog.centerDialog()
142
- return p
15
+ imageDB: ImageDB;
16
+
17
+ userImageDB: ImageDB;
18
+
19
+ page: ImageManagerPage;
20
+
21
+ imgId: number | false;
22
+
23
+ imgDb: "document" | "user" = "document";
24
+
25
+ images: ImageSelectionItem[] = [];
26
+
27
+ imageDialog!: Dialog;
28
+
29
+ selectionTable!: SelectionDataTable;
30
+
31
+ table: DataTable | null = null;
32
+
33
+ constructor(
34
+ imageDB: ImageDB,
35
+ userImageDB: ImageDB,
36
+ imgId: number | false,
37
+ page: ImageManagerPage,
38
+ ) {
39
+ this.imageDB = imageDB;
40
+ this.userImageDB = userImageDB;
41
+ this.page = page;
42
+ this.imgId = imgId; // a preselected image
43
+ // the preselection image will always come from the document
44
+ this.images = []; // images from both databases
45
+ }
46
+
47
+ isE2EE(): boolean {
48
+ return this.page.e2ee?.encrypted === true;
49
+ }
50
+
51
+ init(): Promise<unknown> {
52
+ console.log(
53
+ "DEBUG selection init: docImages=",
54
+ Object.keys(this.imageDB.db).length,
55
+ "userImages=",
56
+ Object.keys(this.userImageDB.db).length,
57
+ );
58
+ this.images = Object.values(this.imageDB.db).map((image) => ({
59
+ image,
60
+ db: "document" as const,
61
+ }));
62
+ Object.values(this.userImageDB.db).forEach((image) => {
63
+ if (this.imageDB.db[image.id]) {
64
+ return;
65
+ }
66
+ this.images.push({
67
+ image,
68
+ db: "user" as const,
69
+ });
70
+ });
71
+ console.log("DEBUG selection images=", this.images.length);
72
+ const buttons: DialogButtonSpec[] = [];
73
+ const p = new Promise((resolve) => {
74
+ if (!this.page.app.isOffline()) {
75
+ buttons.push({
76
+ text: gettext("Add new image"),
77
+ icon: "plus-circle",
78
+ click: () => {
79
+ import("../edit_dialog/index.js").then(({ ImageEditDialog }) => {
80
+ const targetDB = this.isE2EE() ? this.imageDB : 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("DEBUG upload resolved imageId=", imageId);
90
+ this.imgId = imageId || false;
91
+ // For E2EE docs the image goes straight
92
+ // into the document DB, not the user's.
93
+ this.imgDb = this.isE2EE() ? "document" : "user";
94
+ console.log("DEBUG closing selection dialog");
95
+ this.imageDialog.close();
96
+ console.log("DEBUG reinit selection dialog");
97
+ return this.init();
98
+ }),
99
+ );
100
+ });
101
+ },
102
+ });
103
+ }
104
+
105
+ buttons.push({
106
+ text: gettext("Use image"),
107
+ classes: "fw-dark",
108
+ click: () => {
109
+ this.imageDialog.close();
110
+ resolve({ id: this.imgId, db: this.imgDb });
111
+ },
112
+ });
113
+
114
+ buttons.push({
115
+ type: "cancel" as const,
116
+ click: () => {
117
+ this.imageDialog.close();
118
+ resolve(cancelPromise());
119
+ },
120
+ });
121
+ });
122
+ this.imageDialog = new Dialog({
123
+ buttons,
124
+ width: 300,
125
+ body: '<div class="image-selection-table"></div>',
126
+ title: gettext("Images"),
127
+ id: "select-image-dialog",
128
+ });
129
+ this.imageDialog.open();
130
+ this.initTable();
131
+ this.imageDialog.centerDialog();
132
+ return p;
133
+ }
134
+
135
+ initTable(): void {
136
+ /* Initialize the overview table */
137
+ const tableEl = document.createElement("table");
138
+ tableEl.classList.add("fw-data-table");
139
+ tableEl.classList.add("fw-small");
140
+ const host = this.imageDialog.dialogEl.querySelector(
141
+ "div.image-selection-table",
142
+ ) as HTMLElement | null;
143
+ if (!host) {
144
+ return;
143
145
  }
144
-
145
- initTable(): void {
146
- /* Initialize the overview table */
147
- const tableEl = document.createElement("table")
148
- tableEl.classList.add("fw-data-table")
149
- tableEl.classList.add("fw-small")
150
- const host = this.imageDialog.dialogEl.querySelector(
151
- "div.image-selection-table"
152
- ) as HTMLElement | null
153
- if (!host) {
154
- return
146
+ host.innerHTML = "";
147
+ host.appendChild(tableEl);
148
+
149
+ const selectedIds =
150
+ this.imgId === false ? [] : [`${this.imgDb}-${this.imgId}`];
151
+
152
+ this.selectionTable = new SelectionDataTable({
153
+ dom: host,
154
+ classes: ["fw-data-table", "fw-small"],
155
+ columns: [
156
+ {
157
+ select: 0,
158
+ hidden: true,
159
+ },
160
+ {
161
+ select: [0, 2],
162
+ type: "string",
163
+ },
164
+ {
165
+ select: [1, 3],
166
+ sortable: false,
167
+ },
168
+ ],
169
+ data: this.images.map((image) => this.createTableRow(image)),
170
+ idColumn: 0,
171
+ multiple: false,
172
+ selectedIds,
173
+ scrollY: "270px",
174
+ labels: {
175
+ noRows: gettext("No images available"), // Message shown when there are no images
176
+ noResults: gettext("No images found"), // Message shown when no images are found after search
177
+ placeholder: gettext("Search..."), // placeholder for search field
178
+ },
179
+ onChange: (selected) => {
180
+ if (selected.length) {
181
+ const [db, id] = String(selected[0]).split("-");
182
+ this.imgId = Number.parseInt(id);
183
+ this.imgDb = db as "document" | "user";
184
+ } else {
185
+ this.imgId = false;
155
186
  }
156
- host.innerHTML = ""
157
- host.appendChild(tableEl)
158
-
159
- const selectedIds =
160
- this.imgId === false ? [] : [`${this.imgDb}-${this.imgId}`]
161
-
162
- this.selectionTable = new SelectionDataTable({
163
- dom: host,
164
- classes: ["fw-data-table", "fw-small"],
165
- columns: [
166
- {
167
- select: 0,
168
- hidden: true
169
- },
170
- {
171
- select: [0, 2],
172
- type: "string"
173
- },
174
- {
175
- select: [1, 3],
176
- sortable: false
177
- }
178
- ],
179
- data: this.images.map(image => this.createTableRow(image)),
180
- idColumn: 0,
181
- multiple: false,
182
- selectedIds,
183
- scrollY: "270px",
184
- labels: {
185
- noRows: gettext("No images available"), // Message shown when there are no images
186
- noResults: gettext("No images found"), // Message shown when no images are found after search
187
- placeholder: gettext("Search...") // placeholder for search field
188
- },
189
- onChange: selected => {
190
- if (selected.length) {
191
- const [db, id] = String(selected[0]).split("-")
192
- this.imgId = Number.parseInt(id)
193
- this.imgDb = db as "document" | "user"
194
- } else {
195
- this.imgId = false
196
- }
197
- }
198
- })
199
- this.selectionTable.init()
200
- this.table = this.selectionTable.table!
201
- }
202
-
203
- createTableRow(image: ImageSelectionItem): [string, string, string] {
204
- return [
205
- `${image.db}-${image.image.id}`,
206
- image.image.thumbnail === undefined
207
- ? `<img src="${image.image.image}" style="max-heigth:30px;max-width:30px;">`
208
- : `<img src="${image.image.thumbnail}" style="max-heigth:30px;max-width:30px;">`,
209
- escapeText(image.image.title)
210
- ]
211
- }
187
+ },
188
+ });
189
+ this.selectionTable.init();
190
+ this.table = this.selectionTable.table!;
191
+ }
192
+
193
+ createTableRow(image: ImageSelectionItem): [string, string, string] {
194
+ return [
195
+ `${image.db}-${image.image.id}`,
196
+ image.image.thumbnail === undefined
197
+ ? `<img src="${image.image.image}" style="max-heigth:30px;max-width:30px;">`
198
+ : `<img src="${image.image.thumbnail}" style="max-heigth:30px;max-width:30px;">`,
199
+ escapeText(image.image.title),
200
+ ];
201
+ }
212
202
  }
package/src/types.ts CHANGED
@@ -1,133 +1,147 @@
1
- import type {DataTable} from "simple-datatables"
2
- import type {ImageDB} from "./database.js"
1
+ import type { DataTable } from "simple-datatables";
2
+ import type { ImageDB } from "./database.js";
3
3
 
4
4
  /** A single license under which an image is available. */
5
5
  export interface License {
6
- url: string
7
- title: string
8
- start?: string | false
6
+ url: string;
7
+ title: string;
8
+ start?: string | false;
9
9
  }
10
10
 
11
11
  /** Copyright and licensing information for an image. */
12
12
  export interface Copyright {
13
- holder?: string | false
14
- year?: number | false
15
- freeToRead: boolean
16
- licenses: License[]
13
+ holder?: string | false;
14
+ year?: number | false;
15
+ freeToRead: boolean;
16
+ licenses: License[];
17
17
  }
18
18
 
19
19
  /** A category used to group images. */
20
20
  export interface ImageCategory {
21
- id: number
22
- category_title: string
21
+ id: number;
22
+ category_title: string;
23
23
  }
24
24
 
25
25
  /** A single image as stored in the client-side image database. */
26
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
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
38
  }
39
39
 
40
40
  /** End-to-end encryption context passed to dialogs that handle images. */
41
41
  export interface E2EEContext {
42
- encrypted: boolean
43
- key: CryptoKey
42
+ encrypted: boolean;
43
+ key: CryptoKey;
44
+ }
45
+
46
+ /** API connector for image server operations. */
47
+ export interface ImageApi {
48
+ getImages(): Promise<ImagesResponse>;
49
+ saveImage(
50
+ data: SaveImageRequest,
51
+ files?: Record<string, unknown>,
52
+ ): Promise<SaveImageResponse>;
53
+ saveCategories(cats: SaveCategoriesRequest): Promise<SaveCategoriesResponse>;
54
+ deleteImages(ids: number[]): Promise<unknown>;
44
55
  }
45
56
 
46
57
  /** Subset of the main Fidus Writer app object used by image-manager code. */
47
58
  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
59
+ imageDB: ImageDB;
60
+ isOffline: () => boolean;
61
+ settings: {
62
+ APPS: string[];
63
+ MEDIA_MAX_SIZE?: number;
64
+ [key: string]: unknown;
65
+ };
66
+ name: string;
67
+ apiConnectors: {
68
+ image: ImageApi;
69
+ };
70
+ [key: string]: unknown;
57
71
  }
58
72
 
59
73
  /** Page object passed to image dialogs. */
60
74
  export interface ImageManagerPage {
61
- app: ImageManagerApp
62
- e2ee?: E2EEContext
63
- menu?: unknown
64
- [key: string]: unknown
75
+ app: ImageManagerApp;
76
+ e2ee?: E2EEContext;
77
+ menu?: unknown;
78
+ [key: string]: unknown;
65
79
  }
66
80
 
67
81
  /** Plugin instance created by the overview's plugin activation. */
68
82
  export interface ImageOverviewPlugin {
69
- init?: () => Promise<unknown> | unknown
70
- [key: string]: unknown
83
+ init?: () => Promise<unknown> | unknown;
84
+ [key: string]: unknown;
71
85
  }
72
86
 
73
87
  /** Constructor for an image-overview plugin. */
74
88
  export interface ImageOverviewPluginConstructor {
75
- new (overview: unknown): ImageOverviewPlugin
89
+ new (overview: unknown): ImageOverviewPlugin;
76
90
  }
77
91
 
78
92
  /** Row displayed in the image overview data table. */
79
93
  export type ImageTableRow = [
80
- id: number,
81
- selected: boolean,
82
- file: string,
83
- size: string,
84
- added: string,
85
- actions: string
86
- ]
94
+ id: number,
95
+ selected: boolean,
96
+ file: string,
97
+ size: string,
98
+ added: string,
99
+ actions: string,
100
+ ];
87
101
 
88
102
  /** An image selection item coming from either the document or user DB. */
89
103
  export interface ImageSelectionItem {
90
- image: Image
91
- db: "document" | "user"
104
+ image: Image;
105
+ db: "document" | "user";
92
106
  }
93
107
 
94
108
  /** Response body from `POST /api/usermedia/images/`. */
95
109
  export interface ImagesResponse {
96
- imageCategories: ImageCategory[]
97
- images: Image[]
110
+ imageCategories: ImageCategory[];
111
+ images: Image[];
98
112
  }
99
113
 
100
114
  /** Response body from `POST /api/usermedia/save/`. */
101
115
  export interface SaveImageResponse {
102
- errormsg: Record<string, string> & {error?: string}
103
- values: Image
116
+ errormsg: Record<string, string> & { error?: string };
117
+ values: Image;
104
118
  }
105
119
 
106
120
  /** Request body for `POST /api/usermedia/save/`. */
107
121
  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
122
+ id?: number;
123
+ title: string;
124
+ copyright: Copyright | string;
125
+ cats: (string | number)[];
126
+ image?: File | Blob;
127
+ original_file_type?: string;
128
+ [key: string]: unknown;
115
129
  }
116
130
 
117
131
  /** Request body for `POST /api/usermedia/save_category/`. */
118
132
  export interface SaveCategoriesRequest {
119
- ids: (string | number)[]
120
- titles: string[]
133
+ ids: (string | number)[];
134
+ titles: string[];
121
135
  }
122
136
 
123
137
  /** Response body from `POST /api/usermedia/save_category/`. */
124
138
  export interface SaveCategoriesResponse {
125
- entries: ImageCategory[]
139
+ entries: ImageCategory[];
126
140
  }
127
141
 
128
142
  /** Request body for `POST /api/usermedia/delete/`. */
129
143
  export interface DeleteImagesRequest {
130
- ids: (string | number)[]
144
+ ids: (string | number)[];
131
145
  }
132
146
 
133
- export type {DataTable}
147
+ export type { DataTable };