Browse Source

Fix #3 Add and remove tag on picture

isundil 1 year ago
parent
commit
8615d49a76
3 changed files with 115 additions and 8 deletions
  1. 29 1
      router/api.js
  2. 58 6
      static/public/js/medias.js
  3. 28 1
      static/public/js/uiMediaFullpage.js

+ 29 - 1
router/api.js

@@ -5,6 +5,7 @@ const Path = require('path');
 const Security = require('../src/security.js');
 const MediaService = require('../model/mediaService.js');
 const MediaFileMetaModel = require('../model/mediaItemMeta.js').MediaFileMetaModel;
+const MediaFileTagModel = require('../model/mediaItemTag.js').MediaFileTagModel;
 const { AccessModel, ACCESS_TYPE, ACCESS_GRANT } = require('../model/access.js');
 
 module.exports = { register: app => {
@@ -27,6 +28,33 @@ module.exports = { register: app => {
         const result = Security.removeFromSession(req, req.params.id);
         app.routerUtils.jsonResponse(res, result);
     });
+    app.router.del("/api/media/:id/tag/:tag", async (req, res) => {
+        app.routerUtils.onApiRequest(req, res);
+        if (!req.params.id ||!req.params.tag)
+            return app.routerUtils.onBadRequest(res);
+        let data = await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList);
+        if (!data || data.accessType !== ACCESS_GRANT.write)
+            return app.routerUtils.onPageNotFound(res);
+        await app.databaseHelper.remove(MediaFileTagModel, { md5sum: data.md5sum, tag: decodeURIComponent(req.params.tag), fromMeta: 0 });
+        app.routerUtils.jsonResponse(res, await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList));
+    });
+    app.router.put("/api/media/:id/tag", async (req, res) => {
+        app.routerUtils.onApiRequest(req, res);
+        if (!req.params.id ||!req.body?.tag)
+            return app.routerUtils.onBadRequest(res);
+        let data = await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList);
+        if (!data || data.accessType !== ACCESS_GRANT.write)
+            return app.routerUtils.onPageNotFound(res);
+        const requestedTag = req.body.tag;
+        for (let existingTag of [...data.tags, ...data.fixedTags]) {
+            if (existingTag === requestedTag || existingTag.startsWith(`${requestedTag}/`)) {
+                return app.routerUtils.jsonResponse(res, data);
+            }
+        }
+        let tag = new MediaFileTagModel(data.md5sum, requestedTag, false);
+        await app.databaseHelper.insertOne(tag);
+        app.routerUtils.jsonResponse(res, await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList));
+    });
     app.router.patch("/api/media/:id/meta/:key", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);
         if (!req.params.id ||!req.params.key || !Number.isInteger(req.body?.value?.length))
@@ -40,7 +68,7 @@ module.exports = { register: app => {
             let newMediaItemMedia = new MediaFileMetaModel(data.md5sum, req.params.key, req.body.value, false);
             await app.databaseHelper.upsertOne(newMediaItemMedia);
         }
-        res.end();
+        app.routerUtils.jsonResponse(res, await MediaService.fetchOne(app, req.params.id, req.sessionObj?.accessList));
     });
     app.router.get("/api/media/list", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);

+ 58 - 6
static/public/js/medias.js

