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

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu 2025-11-25 04:33:42 +02:00
parent 960e2126b4
commit 1b6e8797ec
9 changed files with 160 additions and 1 deletions

View file

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

View file

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

View file

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

View file

@ -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());
}
});
});

View file

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

View file

@ -90,6 +90,15 @@ Template.memberMenuPopup.events({
'click .js-notifications-drawer-toggle'() {
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) {
event.preventDefault();

View file

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

View file

@ -173,6 +173,13 @@ Users.attachSchema(
type: Boolean,
optional: true,
},
'profile.GreyIcons': {
/**
* per-user preference to render unicode icons in grey
*/
type: Boolean,
optional: true,
},
'profile.cardMaximized': {
/**
* has user clicked maximize card?
@ -708,6 +715,7 @@ Users.safeFields = {
'profile.initials': 1,
'profile.zoomLevel': 1,
'profile.mobileMode': 1,
'profile.GreyIcons': 1,
orgs: 1,
teams: 1,
authenticationMethod: 1,
@ -1061,6 +1069,11 @@ Users.helpers({
return profile.showDesktopDragHandles || false;
},
hasGreyIcons() {
const profile = this.profile || {};
return profile.GreyIcons || false;
},
hasCustomFieldsGrid() {
const profile = this.profile || {};
return profile.customFieldsGrid || false;
@ -1485,6 +1498,13 @@ Users.mutations({
},
};
},
toggleGreyIcons(value = false) {
return {
$set: {
'profile.GreyIcons': !value,
},
};
},
addNotification(activityId) {
return {
@ -1688,6 +1708,23 @@ Meteor.methods({
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() {
const user = ReactiveCache.getCurrentUser();
user.toggleDesktopHandles(user.hasShowDesktopDragHandles());

View file

@ -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 } });
});