浏览代码

feat: allow undo after remove

Gerald 8 年之前
父节点
当前提交
214df76a8e

+ 1 - 0
icons/undo.svg

@@ -0,0 +1 @@
+<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M596.677 248.123c204.8 0 372.185 165.415 372.185 372.185S801.477 992.492 596.677 992.492H435.2c-15.754 0-25.6-11.815-25.6-27.569v-63.015c0-15.754 11.815-29.539 27.57-29.539h159.507c139.815 0 252.061-112.246 252.061-252.061S736.492 368.246 596.677 368.246H322.954s-15.754 0-21.662 1.97c-15.754 7.876-11.815 19.692 1.97 33.476l96.492 96.493c11.815 11.815 9.846 29.538-1.97 41.353l-43.322 43.324c-11.816 11.815-25.6 11.815-37.416 1.969l-256-256a24.96 24.96 0 0 1 0-35.446l254.03-254.031c11.816-11.816 31.509-11.816 41.355 0l41.354 41.354c11.815 11.815 11.815 31.507 0 41.354l-96.493 96.492c-11.815 11.815-11.815 25.6 7.877 25.6h13.785l273.723 1.97z"/></svg>

+ 6 - 0
src/_locales/cs/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/de/messages.yml

@@ -414,3 +414,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/en/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: Support page
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: Undo
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: '[Removed]'

+ 6 - 0
src/_locales/es/messages.yml

@@ -416,3 +416,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/hr/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/id/messages.yml

@@ -414,3 +414,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/ja/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/pl/messages.yml

@@ -417,3 +417,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/ro/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/ru/messages.yml

@@ -419,3 +419,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/sr/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/vi/messages.yml

@@ -412,3 +412,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: ''
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: ''
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: ''

+ 6 - 0
src/_locales/zh/messages.yml

@@ -410,3 +410,9 @@ buttonHome:
 buttonSupport:
   description: Button to open support page.
   message: 支持页面
+buttonUndo:
+  description: Button to undo removement of a script.
+  message: 撤销
+labelRemoved:
+  description: Label shown when a script is removed.
+  message: 【已删除】

+ 25 - 7
src/background/utils/db.js

@@ -195,6 +195,9 @@ const storage = {
       if (!id) return Promise.resolve();
       return browser.storage.local.remove(this.getKey(id));
     },
+    removeMulti(ids) {
+      return browser.storage.local.remove(ids.map(id => this.getKey(id)));
+    },
   },
 };
 storage.script = Object.assign({}, storage.base, {
@@ -344,7 +347,9 @@ export function setValues(where, values) {
  * @desc Get scripts to be injected to page with specific URL.
  */
 export function getScriptsByURL(url) {
-  const scripts = testBlacklist(url) ? [] : store.scripts.filter(script => testScript(url, script));
+  const scripts = testBlacklist(url)
+    ? []
+    : store.scripts.filter(script => !script.config.removed && testScript(url, script));
   const reqKeys = {};
   const cacheKeys = {};
   scripts.forEach(script => {
@@ -379,8 +384,14 @@ export function getScriptsByURL(url) {
  * @desc Get data for dashboard.
  */
 export function getData() {
-  const scripts = store.scripts;
   const cacheKeys = {};
+  const toRemove = store.scripts.filter(script => script.config.removed);
+  if (toRemove.length) {
+    store.scripts = store.scripts.filter(script => !script.config.removed);
+    storage.script.removeMulti(toRemove);
+    storage.code.removeMulti(toRemove);
+  }
+  const { scripts } = store;
   scripts.forEach(script => {
     const icon = object.get(script, 'meta.icon');
     if (isRemote(icon)) cacheKeys[icon] = 1;
@@ -400,6 +411,7 @@ export function removeScript(id) {
   if (i >= 0) {
     store.scripts.splice(i, 1);
     storage.script.remove(id);
+    storage.code.remove(id);
   }
   return browser.runtime.sendMessage({
     cmd: 'RemoveScript',
@@ -475,9 +487,13 @@ export function updateScriptInfo(id, data) {
 }
 
 export function getExportData(ids, withValues) {
+  const availableIds = ids.filter(id => {
+    const script = store.scriptMap[id];
+    return script && !script.config.removed;
+  });
   return Promise.all([
-    Promise.all(ids.map(id => getScript({ id }))),
-    storage.code.getMulti(ids),
+    Promise.all(availableIds.map(id => getScript({ id }))),
+    storage.code.getMulti(availableIds),
   ])
   .then(([scripts, codeMap]) => {
     const data = {};
@@ -500,7 +516,9 @@ export function parseScript(data) {
   const result = {
     cmd: 'UpdateScript',
     data: {
-      message: message == null ? i18n('msgUpdated') : message || '',
+      update: {
+        message: message == null ? i18n('msgUpdated') : message || '',
+      },
     },
   };
   return getScript({ id, meta })
@@ -512,7 +530,7 @@ export function parseScript(data) {
     } else {
       ({ script } = newScript());
       result.cmd = 'AddScript';
-      result.data.message = i18n('msgInstalled');
+      result.data.update.message = i18n('msgInstalled');
     }
     script.config = Object.assign({}, script.config, config);
     script.custom = Object.assign({}, script.custom, custom);
@@ -526,7 +544,7 @@ export function parseScript(data) {
   })
   .then(script => {
     fetchScriptResources(script, data);
-    Object.assign(result.data, script);
+    Object.assign(result.data.update, script);
     return result;
   });
 }

+ 18 - 13
src/background/utils/update.js

@@ -7,11 +7,16 @@ import { notify } from '.';
 const processes = {};
 
 function doCheckUpdate(script) {
+  const update = {
+    checking: true,
+  };
   const res = {
     cmd: 'UpdateScript',
     data: {
-      id: script.props.id,
-      checking: true,
+      where: {
+        id: script.props.id,
+      },
+      update,
     },
   };
   const downloadURL = (
@@ -23,35 +28,35 @@ function doCheckUpdate(script) {
   const okHandler = ({ data }) => {
     const meta = parseMeta(data);
     if (compareVersion(script.meta.version, meta.version) < 0) return Promise.resolve();
-    res.data.checking = false;
-    res.data.message = i18n('msgNoUpdate');
+    update.checking = false;
+    update.message = i18n('msgNoUpdate');
     browser.runtime.sendMessage(res);
     return Promise.reject();
   };
   const errHandler = () => {
-    res.data.checking = false;
-    res.data.message = i18n('msgErrorFetchingUpdateInfo');
+    update.checking = false;
+    update.message = i18n('msgErrorFetchingUpdateInfo');
     browser.runtime.sendMessage(res);
     return Promise.reject();
   };
-  const update = () => {
+  const doUpdate = () => {
     if (!downloadURL) {
-      res.data.message = `<span class="new">${i18n('msgNewVersion')}</span>`;
+      update.message = `<span class="new">${i18n('msgNewVersion')}</span>`;
       browser.runtime.sendMessage(res);
       return Promise.reject();
     }
-    res.data.message = i18n('msgUpdating');
+    update.message = i18n('msgUpdating');
     browser.runtime.sendMessage(res);
     return request(downloadURL)
     .then(({ data }) => data, () => {
-      res.data.checking = false;
-      res.data.message = i18n('msgErrorFetchingScript');
+      update.checking = false;
+      update.message = i18n('msgErrorFetchingScript');
       browser.runtime.sendMessage(res);
       return Promise.reject();
     });
   };
   if (!updateURL) return Promise.reject();
-  res.data.message = i18n('msgCheckingForUpdate');
+  update.message = i18n('msgCheckingForUpdate');
   browser.runtime.sendMessage(res);
   return request(updateURL, {
     headers: {
@@ -59,7 +64,7 @@ function doCheckUpdate(script) {
     },
   })
   .then(okHandler, errHandler)
-  .then(update);
+  .then(doUpdate);
 }
 
 export default function checkUpdate(script) {

+ 23 - 4
src/common/ui/tooltip.vue

@@ -100,20 +100,39 @@ $gap: 10px;
         top: 0;
       }
     }
+    &.tooltip-left,
     &.tooltip-right {
       top: 50%;
-      left: 100%;
-      margin-left: 10px;
       > * {
         transform: translateY(-50%);
       }
       > i {
-        right: 100%;
         border-top: $border-side;
-        border-right: $border-base;
         border-bottom: $border-side;
       }
     }
+    &.tooltip-left {
+      margin-right: 10px;
+      right: 100%;
+      > div {
+        right: 100%;
+      }
+      > i {
+        left: 100%;
+        border-left: $border-base;
+      }
+    }
+    &.tooltip-right {
+      margin-left: 10px;
+      left: 100%;
+      > div {
+        left: 100%;
+      }
+      > i {
+        right: 100%;
+        border-right: $border-base;
+      }
+    }
   }
 }
 </style>

+ 4 - 7
src/options/app.js

@@ -82,15 +82,12 @@ function initMain() {
     },
     UpdateScript(data) {
       if (!data) return;
-      const index = store.scripts.findIndex(item => item.props.id === data.props.id);
+      const index = store.scripts.findIndex(item => item.props.id === data.where.id);
       if (index >= 0) {
-        Vue.set(store.scripts, index, data);
-        initSearch(data);
+        const updated = Object.assign({}, store.scripts[index], data.update);
+        Vue.set(store.scripts, index, updated);
+        initSearch(updated);
       }
     },
-    // RemoveScript(data) {
-    //   const i = store.scripts.findIndex(script => script.props.id === data);
-    //   if (i >= 0) store.scripts.splice(i, 1);
-    // },
   });
 }

+ 30 - 7
src/options/views/script-item.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="script" :class="{ disabled: !script.config.enabled || script.config.removed }" draggable="true" @dragstart.prevent="onDragStart">
+  <div class="script" :class="{ disabled: !script.config.enabled, removed: script.config.removed }" :draggable="!script.config.removed" @dragstart.prevent="onDragStart">
     <img class="script-icon" :src="safeIcon">
     <div class="script-info flex">
       <div class="script-name ellipsis" v-text="script.custom.name || getLocaleString('name')"></div>
@@ -10,6 +10,14 @@
         <span v-if="!author.email" v-text="author.name"></span>
       </div>
       <div class="script-version" v-text="script.meta.version ? `v${script.meta.version}` : ''"></div>
+      <div v-if="script.config.removed" v-text="i18n('labelRemoved')"></div>
+      <div v-if="script.config.removed">
+        <tooltip :title="i18n('buttonUndo')" placement="left">
+          <span class="btn-ghost" @click="onRemove(0)">
+            <icon name="undo"></icon>
+          </span>
+        </tooltip>
+      </div>
     </div>
     <p class="script-desc ellipsis" v-text="script.custom.description || getLocaleString('description')"></p>
     <div class="script-buttons flex">
@@ -41,7 +49,7 @@
       </tooltip>
       <div class="flex-auto" v-text="script.message"></div>
       <tooltip :title="i18n('buttonRemove')" align="end">
-        <span class="btn-ghost" @click="onRemove">
+        <span class="btn-ghost" @click="onRemove(1)">
           <icon name="trash"></icon>
         </span>
       </tooltip>
@@ -135,13 +143,13 @@ export default {
     onEdit() {
       this.$emit('edit', this.script.props.id);
     },
-    onRemove() {
+    onRemove(remove) {
       sendMessage({
         cmd: 'UpdateScriptInfo',
         data: {
           id: this.script.props.id,
           config: {
-            removed: 1,
+            removed: remove ? 1 : 0,
           },
         },
       });
@@ -301,10 +309,14 @@ export default {
   &:hover {
     border-color: darkgray;
   }
-  &.disabled {
+  &.disabled,
+  &.removed {
     background: #f0f0f0;
     color: #999;
   }
+  &.removed {
+    padding-bottom: 10px;
+  }
   &-buttons {
     align-items: center;
     line-height: 1;
@@ -312,6 +324,9 @@ export default {
     > .flex-auto {
       margin-left: 1rem;
     }
+    .removed & {
+      display: none;
+    }
   }
   &-info {
     margin-left: 3.5rem;
@@ -326,12 +341,17 @@ export default {
   }
   &-icon {
     position: absolute;
-    top: 1rem;
     width: 3rem;
     height: 3rem;
-    .disabled & {
+    top: 1rem;
+    .disabled &,
+    .removed & {
       filter: grayscale(.8);
     }
+    .removed & {
+      width: 2rem;
+      height: 2rem;
+    }
   }
   &-name {
     font-weight: bold;
@@ -350,6 +370,9 @@ export default {
     &::after {
       content: "\200b";
     }
+    .removed & {
+      display: none;
+    }
   }
 }
 .dragging {