diff --git a/src/public/app/widgets/icon_list.js b/src/public/app/widgets/icon_list.js
index 1ad713b10..32fe21a95 100644
--- a/src/public/app/widgets/icon_list.js
+++ b/src/public/app/widgets/icon_list.js
@@ -11175,6 +11175,22 @@ const icons = [
}
];
+function getIconClass(icon) {
+ if (icon.type_of_icon === 'LOGO') {
+ return `bxl-${icon.name}`;
+ }
+ else if (icon.type_of_icon === 'SOLID') {
+ return `bxs-${icon.name}`;
+ }
+ else {
+ return `bx-${icon.name}`;
+ }
+}
+
+for (const icon of icons) {
+ icon.className = getIconClass(icon);
+}
+
export default {
categories,
icons
diff --git a/src/public/app/widgets/note_icon.js b/src/public/app/widgets/note_icon.js
index 20bdeed79..ba2bbb683 100644
--- a/src/public/app/widgets/note_icon.js
+++ b/src/public/app/widgets/note_icon.js
@@ -1,5 +1,6 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import attributeService from "../services/attributes.js";
+import server from "../services/server.js";
const TPL = `
@@ -147,6 +148,8 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
}
async renderDropdown(categoryId, search) {
+ const iconToCountPromise = this.getIconToCountMap();
+
this.$iconList.empty();
if (this.getIconLabels().length > 0) {
@@ -165,41 +168,50 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
search = search?.trim()?.toLowerCase();
- for (const icon of icons) {
+ const filteredIcons = icons.filter(icon => {
if (categoryId && icon.category_id !== categoryId) {
- continue;
+ return false;
}
if (search) {
if (!icon.name.includes(search) && !icon.term?.find(t => t.includes(search))) {
- continue;
+ return false;
}
}
- this.$iconList.append(
- $('')
- .addClass(this.getIconClass(icon))
- .attr("title", icon.name)
- );
+ return true;
+ });
+
+ const iconToCount = await iconToCountPromise;
+
+ filteredIcons.sort((a, b) => {
+ const countA = iconToCount[a.className] || 0;
+ const countB = iconToCount[b.className] || 0;
+
+ return countB - countA;
+ });
+
+ for (const icon of filteredIcons) {
+ this.$iconList.append(this.renderIcon(icon));
}
this.$iconSearch.focus();
}
+ async getIconToCountMap() {
+ const {iconClassToCountMap} = await server.get('other/icon-usage');
+
+ return iconClassToCountMap;
+ }
+
+ renderIcon(icon) {
+ return $('')
+ .addClass("bx " + icon.className)
+ .attr("title", icon.name);
+ }
+
getIconLabels() {
return this.note.getOwnedLabels()
.filter(label => ['workspaceIconClass', 'iconClass'].includes(label.name));
}
-
- getIconClass(icon) {
- if (icon.type_of_icon === 'LOGO') {
- return `bx bxl-${icon.name}`;
- }
- else if (icon.type_of_icon === 'SOLID') {
- return `bx bxs-${icon.name}`;
- }
- else {
- return `bx bx-${icon.name}`;
- }
- }
}
diff --git a/src/routes/api/other.js b/src/routes/api/other.js
new file mode 100644
index 000000000..c7e00235b
--- /dev/null
+++ b/src/routes/api/other.js
@@ -0,0 +1,29 @@
+const becca = require("../../becca/becca");
+
+function getIconUsage() {
+ const iconClassToCountMap = {};
+
+ for (const {value: iconClass, noteId} of becca.findAttributes('label', 'iconClass')) {
+ if (noteId.startsWith("_")) {
+ continue; // ignore icons of "system" notes since they were not set by the user
+ }
+
+ if (!iconClass?.trim()) {
+ continue;
+ }
+
+ for (const clazz of iconClass.trim().split(/\s+/)) {
+ if (clazz === 'bx') {
+ continue;
+ }
+
+ iconClassToCountMap[clazz] = (iconClassToCountMap[clazz] || 0) + 1;
+ }
+ }
+
+ return { iconClassToCountMap };
+}
+
+module.exports = {
+ getIconUsage
+};
diff --git a/src/routes/routes.js b/src/routes/routes.js
index cdca32975..9cc87bb37 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -56,6 +56,7 @@ const backendLogRoute = require('./api/backend_log');
const statsRoute = require('./api/stats');
const fontsRoute = require('./api/fonts');
const etapiTokensApiRoutes = require('./api/etapi_tokens');
+const otherRoute = require('./api/other');
const shareRoutes = require('../share/routes');
const etapiAuthRoutes = require('../etapi/auth');
const etapiAppInfoRoutes = require('../etapi/app_info');
@@ -298,6 +299,7 @@ function register(app) {
apiRoute(POST, '/api/delete-notes-preview', notesApiRoute.getDeleteNotesPreview);
route(GET, '/api/fonts', [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
+ apiRoute(GET, '/api/other/icon-usage', otherRoute.getIconUsage);
apiRoute(GET, '/api/etapi-tokens', etapiTokensApiRoutes.getTokens);
apiRoute(POST, '/api/etapi-tokens', etapiTokensApiRoutes.createToken);