mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 07:20:12 +01:00
Feature: Grey Icons. This makes WeKan very slow. Not recommended.
Thanks to xet7 !
This commit is contained in:
parent
960e2126b4
commit
1b6e8797ec
9 changed files with 160 additions and 1 deletions
|
|
@ -62,3 +62,12 @@ Meteor.startup(() => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Subscribe to per-user small publications
|
||||
Meteor.startup(() => {
|
||||
Tracker.autorun(() => {
|
||||
if (Meteor.userId()) {
|
||||
Meteor.subscribe('userGreyIcons');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -394,6 +394,7 @@ Template.memberPopup.events({
|
|||
FlowRouter.go('home');
|
||||
});
|
||||
}),
|
||||
|
||||
});
|
||||
|
||||
Template.removeMemberPopup.helpers({
|
||||
|
|
|
|||
6
client/components/unicode-icons.css
Normal file
6
client/components/unicode-icons.css
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.unicode-icon {
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.8;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
}
|
||||
83
client/components/unicode-icons.js
Normal file
83
client/components/unicode-icons.js
Normal 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());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
7
server/publications/userGreyIcons.js
Normal file
7
server/publications/userGreyIcons.js
Normal 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 } });
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue