浏览代码

Feature: Grey Icons. This makes WeKan very slow. Not recommended.

Thanks to xet7 !
Lauri Ojansivu 4 周之前
父节点
当前提交
1b6e8797ec

+ 9 - 0
client/00-startup.js

@@ -62,3 +62,12 @@ Meteor.startup(() => {
     }
     }
   });
   });
 });
 });
+
+// Subscribe to per-user small publications
+Meteor.startup(() => {
+  Tracker.autorun(() => {
+    if (Meteor.userId()) {
+      Meteor.subscribe('userGreyIcons');
+    }
+  });
+});

+ 1 - 0
client/components/sidebar/sidebar.js

@@ -394,6 +394,7 @@ Template.memberPopup.events({
       FlowRouter.go('home');
       FlowRouter.go('home');
     });
     });
   }),
   }),
+  
 });
 });
 
 
 Template.removeMemberPopup.helpers({
 Template.removeMemberPopup.helpers({

+ 6 - 0
client/components/unicode-icons.css

@@ -0,0 +1,6 @@
+.unicode-icon {
+  filter: grayscale(100%);
+  opacity: 0.8;
+  display: inline-block;
+  line-height: 1;
+}

+ 83 - 0
client/components/unicode-icons.js

@@ -0,0 +1,83 @@
+Meteor.startup(() => {
+  // Unicode pictographic ranges (emoji, symbols, etc.)
+  // Only greyscale these icons:
+  const greyscaleIcons = [
+    '🔼', '❌', '🏷️', '📅', '📥', '🚀', '👤', '👥', '✍️', '📋', '✏️', '🌐', '📎', '📝', '📋', '📜', '🏠', '🔒', '🔕', '🃏',
+    '⏰', '🛒', '🔢', '✅', '❌', '👁️', '👍', '📋', '🕐', '🎨',
+    '📤', '⬆️', '⬇️', '➡️', '📦',
+    '⬅️', '↕️', '🔽', '🔍', '▼', '🏊',
+    '🔔', '⚙️', '🖼️', '🔑', '🚪', '◀️', '⌨️', '👥', '🏷️', '✅', '🚫'
+  ];
+
+  function wrapUnicodeIcons(root) {
+    try {
+      // Exclude avatar initials from wrapping
+      const excludeSelector = '.header-user-bar-avatar, .avatar-initials';
+
+      const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
+      while (walker.nextNode()) {
+        const node = walker.currentNode;
+        if (!node || !node.nodeValue) continue;
+        const parent = node.parentNode;
+        if (!parent) continue;
+        if (parent.closest && (parent.closest('.unicode-icon') || parent.closest(excludeSelector))) continue;
+        if (parent.tagName === 'SCRIPT' || parent.tagName === 'STYLE') continue;
+        // Only wrap if the text node is a single greyscale icon (no other text)
+        const txt = node.nodeValue.trim();
+        if (greyscaleIcons.includes(txt)) {
+          const span = document.createElement('span');
+          span.className = 'unicode-icon';
+          span.textContent = txt;
+          parent.replaceChild(span, node);
+        }
+      }
+
+      // Also wrap direct unicode icon children (e.g., <a>🎨</a>), including Member Settings and card details, but not avatar initials
+      const elements = root.querySelectorAll('*:not(script):not(style):not(.header-user-bar-avatar):not(.avatar-initials)');
+      elements.forEach((el) => {
+        el.childNodes.forEach((child) => {
+          if (child.nodeType === Node.TEXT_NODE) {
+            const txt = child.nodeValue.trim();
+            if (greyscaleIcons.includes(txt)) {
+              const span = document.createElement('span');
+              span.className = 'unicode-icon';
+              span.textContent = txt;
+              el.replaceChild(span, child);
+            }
+          }
+        });
+      });
+    } catch (e) {
+      // ignore
+    }
+  }
+  function unwrap() {
+    document.querySelectorAll('span.unicode-icon').forEach((span) => {
+      const txt = document.createTextNode(span.textContent);
+      span.parentNode.replaceChild(txt, span);
+    });
+  }
+
+  function runWrapAfterDOM() {
+    Meteor.defer(() => {
+      setTimeout(() => wrapUnicodeIcons(document.body), 100);
+    });
+    // Also rerun after Blaze renders popups
+    const observer = new MutationObserver(() => {
+      const user = Meteor.user();
+      if (user && user.profile && user.profile.GreyIcons) {
+        wrapUnicodeIcons(document.body);
+      }
+    });
+    observer.observe(document.body, { childList: true, subtree: true });
+  }
+
+  Tracker.autorun(() => {
+    const user = Meteor.user();
+    if (user && user.profile && user.profile.GreyIcons) {
+      runWrapAfterDOM();
+    } else {
+      Meteor.defer(() => unwrap());
+    }
+  });
+});

+ 7 - 1
client/components/users/userHeader.jade

@@ -13,6 +13,13 @@ template(name="headerUserBar")
 template(name="memberMenuPopup")
 template(name="memberMenuPopup")
   ul.pop-over-list
   ul.pop-over-list
     with currentUser
     with currentUser
+      li
+        a.js-toggle-grey-icons(href="#")
+          | 🎨
+          | {{_ 'grey-icons'}}
+          if currentUser.profile
+            if currentUser.profile.GreyIcons
+              span(key="grey-icons-checkmark") ✅
       li
       li
         a.js-my-cards(href="{{pathFor 'my-cards'}}")
         a.js-my-cards(href="{{pathFor 'my-cards'}}")
           | 📋
           | 📋
@@ -27,7 +34,6 @@ template(name="memberMenuPopup")
           | {{_ 'globalSearch-title'}}
           | {{_ 'globalSearch-title'}}
       li
       li
         a(href="{{pathFor 'home'}}")
         a(href="{{pathFor 'home'}}")
-          | 🏠
           | 🏠
           | 🏠
           | {{_ 'all-boards'}}
           | {{_ 'all-boards'}}
       li
       li

+ 9 - 0
client/components/users/userHeader.js

@@ -90,6 +90,15 @@ Template.memberMenuPopup.events({
   'click .js-notifications-drawer-toggle'() {
   'click .js-notifications-drawer-toggle'() {
     Session.set('showNotificationsDrawer', !Session.get('showNotificationsDrawer'));
     Session.set('showNotificationsDrawer', !Session.get('showNotificationsDrawer'));
   },
   },
+  'click .js-toggle-grey-icons'(event) {
+    event.preventDefault();
+    const currentUser = ReactiveCache.getCurrentUser();
+    if (!currentUser || !Meteor.userId()) return;
+    const current = (currentUser.profile && currentUser.profile.GreyIcons) || false;
+    Meteor.call('toggleGreyIcons', (err) => {
+      if (err && process.env.DEBUG === 'true') console.error('toggleGreyIcons error', err);
+    });
+  },
   'click .js-logout'(event) {
   'click .js-logout'(event) {
     event.preventDefault();
     event.preventDefault();
 
 

+ 1 - 0
imports/i18n/data/en.i18n.json

@@ -547,6 +547,7 @@
   "log-in": "Log In",
   "log-in": "Log In",
   "loginPopup-title": "Log In",
   "loginPopup-title": "Log In",
   "memberMenuPopup-title": "Member Settings",
   "memberMenuPopup-title": "Member Settings",
+  "grey-icons": "Grey Icons",
   "members": "Members",
   "members": "Members",
   "menu": "Menu",
   "menu": "Menu",
   "move-selection": "Move selection",
   "move-selection": "Move selection",

+ 37 - 0
models/users.js

@@ -173,6 +173,13 @@ Users.attachSchema(
       type: Boolean,
       type: Boolean,
       optional: true,
       optional: true,
     },
     },
+    'profile.GreyIcons': {
+      /**
+       * per-user preference to render unicode icons in grey
+       */
+      type: Boolean,
+      optional: true,
+    },
     'profile.cardMaximized': {
     'profile.cardMaximized': {
       /**
       /**
        * has user clicked maximize card?
        * has user clicked maximize card?
@@ -708,6 +715,7 @@ Users.safeFields = {
   'profile.initials': 1,
   'profile.initials': 1,
   'profile.zoomLevel': 1,
   'profile.zoomLevel': 1,
   'profile.mobileMode': 1,
   'profile.mobileMode': 1,
+  'profile.GreyIcons': 1,
   orgs: 1,
   orgs: 1,
   teams: 1,
   teams: 1,
   authenticationMethod: 1,
   authenticationMethod: 1,
@@ -1061,6 +1069,11 @@ Users.helpers({
     return profile.showDesktopDragHandles || false;
     return profile.showDesktopDragHandles || false;
   },
   },
 
 
+  hasGreyIcons() {
+    const profile = this.profile || {};
+    return profile.GreyIcons || false;
+  },
+
   hasCustomFieldsGrid() {
   hasCustomFieldsGrid() {
     const profile = this.profile || {};
     const profile = this.profile || {};
     return profile.customFieldsGrid || false;
     return profile.customFieldsGrid || false;
@@ -1485,6 +1498,13 @@ Users.mutations({
       },
       },
     };
     };
   },
   },
+  toggleGreyIcons(value = false) {
+    return {
+      $set: {
+        'profile.GreyIcons': !value,
+      },
+    };
+  },
 
 
   addNotification(activityId) {
   addNotification(activityId) {
     return {
     return {
@@ -1688,6 +1708,23 @@ Meteor.methods({
     
     
     Users.update(this.userId, updateObject);
     Users.update(this.userId, updateObject);
   },
   },
+  toggleGreyIcons(value) {
+    if (!this.userId) {
+      throw new Meteor.Error('not-logged-in', 'User must be logged in');
+    }
+    if (value !== undefined) check(value, Boolean);
+
+    const user = Users.findOne(this.userId);
+    if (!user) {
+      throw new Meteor.Error('user-not-found', 'User not found');
+    }
+
+    const current = (user.profile && user.profile.GreyIcons) || false;
+    const newValue = value !== undefined ? value : !current;
+
+    Users.update(this.userId, { $set: { 'profile.GreyIcons': newValue } });
+    return newValue;
+  },
   toggleDesktopDragHandles() {
   toggleDesktopDragHandles() {
     const user = ReactiveCache.getCurrentUser();
     const user = ReactiveCache.getCurrentUser();
     user.toggleDesktopHandles(user.hasShowDesktopDragHandles());
     user.toggleDesktopHandles(user.hasShowDesktopDragHandles());

+ 7 - 0
server/publications/userGreyIcons.js

@@ -0,0 +1,7 @@
+// Publish only the current logged-in user's GreyIcons profile flag
+import { Meteor } from 'meteor/meteor';
+
+Meteor.publish('userGreyIcons', function publishUserGreyIcons() {
+  if (!this.userId) return this.ready();
+  return Meteor.users.find({ _id: this.userId }, { fields: { 'profile.GreyIcons': 1 } });
+});