Skip to content

Commit d7ecf40

Browse files
authored
Merge pull request #44 from Omniaevo/develop
Better notifications rule builder
2 parents d46c03a + 4f03553 commit d7ecf40

File tree

3 files changed

+177
-55
lines changed

3 files changed

+177
-55
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mqtt5-explorer",
3-
"version": "1.13.0",
3+
"version": "1.13.1",
44
"private": false,
55
"license": "GPLv3",
66
"description": "A simple MQTT client that supports MQTT5 protocol.",

src/background.js

+8
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,14 @@ async function createWindow() {
259259
ipcMain.on("enterHomePage", () => {
260260
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate(pages.HOME)));
261261
});
262+
ipcMain.on("focusWindow", () => {
263+
if (!win) return;
264+
if (!win.isVisible()) win.show();
265+
if (win.isMinimized()) win.restore();
266+
267+
// Ensure window is visible then focus
268+
setTimeout(() => win.focus(), 200);
269+
});
262270

263271
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate()));
264272

src/views/MqttViewer.vue

+168-54
Original file line numberDiff line numberDiff line change
@@ -476,49 +476,60 @@
476476
<v-text-field
477477
v-model="notifyEntry"
478478
v-bind:outlined="outline"
479-
v-on:click:append-outer="addNotifyEntry"
480479
v-on:keyup.enter="addNotifyEntry"
481-
append-outer-icon="mdi-bell-plus-outline"
482480
clear-icon="mdi-close"
483-
label="Notify topic"
481+
label="Add condition"
484482
clearable
485-
/>
486-
<v-btn-toggle v-model="notifyFilterType" class="ms-4">
487-
<v-btn
488-
v-bind:value="searchModes.CASES"
489-
v-on:click.stop
490-
title="Uppercase/Lowercase"
491-
small
492-
icon
493-
>
494-
<v-icon small>mdi-format-letter-case</v-icon>
495-
</v-btn>
496-
<v-btn
497-
v-bind:value="searchModes.WORDS"
498-
v-on:click.stop
499-
title="Match whole word"
500-
small
501-
icon
502-
>
503-
<v-icon small>mdi-format-letter-matches</v-icon>
504-
</v-btn>
505-
<v-btn
506-
v-bind:value="searchModes.REG_EXP"
507-
v-on:click.stop
508-
title="Regular expression"
509-
small
510-
icon
511-
>
512-
<v-icon small>mdi-regex</v-icon>
513-
</v-btn>
514-
</v-btn-toggle>
483+
>
484+
<template slot="append-outer">
485+
<div class="d-flex align-center">
486+
<v-btn-toggle v-model="notifyFilterType">
487+
<v-btn
488+
v-bind:value="searchModes.CASES"
489+
v-on:click.stop
490+
title="Uppercase/Lowercase"
491+
small
492+
icon
493+
>
494+
<v-icon small>mdi-format-letter-case</v-icon>
495+
</v-btn>
496+
<v-btn
497+
v-bind:value="searchModes.WORDS"
498+
v-on:click.stop
499+
title="Match whole word"
500+
small
501+
icon
502+
>
503+
<v-icon small>mdi-format-letter-matches</v-icon>
504+
</v-btn>
505+
<v-btn
506+
v-bind:value="searchModes.REG_EXP"
507+
v-on:click.stop
508+
title="Regular expression"
509+
small
510+
icon
511+
>
512+
<v-icon small>mdi-regex</v-icon>
513+
</v-btn>
514+
</v-btn-toggle>
515+
<v-btn
516+
v-bind:disabled="!notifyEntry"
517+
v-on:click="addNotifyEntry"
518+
class="ms-1"
519+
icon
520+
>
521+
<v-icon>mdi-plus</v-icon>
522+
</v-btn>
523+
</div>
524+
</template>
525+
</v-text-field>
515526
<v-tooltip bottom>
516527
<template v-slot:activator="{ on, attrs }">
517528
<v-btn
518529
v-bind="attrs"
519530
v-on="on"
520531
v-on:click="searchInfoDialog = true"
521-
class="ms-2"
532+
class="ms-4"
522533
icon
523534
>
524535
<v-icon>mdi-information-outline</v-icon>
@@ -528,32 +539,90 @@
528539
</v-tooltip>
529540
</div>
530541
<div class="mt-2">
531-
<div>Notifications active for:</div>
532-
<v-list>
533-
<div v-for="(entry, i) in notifyEntries" v-bind:key="entry">
534-
<v-list-item>
542+
<div class="d-flex justify-space-between align-center">
543+
<span>Notification conditions:</span>
544+
<v-btn-toggle v-model="notifyJoinType">
545+
<v-btn
546+
v-bind:value="joinModes.OR"
547+
v-on:click.stop
548+
title="Join conditions with OR operator"
549+
small
550+
icon
551+
>
552+
<span class="px-2">{{ joinModes.OR }}</span>
553+
</v-btn>
554+
<v-btn
555+
v-bind:value="joinModes.AND"
556+
v-on:click.stop
557+
title="Join conditions with AND operator"
558+
small
559+
icon
560+
>
561+
<span class="px-2">{{ joinModes.AND }}</span>
562+
</v-btn>
563+
</v-btn-toggle>
564+
</div>
565+
<v-list dense>
566+
<div
567+
v-for="(entry, i) in notifyEntries"
568+
v-bind:key="entry.notifyEntry"
569+
>
570+
<v-list-item dense>
535571
<v-list-item-avatar>
536-
<v-icon>mdi-bell-outline</v-icon>
572+
<v-icon color="primary" small>mdi-pin-outline</v-icon>
537573
</v-list-item-avatar>
538574
<v-list-item-content>
539-
<v-list-item-title>{{ entry }}</v-list-item-title>
575+
<v-list-item-title
576+
style="font-size: 1.1em"
577+
class="d-flex align-center justify-space-between"
578+
>
579+
<span>{{ entry.notifyEntry }}</span>
580+
<v-tooltip bottom>
581+
<template v-slot:activator="{ attr, on }">
582+
<v-chip
583+
v-bind="attr"
584+
v-on="on"
585+
class="me-4"
586+
color="primary"
587+
outlined
588+
small
589+
>
590+
<v-icon small>
591+
{{ getFilterTypeIcon(entry.filterType) }}
592+
</v-icon>
593+
</v-chip>
594+
</template>
595+
<span>Selected match mode</span>
596+
</v-tooltip>
597+
</v-list-item-title>
540598
</v-list-item-content>
541599
<v-list-item-action>
542600
<v-btn
543601
v-on:click="deleteNotifyEntry(entry)"
544602
color="error"
545603
icon
604+
small
546605
>
547-
<v-icon>mdi-delete-outline</v-icon>
606+
<v-icon small>mdi-delete-outline</v-icon>
548607
</v-btn>
549608
</v-list-item-action>
550609
</v-list-item>
551-
<v-divider v-if="i < notifyEntries.length - 1" />
610+
<div
611+
v-if="i < notifyEntries.length - 1"
612+
class="d-flex align-center"
613+
>
614+
<v-divider />
615+
<span class="mx-4 caption"> {{ notifyJoinType }} </span>
616+
<v-divider />
617+
</div>
552618
</div>
553619
</v-list>
554620
</div>
555621
</v-card-text>
556622
<v-card-actions>
623+
<v-btn v-on:click="notifyEntries = []" color="error" text>
624+
Reset
625+
</v-btn>
557626
<v-spacer />
558627
<v-btn v-on:click="notifySettings = false" text> Done </v-btn>
559628
</v-card-actions>
@@ -695,6 +764,7 @@ div[center-vertical] {
695764
</style>
696765

697766
<script>
767+
import Vue from "vue";
698768
import Connection from "../utils/Connection";
699769
import ConnectionProperties from "../models/ConnectionProperties";
700770
import SearchEngine from "../utils/SearchEngine";
@@ -727,6 +797,11 @@ export default {
727797
notifyFilterType: undefined,
728798
notifyEntry: undefined,
729799
notifyEntries: [],
800+
notifyJoinType: "or",
801+
joinModes: {
802+
OR: "or",
803+
AND: "and",
804+
},
730805
}),
731806
732807
computed: {
@@ -898,26 +973,65 @@ export default {
898973
addNotifyEntry() {
899974
if (!this.notifyEntry) return;
900975
901-
this.notifyEntries.push(this.notifyEntry);
976+
const entry = {
977+
notifyEntry: this.notifyEntry,
978+
filterType: this.notifyFilterType || SearchEngine.modes.ALL,
979+
};
980+
const entryIndex = this.notifyEntries.findIndex(
981+
(e) => e.notifyEntry === entry.notifyEntry
982+
);
983+
984+
if (entryIndex < 0) this.notifyEntries.push(entry);
985+
else Vue.set(this.notifyEntries, entryIndex, entry);
902986
903-
this.notifyEntries = [...new Set(this.notifyEntries)];
904987
this.notifyEntry = undefined;
988+
this.notifyFilterType = undefined;
989+
},
990+
getFilterTypeIcon(filterType) {
991+
switch (filterType) {
992+
case this.searchModes.CASES:
993+
return "mdi-format-letter-case";
994+
case this.searchModes.WORDS:
995+
return "mdi-format-letter-matches";
996+
case this.searchModes.REG_EXP:
997+
return "mdi-regex";
998+
default:
999+
return "mdi-equal";
1000+
}
9051001
},
9061002
deleteNotifyEntry(entry) {
9071003
this.notifyEntries = this.notifyEntries.filter((item) => item !== entry);
9081004
},
9091005
notify(node) {
910-
this.notifyEntries
911-
.flatMap((entry) => {
912-
return node.deepSearch(
913-
entry,
914-
this.notifyFilterType || SearchEngine.modes.ALL
915-
);
916-
})
917-
.forEach((foundNode) => {
918-
if (!foundNode?.value) return;
919-
this.sendNotification(foundNode.value.topic, foundNode.value.payload);
1006+
let foundNodes = this.notifyEntries.flatMap((entry) => {
1007+
return node.deepSearch(entry.notifyEntry, entry.filterType);
1008+
});
1009+
1010+
if (this.notifyJoinType === this.joinModes.AND) {
1011+
foundNodes = foundNodes.filter((foundNode) => {
1012+
return foundNode
1013+
? this.notifyEntries.reduce((previous, entry) => {
1014+
return (
1015+
previous &&
1016+
foundNode.search(entry.notifyEntry, entry.filterType)
1017+
);
1018+
}, true)
1019+
: false;
9201020
});
1021+
}
1022+
1023+
foundNodes.forEach((foundNode) => {
1024+
if (!foundNode?.value) return;
1025+
this.sendNotification(
1026+
foundNode.value.topic,
1027+
foundNode.value.payload,
1028+
() => {
1029+
// Open the app if closed/minimized and select the topic
1030+
this.getProperties(foundNode);
1031+
ipcRenderer.send("focusWindow");
1032+
}
1033+
);
1034+
});
9211035
},
9221036
publishItem() {
9231037
this.itemEditing.value.topic = this.itemEditing.topic;

0 commit comments

Comments
 (0)