@@ -6,15 +6,15 @@ class Media {
         this.path = data.path;
         this.fileName = data.fileName;
         this.meta = data.meta || {};
-        this.fixedTags = data.fixedTags || [];
-        this.tags = data.tags || [];
+        this.fixedTags = [];
+
+        this.tags = [];
         this.writeAccess = data.accessType === 2;
         this.thumbnail = `/api/media/thumbnail/${data.md5sum}.jpg`;
         this.original = `/api/media/original/${data.md5sum}`;
         this.ui = null;
 
-        this.tags = this.tags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
-        this.fixedTags = this.fixedTags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
+        this.setTags(data.fixedTags || [], data.tags || []);
 
         for (let i in this.meta) {
             if (this.meta[i].type === 'date')
@@ -36,6 +36,11 @@ class Media {
         };
     }
 
+    setTags(fixedTags, tags) {
+        this.tags = tags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
+        this.fixedTags = fixedTags.reduce((acc, tag) => { acc.add(tag.replaceAll(/\/\/+/gi, '/')); return acc; }, new Set());
+    }
+
     allTags() {
         return Array.from(new Set([...this.fixedTags, ...this.tags])).sort();
     }
@@ -164,7 +169,7 @@ class MediaStorage extends EventTarget
     }
 
     setMetaValue(md5sum, key, value) {
-        LoadingTasks.push(() => {
+        return LoadingTasks.push(() => {
             return new Promise(ok => {
                 let media = this.medias.find(x => x.md5sum === md5sum);
                 if (!media || !media.writeAccess)
@@ -173,7 +178,7 @@ class MediaStorage extends EventTarget
                     url: `/api/media/${encodeURIComponent(md5sum)}/meta/${encodeURIComponent(key)}`,
                     type: "PATCH",
                     data: { value },
-                    success: () => {
+                    success: (media) => {
                         let meta = media.meta[key] || { type: 'string', value: value, canWrite: true };
                         meta.value = value;
                         this.#pushMeta(key, meta);
@@ -186,6 +191,53 @@ class MediaStorage extends EventTarget
             });
         });
     }
+
+    removeTag(md5sum, tagName) {
+        return LoadingTasks.push(() => {
+            return new Promise(ok => {
+                let media = this.medias.find(x => x.md5sum === md5sum);
+                if (!media || !media.writeAccess)
+                    return ok(false);
+                $.ajax({
+                    url: `/api/media/${encodeURIComponent(md5sum)}/tag/${encodeURIComponent(tagName)}`,
+                    type: "DELETE",
+                    success: data => {
+                        media.setTags(data.fixedTags, data.tags);
+                        for (let i of data.tags)
+                            this.#pushTag(i, true);
+                        for (let i of data.fixedTags)
+                            this.#pushTag(i, true);
+                        ok(true);
+                    },
+                    error: err => ok(false),
+                });
+            });
+        });
+    }
+
+    addTag(md5sum, tagName) {
+        return LoadingTasks.push(() => {
+            return new Promise(ok => {
+                let media = this.medias.find(x => x.md5sum === md5sum);
+                if (!media || !media.writeAccess)
+                    return ok(false);
+                $.ajax({
+                    url: `/api/media/${encodeURIComponent(md5sum)}/tag`,
+                    type: "PUT",
+                    data: { tag: tagName },
+                    success: data => {
+                        media.setTags(data.fixedTags, data.tags);
+                        for (let i of data.tags)
+                            this.#pushTag(i, true);
+                        for (let i of data.fixedTags)
+                            this.#pushTag(i, true);
+                        ok(true);
+                    },
+                    error: err => ok(false),
+                });
+            });
+        });
+    }
 }
 
 MediaStorage.Instance = new MediaStorage();

+ 28 - 1
static/public/js/uiMediaFullpage.js

@@ -103,6 +103,10 @@ $(() => {
         return metaList;
     }
 
+    function reloadCurrentMedia() {
+        window.displayMediaFullPage(fullPageMediaDisplayed);
+    }
+
     function displayTags(fixedTagList, tagList, writeAccess) {
         let tagListUi = document.createElement("ul");
         tagListUi.className = "taglist";
@@ -116,8 +120,10 @@ $(() => {
                 let btItem = document.createElement("a");
                 btItem.className = "border-start bi bi-x removeBt";
                 btItem.href = '#';
-                btItem.addEventListener('click', e => {
+                btItem.addEventListener('click', async e => {
                     e.preventDefault();
+                    await MediaStorage.Instance.removeTag(fullPageMediaDisplayed.md5sum, i);
+                    reloadCurrentMedia();
                 });
                 uiItem.appendChild(btItem);
             }
@@ -127,6 +133,27 @@ $(() => {
             createTagUiItem(i, true);
         for (let i of tagList)
             createTagUiItem(i, !writeAccess);
+        if (writeAccess) {
+            let inputGroup = document.createElement('form');
+            tagListUi.appendChild(inputGroup);
+            inputGroup.classList.add('input-group');
+            let valInput = document.createElement("input");
+            valInput.classList.add("form-control");
+            valInput.addEventListener('keyup', evt => evt.stopPropagation());
+            valInput.addEventListener('keydown', evt => evt.stopPropagation());
+            inputGroup.appendChild(valInput);
+            let bt = document.createElement('button');
+            bt.className = 'btn btn-outline-secondary';
+            bt.type = 'button';
+            bt.innerHTML = '<i class="bi bi-pen"></i>';
+            inputGroup.appendChild(bt);
+            bt.addEventListener('click', async () => { await MediaStorage.Instance.addTag(fullPageMediaDisplayed.md5sum, valInput.value); reloadCurrentMedia(); });
+            inputGroup.addEventListener('submit', async evt => {
+                evt.preventDefault();
+                await MediaStorage.Instance.addTag(fullPageMediaDisplayed.md5sum, valInput.value);
+                reloadCurrentMedia();
+            });
+        }
         return tagListUi;
     }