[Pkg-mozext-maintainers] Bug#1082115: bookworm-pu: package mailmindr/1.7.1-1~deb12u1
Mechtilde Stehmann
mechtilde at debian.org
Wed Sep 18 15:13:12 BST 2024
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: mailmindr at packages.debian.org, mechtilde at debian.org
Control: affects -1 + src:mailmindr
User: release.debian.org at packages.debian.org
Usertags: pu
[ Reason ]
Thunderbird will come with a new version (>=128.2.x) into stable. This need an
update for the Add-Ons (here: mailmindr) too
[ Impact ]
If the update isn't approved the user can't anymore use it
in recent thunderbird
[ Tests ]
The same upstream code works with thunderbird >= 128.2 in testing.
[ Risks ]
code is trivial so no risk
[ Checklist ]
[X] *all* changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in (old)stable
[X] the issue is verified as fixed in unstable
[ Changes ]
The new version of thunderbird needs a new version of webext-mailmindr.
[ Other info ]
The only reason is the new upcomming version of the thunderbird.
-------------- next part --------------
diffstat for mailmindr-1.4.0 mailmindr-1.7.1
_locales/de/messages.json | 36
_locales/en/messages.json | 80 +
api/messages.js | 147 --
debian/changelog | 31
debian/control | 5
debian/copyright | 4
images/mailmindr-flag--rainbow-shadow.svg | 201 ---
images/mailmindr-flag_marker--white.svg | 87 +
images/mailmindr-flag_marker.svg | 87 +
manifest.json | 48
modules/core-utils.mjs.js | 206 ++-
modules/defaults.mjs.js | 37
modules/logger.mjs.js | 47
modules/message-actions.mjs.js | 187 ++
modules/message-utils.mjs.js | 135 +-
modules/storage.mjs.js | 2
modules/store/actions/actionTypes.mjs.js | 16
modules/store/actions/actions.mjs.js | 91 +
modules/store/actions/executeMindr.mjs.js | 215 +++
modules/store/actions/heartBeat.mjs.js | 49
modules/store/actions/index.mjs.js | 7
modules/store/actions/setReplyReceived.mjs.js | 24
modules/store/actions/showMindrAlert.mjs.js | 105 +
modules/store/actions/snoozeMindrs.mjs.js | 29
modules/store/reducers/createOrUpdateDraft.mjs.js | 33
modules/store/reducers/createOrUpdateMindr.mjs.js | 26
modules/store/reducers/index.mjs.js | 216 +++
modules/store/reducers/removeMindr.mjs.js | 52
modules/store/selectors/index.mjs.js | 68 +
modules/store/state-manager.mjs.js | 142 ++
modules/string-utils.mjs.js | 2
modules/ui-utils.mjs.js | 136 +-
schema.json | 20
scripts/mailmindr-background.js | 1465 +++++++---------------
scripts/mailmindr-message-script.js | 23
views/dialogs/create-mindr/index.css | 27
views/dialogs/create-mindr/index.js | 114 -
views/dialogs/mindr-alert/index.js | 22
views/options/index.html | 19
views/options/index.js | 43
views/popups/create-outgoing-mindr/index.css | 173 ++
views/popups/create-outgoing-mindr/index.html | 73 +
views/popups/create-outgoing-mindr/index.js | 417 ++++++
views/popups/list-all/index.css | 59
views/popups/list-all/index.html | 19
views/popups/list-all/index.js | 90 +
46 files changed, 3563 insertions(+), 1552 deletions(-)
diff -Nru mailmindr-1.4.0/api/messages.js mailmindr-1.7.1/api/messages.js
--- mailmindr-1.4.0/api/messages.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/api/messages.js 1970-01-01 01:00:00.000000000 +0100
@@ -1,147 +0,0 @@
-var { ExtensionCommon } = ChromeUtils.import(
- 'resource://gre/modules/ExtensionCommon.jsm'
-);
-
-var { ExtensionParent } = ChromeUtils.import(
- 'resource://gre/modules/ExtensionParent.jsm'
-);
-
-var extension = ExtensionParent.GlobalManager.getExtension(
- 'mailmindr at arndissler.net'
-);
-
-const { XPCOMUtils } = ChromeUtils.import(
- 'resource://gre/modules/XPCOMUtils.jsm'
-);
-
-const { MailUtils } = ChromeUtils.import('resource:///modules/MailUtils.jsm');
-
-XPCOMUtils.defineLazyModuleGetters(this, {
- MailServices: 'resource:///modules/MailServices.jsm',
- Services: 'resource://gre/modules/Services.jsm'
- //
-});
-
-var mailmindrMessagesApi = class extends ExtensionCommon.ExtensionAPI {
- getAPI(context) {
- return {
- //
- mailmindrMessagesApi: {
- openMessageByMessageHeaderId: async function(headerMessageId) {
- const msgId = headerMessageId
- .replace('>', '')
- .replace('<', '')
- .trim();
-
- try {
- if (MailUtils.openMessageByMessageId) {
- MailUtils.openMessageByMessageId(msgId);
- } else {
- const getFirstMessageHeaderForMessageId = (
- msgId,
- startServer
- ) => {
- console.warn(
- `MailUtils.openMessageByMessageId doesn't exist, using fallback to manual traveral`
- );
-
- const findMsgIdInFolder = (msgId, folder) => {
- let msgHdr;
- //
- if (!folder.isServer) {
- msgHdr = folder.msgDatabase.getMsgHdrForMessageID(
- msgId
- );
- if (msgHdr) {
- return msgHdr;
- }
- }
-
- //
- for (let currentFolder of folder.subFolders) {
- msgHdr = findMsgIdInFolder(
- msgId,
- currentFolder
- );
- if (msgHdr) {
- return msgHdr;
- }
- }
- return null;
- };
-
- let allServers =
- MailServices.accounts.allServers;
- if (startServer) {
- allServers = [startServer].concat(
- allServers.filter(
- s => s.key != startServer.key
- )
- );
- }
- for (let server of allServers) {
- if (
- server &&
- server.canSearchMessages &&
- !server.isDeferredTo
- ) {
- let msgHdr = findMsgIdInFolder(
- msgId,
- server.rootFolder
- );
- if (msgHdr) {
- return msgHdr;
- }
- }
- }
- return null;
- };
-
- let msgHdr = null;
- if (MailUtils.getMsgHdrForMsgId) {
- msgHdr = MailUtils.getMsgHdrForMsgId(msgId);
- } else {
- console.warn(
- `MailUtils.getMsgHdrForMsgId doesn't exist, falling back to manual traversal to find msgHdr`
- );
- msgHdr = getFirstMessageHeaderForMessageId(
- msgId
- );
- }
-
- if (msgHdr) {
- if (MailUtils.displayMessage) {
- MailUtils.displayMessage(msgHdr);
- } else {
- console.error(
- `MailUtils.displayMessage doesn't exist, no fallback available (${navigator &&
- navigator.userAgent})`
- );
- }
- } else {
- console.warn(
- `No headers found for msgId: '${msgId}'`
- );
- }
- }
- } catch (e) {
- console.error(
- '[mailmindr] mailmindrMessagesApi.openMessageByMessageHeaderId: ',
- e
- );
- }
- }
- }
- };
- }
-
- onShutdown(isAppShutdown) {
- if (isAppShutdown) {
- return;
- }
-
- //
-
- Services.obs.notifyObservers(null, 'startupcache-invalidate', null);
- }
-};
diff -Nru mailmindr-1.4.0/debian/changelog mailmindr-1.7.1/debian/changelog
--- mailmindr-1.4.0/debian/changelog 2022-09-24 20:08:36.000000000 +0200
+++ mailmindr-1.7.1/debian/changelog 2024-09-18 16:00:14.000000000 +0200
@@ -1,3 +1,34 @@
+mailmindr (1.7.1-1~deb12u1) bookworm; urgency=medium
+
+ * Prepared for bookworm proposed-update
+
+ -- Mechtilde Stehmann <mechtilde at debian.org> Wed, 18 Sep 2024 16:00:14 +0200
+
+mailmindr (1.7.1-1) unstable; urgency=medium
+
+ [ Mechtilde ]
+ * [c69b29d] New upstream version 1.7.1
+ * [c0d7293] Bumped version of dependencies in d/control
+
+ -- Mechtilde Stehmann <mechtilde at debian.org> Sun, 01 Sep 2024 16:26:31 +0200
+
+mailmindr (1.6.1-1) unstable; urgency=medium
+
+ [ Mechtilde ]
+ * [919abc8] New upstream version 1.6.1
+ * [1891988] Bumped standard version - no changes needed;
+ bumpd version of dependency
+ * [f1ea3a7] Bumped year in d/copyright
+
+ -- Mechtilde Stehmann <mechtilde at debian.org> Wed, 15 May 2024 17:26:47 +0200
+
+mailmindr (1.6.0-1) unstable; urgency=medium
+
+ [ Mechtilde ]
+ * [534d272] New upstream version 1.6.0
+
+ -- Mechtilde Stehmann <mechtilde at debian.org> Tue, 03 Oct 2023 18:07:14 +0200
+
mailmindr (1.4.0-1) unstable; urgency=medium
[ Mechtilde ]
diff -Nru mailmindr-1.4.0/debian/control mailmindr-1.7.1/debian/control
--- mailmindr-1.4.0/debian/control 2022-09-24 20:04:02.000000000 +0200
+++ mailmindr-1.7.1/debian/control 2024-09-01 15:02:05.000000000 +0200
@@ -4,7 +4,7 @@
Maintainer: Debian Mozilla Extension Maintainers <pkg-mozext-maintainers at lists.alioth.debian.org>
Uploaders: Mechtilde Stehmann <mechtilde at debian.org>
Build-Depends: debhelper-compat (=13), zip
-Standards-Version: 4.6.1
+Standards-Version: 4.7.0
Rules-Requires-Root: no
Vcs-Git: https://salsa.debian.org/webext-team/mailmindr.git
Vcs-Browser: https://salsa.debian.org/webext-team/mailmindr
@@ -13,7 +13,8 @@
Package: webext-mailmindr
Architecture: all
Depends: ${misc:Depends}
- , thunderbird (>=1:102.2)
+ , thunderbird (>=1:110.10)
+ , thunderbird (<= 1:129.x)
Description: Reminder for emails
mailmindr is an addon for the email client "Mozilla Thunderbird".
It offers additional functionality to handle the everyday work
diff -Nru mailmindr-1.4.0/debian/copyright mailmindr-1.7.1/debian/copyright
--- mailmindr-1.4.0/debian/copyright 2022-09-24 20:02:40.000000000 +0200
+++ mailmindr-1.7.1/debian/copyright 2024-05-15 17:16:01.000000000 +0200
@@ -3,11 +3,11 @@
Source: https://mailmindr.net/
Files: *
-Copyright: 2013-2022 Arnd I?ler
+Copyright: 2013-2024 Arnd I?ler
License: MPL-2
Files: debian/*
-Copyright: 2019-2022 Mechtilde Stehmann <mechtilde at debian.org>
+Copyright: 2019-2024 Mechtilde Stehmann <mechtilde at debian.org>
License: MPL-2
License: MPL-2
diff -Nru mailmindr-1.4.0/images/mailmindr-flag_marker.svg mailmindr-1.7.1/images/mailmindr-flag_marker.svg
--- mailmindr-1.4.0/images/mailmindr-flag_marker.svg 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/images/mailmindr-flag_marker.svg 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="160"
+ height="160"
+ viewBox="0 0 42.333332 42.333332"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.0.2 (e86c8708, 2021-01-15)"
+ sodipodi:docname="mailmindr-flag.svg"
+ inkscape:export-filename="/Users/arndissler/mailmindr-flag.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3558361"
+ inkscape:cx="184.86935"
+ inkscape:cy="43.23945"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="true"
+ inkscape:pagecheckerboard="false"
+ units="px"
+ inkscape:window-width="1464"
+ inkscape:window-height="1113"
+ inkscape:window-x="120"
+ inkscape:window-y="66"
+ inkscape:window-maximized="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid833"
+ spacingx="2.6458333"
+ spacingy="2.6458333"
+ snapvisiblegridlinesonly="true"
+ empspacing="4" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+ x="0"
+ y="44.979168"
+ id="text896"><tspan
+ sodipodi:role="line"
+ id="tspan894"
+ x="0"
+ y="44.979168"
+ style="stroke-width:0.264583" /></text>
+ <path
+ style="fill:#ad3bff;stroke:#0c0c0c;stroke-width:3.0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 10.583333,37.041666 c 0,-29.1041661 0,-29.1041661 0,-29.1041661 h 19.402778 l -4.850695,7.2760411 4.850695,7.276042 H 10.583333"
+ id="path872" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="zzz" />
+</svg>
diff -Nru mailmindr-1.4.0/images/mailmindr-flag_marker--white.svg mailmindr-1.7.1/images/mailmindr-flag_marker--white.svg
--- mailmindr-1.4.0/images/mailmindr-flag_marker--white.svg 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/images/mailmindr-flag_marker--white.svg 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="160"
+ height="160"
+ viewBox="0 0 42.333332 42.333332"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.0.2 (e86c8708, 2021-01-15)"
+ sodipodi:docname="mailmindr-flag.svg"
+ inkscape:export-filename="/Users/arndissler/mailmindr-flag.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3558361"
+ inkscape:cx="184.86935"
+ inkscape:cy="43.23945"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="true"
+ inkscape:pagecheckerboard="false"
+ units="px"
+ inkscape:window-width="1464"
+ inkscape:window-height="1113"
+ inkscape:window-x="120"
+ inkscape:window-y="66"
+ inkscape:window-maximized="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid833"
+ spacingx="2.6458333"
+ spacingy="2.6458333"
+ snapvisiblegridlinesonly="true"
+ empspacing="4" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+ x="0"
+ y="44.979168"
+ id="text896"><tspan
+ sodipodi:role="line"
+ id="tspan894"
+ x="0"
+ y="44.979168"
+ style="stroke-width:0.264583" /></text>
+ <path
+ style="fill:#ad3bff;stroke:#f9f9fa;stroke-width:3.0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 10.583333,37.041666 c 0,-29.1041661 0,-29.1041661 0,-29.1041661 h 19.402778 l -4.850695,7.2760411 4.850695,7.276042 H 10.583333"
+ id="path872" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="zzz" />
+</svg>
diff -Nru mailmindr-1.4.0/images/mailmindr-flag--rainbow-shadow.svg mailmindr-1.7.1/images/mailmindr-flag--rainbow-shadow.svg
--- mailmindr-1.4.0/images/mailmindr-flag--rainbow-shadow.svg 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/images/mailmindr-flag--rainbow-shadow.svg 1970-01-01 01:00:00.000000000 +0100
@@ -1,201 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="160"
- height="160"
- viewBox="0 0 42.333332 42.333332"
- version="1.1"
- id="svg8"
- inkscape:version="1.0.2 (e86c8708, 2021-01-15)"
- sodipodi:docname="mailmindr-flag--rainbow-shadow.svg"
- inkscape:export-filename="/Users/arndissler/mailmindr-flag--rainbow-shadow.png"
- inkscape:export-xdpi="285"
- inkscape:export-ydpi="285">
- <defs
- id="defs2">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient861">
- <stop
- style="stop-color:#9400ff;"
- offset="0"
- id="stop875" />
- <stop
- style="stop-color:#ad3bff;"
- offset="1"
- id="stop877" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient861"
- id="linearGradient863"
- x1="-0.66145998"
- y1="21.166666"
- x2="42.994793"
- y2="21.166666"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.125,0,0,1.125,-5.2916666,-5.2916666)" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="8.5227625"
- inkscape:cx="22.992513"
- inkscape:cy="122.66854"
- inkscape:document-units="mm"
- inkscape:current-layer="layer1"
- inkscape:document-rotation="0"
- showgrid="true"
- inkscape:pagecheckerboard="false"
- units="px"
- inkscape:window-width="1733"
- inkscape:window-height="1202"
- inkscape:window-x="1438"
- inkscape:window-y="81"
- inkscape:window-maximized="0">
- <inkscape:grid
- type="xygrid"
- id="grid833"
- spacingx="2.6458333"
- spacingy="2.6458333"
- snapvisiblegridlinesonly="true"
- empspacing="4" />
- </sodipodi:namedview>
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <rect
- style="fill:url(#linearGradient863);fill-opacity:1;stroke:none;stroke-width:1.48828"
- id="rect32"
- width="47.625"
- height="47.625"
- x="-5.2916665"
- y="-5.2916665" />
- <path
- id="rect928-0"
- style="fill:#8000d7;fill-opacity:1;stroke:none;stroke-width:1.40479"
- d="M 21.166666,24.870834 49.179345,52.475994 36.840754,63.987328 8.8280779,36.382168 8.9946579,24.711111 Z"
- sodipodi:nodetypes="cccccc" />
- <path
- id="rect928-0-1-4"
- style="fill:#6200a4;fill-opacity:1;stroke:none;stroke-width:0.272986"
- d="m 10.790166,34.395833 10.3765,10.583333 -2.323503,2.308367 -10.3764971,-10.583332 0.031366,-2.340398 z"
- sodipodi:nodetypes="cccccc" />
- <path
- id="rect928"
- style="fill:#8000d7;fill-opacity:1;stroke:none;stroke-width:1.32292"
- d="M 31.75,5.8208333 58.298387,31.652372 47.227728,43.030251 20.679343,17.198715 20.525733,5.9744453 Z"
- sodipodi:nodetypes="cccccc" />
- <path
- id="rect928-0-1"
- style="fill:#6200a4;fill-opacity:1;stroke:none;stroke-width:1.32141"
- d="M 23.8125,13.229166 49.347141,40.024992 38.100038,51.198834 12.565399,24.403009 12.717243,13.074126 Z"
- sodipodi:nodetypes="cccccc" />
- <text
- xml:space="preserve"
- style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
- x="0"
- y="44.979168"
- id="text896"><tspan
- sodipodi:role="line"
- id="tspan894"
- x="0"
- y="44.979168"
- style="stroke-width:0.264583" /></text>
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.16237;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 7.4839361,21.166667 19.579163,7.9374999"
- id="path960" />
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.16237;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 20.183933,21.166667 32.279181,7.9374999"
- id="path978" />
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.16237;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 24.417271,21.166667 36.512541,7.9374999"
- id="path984" />
- <g
- id="g1022">
- <path
- id="path869"
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fc0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77953;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
- d="M 46.248047 34 L 46 34.271484 L 46 51.984375 L 62.414062 34.03125 L 62.378906 34 L 46.248047 34 z "
- transform="scale(0.26458333)" />
- <path
- id="path962"
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77953;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 62.246094 34 L 46 51.767578 L 46 69.482422 L 78.410156 34.03125 L 78.375 34 L 62.246094 34 z "
- transform="scale(0.26458333)" />
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.16237;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 11.717268,21.166667 23.812495,7.9374999"
- id="path966" />
- <path
- id="path968"
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77953;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 78.25 34 L 46 69.269531 L 46 76 L 56.042969 76 L 94.40625 34.03125 L 94.373047 34 L 78.25 34 z "
- transform="scale(0.26458333)" />
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.16237;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 15.950601,21.166667 28.045831,7.9374999"
- id="path972" />
- <path
- id="path974"
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77953;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 94.246094 34 L 55.873047 75.966797 L 55.910156 76 L 72.041016 76 L 89.246094 57.181641 L 88 55 L 100 34 L 94.246094 34 z "
- transform="scale(0.26458333)" />
- <path
- id="path980"
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77953;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 89.171875 57.048828 L 71.876953 75.966797 L 71.914062 76 L 88.044922 76 L 95.400391 67.951172 L 89.171875 57.048828 z "
- transform="scale(0.26458333)" />
- <path
- id="path986"
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#800080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77953;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
- d="M 95.324219 67.818359 L 87.873047 75.966797 L 87.910156 76 L 100 76 L 95.324219 67.818359 z "
- transform="scale(0.26458333)" />
- </g>
- <path
- style="fill:none;stroke:#0c0c0c;stroke-width:3.969;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 10.583333,36.512499 c 0,-29.1041658 0,-29.1041658 0,-29.1041658 h 19.402778 l -4.850695,7.2760408 4.850695,7.276042 H 10.583333"
- id="path872" />
- <path
- id="rect928-1"
- style="fill:#8000d7;fill-opacity:1;stroke:none;stroke-width:1.32292"
- d="M 60.854164,-18.520833 H 97.895832 V -2.6458333 H 60.854166 l -7.9375,-7.9374997 z"
- sodipodi:nodetypes="cccccc" />
- <path
- id="rect928-4"
- style="fill:#8000d7;fill-opacity:1;stroke:none;stroke-width:0.364777"
- d="m 72.152807,35.111142 7.222192,7.222191 -3.095224,3.095225 -7.222192,-7.222191 v -3.095225 z"
- sodipodi:nodetypes="cccccc" />
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="zzz" />
-</svg>
diff -Nru mailmindr-1.4.0/_locales/de/messages.json mailmindr-1.7.1/_locales/de/messages.json
--- mailmindr-1.4.0/_locales/de/messages.json 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/_locales/de/messages.json 2024-08-04 22:14:20.000000000 +0200
@@ -17,6 +17,25 @@
"mailmindrMessageDisplayButton": {
"message": "Wiedervorlage"
},
+ "mailmindrComposeMessageButton": {
+ "message": "Wiedervorlage setzen"
+ },
+ "mailmindrComposeMessageButton.edit": {
+ "message": "Wiedervorlage bearbeiten"
+ },
+ "mailmindrComposeMessageButton.detailed": {
+ "message": "$DATE$ um $TIME$",
+ "placeholders": {
+ "date": {
+ "content": "$1",
+ "example": "2021-12-24"
+ },
+ "time": {
+ "content": "$2",
+ "example": "09:00"
+ }
+ }
+ },
"module.string-utils.chunk.and": {
"message": "und"
},
@@ -380,6 +399,12 @@
"view.options.default-action-preset.description": {
"message": "Voreingestellte Aktion bei der Erstellung einer Wiedervorlage"
},
+ "view.options.default-reminder-preset.label": {
+ "message": "Standard-Erinnerung:"
+ },
+ "view.options.default-reminder-preset.description": {
+ "message": "Voreingestellte Zeit, wann an eine Wiedervorlage im Voraus erinnert werden soll."
+ },
"view.options.snooze-time.label": {
"message": "Schlummerfunktion:",
"description": "Default snooze time for alerts"
@@ -476,6 +501,9 @@
"view.message-display.notification.button.edit": {
"message": "Wiedervorlage bearbeiten"
},
+ "view.message-display.notification.button.remove": {
+ "message": "Wiedervorlage entfernen"
+ },
"view.message-display.notification.button.message": {
"message": "Eine Wiedervorlage ist gesetzt: $1"
},
@@ -529,7 +557,7 @@
"mailmindr.utils.core.timePair": {
"message": "#1 #2"
},
- "mailmindr.utils.core.relative.weeks": {
+ "mailmindr.utils.core.relative.weeks.one": {
"message": "in einer Woche;in #1 Wochen"
},
"mailmindr.utils.core.relative.days": {
@@ -592,5 +620,11 @@
},
"mailmindr.utils.core.remindme.before.no-reminder.other": {
"message": "Keine Erinnerung"
+ },
+ "mailmindrShortcut_OpenList": {
+ "message": "Liste mit den Wiedervorlagen ?ffnen"
+ },
+ "mailmindrShortcut_SetFollowUp": {
+ "message": "Wiedervorlage setzen"
}
}
diff -Nru mailmindr-1.4.0/_locales/en/messages.json mailmindr-1.7.1/_locales/en/messages.json
--- mailmindr-1.4.0/_locales/en/messages.json 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/_locales/en/messages.json 2024-08-04 22:14:20.000000000 +0200
@@ -17,6 +17,25 @@
"mailmindrMessageDisplayButton": {
"message": "Follow-Up"
},
+ "mailmindrComposeMessageButton": {
+ "message": "Set follow-up"
+ },
+ "mailmindrComposeMessageButton.edit": {
+ "message": "Edit follow-up"
+ },
+ "mailmindrComposeMessageButton.detailed": {
+ "message": "$DATE$ at $TIME$",
+ "placeholders": {
+ "date": {
+ "content": "$1",
+ "example": "2021-12-24"
+ },
+ "time": {
+ "content": "$2",
+ "example": "09:00"
+ }
+ }
+ },
"module.string-utils.chunk.and": {
"message": "and"
},
@@ -380,6 +399,12 @@
"view.options.default-action-preset.description": {
"message": "Pre-selected action setting when creating a new reminder"
},
+ "view.options.default-reminder-preset.label": {
+ "message": "Default reminder:"
+ },
+ "view.options.default-reminder-preset.description": {
+ "message": "Pre-selected time for when the reminder dialog appears"
+ },
"view.options.snooze-time.label": {
"message": "Snooze time in Minutes:",
"description": "Default snooze time for alerts"
@@ -476,9 +501,58 @@
"view.message-display.notification.button.edit": {
"message": "Edit follow-up"
},
+ "view.message-display.notification.button.remove": {
+ "message": "Remove follow-up"
+ },
"view.message-display.notification.button.message": {
"message": "Follow-up is set for $1"
},
+
+ "view.dialog.create-outgoing-mindr.title.create": {
+ "message": "Create reminder"
+ },
+ "view.dialog.create-outgoing-mindr.title.edit": {
+ "message": "Edit reminder"
+ },
+ "view.dialog.create-outgoing-mindr.label.subject": {
+ "message": "Subject:"
+ },
+ "view.dialog.create-outgoing-mindr.label.due": {
+ "message": "Reply until:"
+ },
+ "view.dialog.create-outgoing-mindr.label.action": {
+ "message": "Action:"
+ },
+ "view.dialog.create-outgoing-mindr.label.icebox": {
+ "message": "Move to icebox folder"
+ },
+ "view.dialog.create-outgoing-mindr.label.icebox.placeholder": {
+ "message": "move to icebox folder: $FOLDERNAME$",
+ "placeholders": {
+ "foldername": {
+ "content": "$1",
+ "example": "Inbox"
+ }
+ }
+ },
+ "view.dialog.create-outgoing-mindr.label.remind-me": {
+ "message": "Remind me:"
+ },
+ "view.dialog.create-outgoing-mindr.caption.remove-follow-up": {
+ "message": "Remove follow-up"
+ },
+ "view.dialog.create-outgoing-mindr.caption.create-mindr": {
+ "message": "Create"
+ },
+ "view.dialog.create-outgoing-mindr.caption.update-mindr": {
+ "message": "Update"
+ },
+ "view.dialog.create-outgoing-mindr.caption.cancel": {
+ "message": "Cancel"
+ },
+ "view.dialog.create-outgoing-mindr.header": {
+ "message": "Set follow-up"
+ },
"mailmindr.utils.core.plural.beforestart.minutes.one": {
"message": "$1 minute before due",
"description": "Menu entry, e.g. '1 minute before due'"
@@ -592,5 +666,11 @@
},
"mailmindr.utils.core.remindme.before.no-reminder.other": {
"message": "No reminder"
+ },
+ "mailmindrShortcut_OpenList": {
+ "message": "Open follow-up list"
+ },
+ "mailmindrShortcut_SetFollowUp": {
+ "message": "Set follow-up"
}
}
diff -Nru mailmindr-1.4.0/manifest.json mailmindr-1.7.1/manifest.json
--- mailmindr-1.4.0/manifest.json 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/manifest.json 2024-08-04 22:14:20.000000000 +0200
@@ -6,13 +6,13 @@
"applications": {
"gecko": {
"id": "mailmindr at arndissler.net",
- "strict_min_version": "78.0",
- "strict_max_version": "103.0"
+ "strict_min_version": "102.0",
+ "strict_max_version": "129.*"
}
},
"author": "Arnd Issler",
"homepage_url": "https://mailmindr.net/",
- "version": "1.4.0",
+ "version": "1.7.1",
"icons": {
"16": "images/mailmindr-flag--rainbow.svg",
"32": "images/mailmindr-flag--rainbow.svg"
@@ -20,15 +20,33 @@
"permissions": [
"activeTab",
"accountsRead",
+ "compose",
"menus",
"messagesModify",
"messagesMove",
"messagesRead",
+ "messagesUpdate",
"storage",
"tabs",
- "tabHide",
"unlimitedStorage"
],
+ "compose_action": {
+ "browser_style": true,
+ "default_title": "__MSG_mailmindrComposeMessageButton__",
+ "default_popup": "views/popups/create-outgoing-mindr/index.html",
+ "theme_icons": [
+ {
+ "dark": "images/mailmindr-flag.svg",
+ "light": "images/mailmindr-flag--white.svg",
+ "size": 16
+ },
+ {
+ "dark": "images/mailmindr-flag.svg",
+ "light": "images/mailmindr-flag--white.svg",
+ "size": 32
+ }
+ ]
+ },
"browser_action": {
"browser_style": true,
"default_title": "__MSG_mailmindrMainToolbarButton__",
@@ -77,7 +95,8 @@
"mac": "Command+Shift+1",
"chromeos": "Ctrl+Shift+1",
"linux": "Ctrl+Shift+1"
- }
+ },
+ "description": "__MSG_mailmindrShortcut_SetFollowUp__"
},
"mailmindr_open_list": {
"suggested_key": {
@@ -85,23 +104,8 @@
"mac": "Command+Shift+0",
"chromeos": "Ctrl+Shift+0",
"linux": "Ctrl+Shift+0"
- }
- }
- },
- "experiment_apis": {
- "mailmindrMessagesApi": {
- "schema": "schema.json",
- "parent": {
- "scopes": [
- "addon_parent"
- ],
- "paths": [
- [
- "mailmindrMessagesApi"
- ]
- ],
- "script": "api/messages.js"
- }
+ },
+ "description": "__MSG_mailmindrShortcut_OpenList__"
}
}
}
diff -Nru mailmindr-1.4.0/modules/core-utils.mjs.js mailmindr-1.7.1/modules/core-utils.mjs.js
--- mailmindr-1.4.0/modules/core-utils.mjs.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/modules/core-utils.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -5,7 +5,42 @@
} from './string-utils.mjs.js';
import { createLogger } from './logger.mjs.js';
-const logger = createLogger('core-utils');
+const logger = createLogger('modules/core-utils');
+
+export const throttle = (func, interval) => {
+ let shouldFire = true;
+ return () => {
+ if (shouldFire) {
+ func();
+ shouldFire = false;
+ setTimeout(() => {
+ shouldFire = true;
+ }, interval);
+ }
+ };
+};
+
+export const sanitizeHeaderMessageId = headerMessageId =>
+ headerMessageId
+ .replace('>', '')
+ .replace('<', '')
+ .trim();
+
+export const sendConnectionMessageEx = async (
+ openConnections,
+ message,
+ connectionName
+) => {
+ logger.info(`open connections?`, openConnections, 'send message', message);
+ const connection = openConnections.find(con => con.name === connectionName);
+ if (connection) {
+ await connection.postMessage(message);
+ } else {
+ logger.warn(
+ `No connection found in ${openConnections.length} open connections`
+ );
+ }
+};
/**
* Finds the currently running Thunderbird version
@@ -33,7 +68,8 @@
export const buildQueryInfoForMessageAndFolder = (messageDetails, folder) => {
const { headerMessageId, author, subject: _ } = messageDetails;
const queryInfo = {
- headerMessageId: headerMessageId.substr(1, headerMessageId.length - 2),
+ //
+ headerMessageId: sanitizeHeaderMessageId(headerMessageId),
author: getMailAddress(author),
folder: {
accountId: folder.accountId,
@@ -64,7 +100,8 @@
* @returns {boolean} `true` if the mindrs are the same by `headerMessageId` and (internal) `guid`
*/
export const isSameMindr = (mindrA, mindrB) =>
- mindrA.headerMessageId === mindrB.headerMessageId &&
+ sanitizeHeaderMessageId(mindrA.headerMessageId) ===
+ sanitizeHeaderMessageId(mindrB.headerMessageId) &&
mindrA.guid === mindrB.guid;
export const createMailmindrId = scope =>
@@ -80,6 +117,20 @@
export const isExecuted = mindr => mindr.isExecuted;
/**
+ * Returns true when a mindr is waiting for a reply
+ * @param {Mindr} mindr
+ * @returns Boolean
+ */
+export const hasReply = mindr =>
+ mindr.isWaitingForReply &&
+ mindr.metaData &&
+ 'string' === typeof mindr.metaData.replyHeaderMessageId;
+
+export const showReminderForMindr = mindr =>
+ String(mindr.remindMeMinutesBefore) !== '-1' ||
+ mindr.action.showReminder !== false;
+
+/**
* Checks if a indr is overdue
* @param {Mindr} mindr
* @returns {boolean}
@@ -282,6 +333,20 @@
return actionTemplates;
};
+export const createRemindMeMinutesBefore = () => {
+ const remindMeMinutesBefore = [
+ { minutes: 0, display: 0, unit: 'on-time' },
+ { minutes: 5, display: 5, unit: 'minutes' },
+ { minutes: 15, display: 15, unit: 'minutes' },
+ { minutes: 30, display: 30, unit: 'minutes' },
+ { minutes: 60, display: 1, unit: 'hours' },
+ { minutes: 120, display: 2, unit: 'hours' },
+ { minutes: 240, display: 4, unit: 'hours' },
+ { minutes: -1, display: null, unit: 'no-reminder' }
+ ];
+ return remindMeMinutesBefore;
+};
+
/**
* Extracts the email address of an email author
* @param {string} author The author of an email message, might be an email address or in format Firstname Surname <email at example.com>
@@ -304,7 +369,9 @@
return author;
};
-export const createMindrFromActionTemplate = async mindrData => {
+export const createMindrFromActionTemplate = async (
+ /** @type {Mindr} */ mindrData
+) => {
const {
guid,
headerMessageId,
@@ -313,7 +380,8 @@
due,
remindMeMinutesBefore,
metaData,
- isExecuted = false
+ isExecuted = false,
+ isWaitingForReply = false
} = mindrData;
const {
flag,
@@ -346,7 +414,8 @@
notes,
metaData,
isExecuted,
- modified
+ modified,
+ isWaitingForReply
};
};
@@ -434,6 +503,10 @@
}
const { accountId, name, path, type } = folder;
+ if (!accountId) {
+ return null;
+ }
+
const account = await browser.accounts.get(accountId);
const identityEmailAddressList = account.identities.map(
identity => identity.email
@@ -465,8 +538,13 @@
}
}
+ if (accountId) {
+ //
+ return { accountId, name, path, type };
+ }
+
//
- return { accountId, name, path, type };
+ return null;
};
export const genericFoldersAreEqual = (a, b) =>
@@ -516,7 +594,7 @@
const { due, remindMeMinutesBefore } = mindr;
const dueTime = due.getTime();
const minutesBefore = remindMeMinutesBefore; // Math.max(remindMeMinutesBefore, 0);
- console.warn(`* minutes before: ${minutesBefore}`);
+ //
const reminderLookahead = (minutesBefore || lookeahedInMinutes) * 60 * 1000;
const remindMeAt = dueTime - reminderLookahead;
@@ -563,58 +641,6 @@
}, seed);
};
-export const snoozeMindrs = async (
- dispatch,
- mindrGuidList,
- mindrs,
- snoozeTimeMinutes,
- correlationId
-) => {
- do {
- const guid = mindrGuidList.pop();
- const theMindr = mindrs.find(mindr => mindr.guid === guid);
-
- if (theMindr) {
- const mindr = { ...theMindr };
- const { due, isExecuted, remindMeMinutesBefore } = mindr;
- //
- //
-
- const dueTime = due.getTime();
- const now = Date.now();
-
- if (dueTime < now) {
- logger.log(
- `set snooze (from now): ${mindr.remindMeMinutesBefore}`,
- { correlationId, mindrGuid: mindr.guid }
- );
-
- if (isExecuted) {
- mindr.remindMeMinutesBefore =
- -1 * (now - dueTime) + (snoozeTimeMinutes || 0);
- } else {
- //
- const diff = Math.floor(
- (now + snoozeTimeMinutes * 60 * 1000 - dueTime) /
- 60 /
- 1000
- );
- mindr.remindMeMinutesBefore = -1 * diff;
- }
- }
-
- logger.log(`snoozed by: ${mindr.remindMeMinutesBefore}`, {
- correlationId,
- mindrGuid: mindr.guid
- });
-
- await dispatch('mindr:create-or-update', mindr);
- } else {
- console.log(`ugh: ${theMindr}`);
- }
- } while (mindrGuidList.length);
-};
-
/**
*
* @param {string} identityMailAddress
@@ -632,7 +658,9 @@
);
const isIceboxFolderSet = Boolean(iceboxFolderSettings?.folder);
- const isDefaultIceboxFolderSet = Boolean(settings?.defaultIceboxFolder);
+ const isDefaultIceboxFolderSet = Boolean(
+ settings?.defaultIceboxFolder?.folder
+ );
logger.info('icebox folder available?', {
identityMailAddress,
@@ -645,8 +673,62 @@
}
if (isDefaultIceboxFolderSet) {
- return await localFolderToGenericFolder(settings.defaultIceboxFolder);
+ return await localFolderToGenericFolder(
+ settings.defaultIceboxFolder.folder
+ );
}
return null;
};
+
+/**
+ *
+ * @param {Mindr} mindr
+ * @param { { readonly snoozeTimeMinutes: number; readonly correlationId: string; } } param1
+ * @returns {Mindr}
+ */
+export const snoozeMindr = (
+ mindr,
+ { snoozeTimeMinutes, correlationId = '<correlationId not set>' }
+) => {
+ const /** @type {EditableMindr} */ modifiedMindr = structuredClone(mindr);
+ const { due, isExecuted, remindMeMinutesBefore } = modifiedMindr;
+
+ const dueTime = due.getTime();
+ const now = Date.now();
+
+ //
+ if (dueTime < now) {
+ const { remindMeMinutesBefore } = modifiedMindr;
+
+ if (isExecuted) {
+ //
+ //
+ //
+
+ if (modifiedMindr.remindMeMinutesBefore < 0) {
+ modifiedMindr.action.showReminder = false;
+ }
+
+ modifiedMindr.remindMeMinutesBefore =
+ //
+ -1 *
+ Math.floor(
+ (now - dueTime + snoozeTimeMinutes * 60 * 1000) / 60 / 1000
+ );
+ } else {
+ //
+
+ modifiedMindr.due = new Date(
+ Date.now() + snoozeTimeMinutes * 60 * 1000
+ );
+ }
+ } else {
+ console.warn(`mindr.remindMeMinutesBefore is not modified`);
+ modifiedMindr.due = new Date(
+ Date.now() + remindMeMinutesBefore * 60 * 1000
+ );
+ }
+
+ return modifiedMindr;
+};
diff -Nru mailmindr-1.4.0/modules/defaults.mjs.js mailmindr-1.7.1/modules/defaults.mjs.js
--- mailmindr-1.4.0/modules/defaults.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/defaults.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,37 @@
+/** @type {MailmindrState} */
+export const initialState = {
+ presets: {
+ actions: [],
+ time: []
+ },
+ settings: {
+ defaultActionPreset: {
+ copyMessageTo: null,
+ moveMessageTo: null,
+ text: null,
+ tagWithLabel: null,
+ flag: null,
+ isSystemAction: false,
+ markUnread: false,
+ showReminder: false
+ },
+ defaultIceboxFolder: '',
+ defaultTimepreset: {
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ isGenerated: true,
+ isRelative: false,
+ isSelectable: false,
+ text: null
+ },
+ iceboxFolders: [],
+ snoozeTime: 15
+ },
+ mindrs: [],
+ active: [],
+ overdue: [],
+ openDialogs: [],
+ openConnections: [],
+ __inExecution: []
+};
diff -Nru mailmindr-1.4.0/modules/logger.mjs.js mailmindr-1.7.1/modules/logger.mjs.js
--- mailmindr-1.4.0/modules/logger.mjs.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/modules/logger.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -44,9 +44,38 @@
this._connected = false;
this._retry = true;
this._buffer = [];
+ this._enabledScopes = [];
console.log(`starting logger for scope '${this.scope}'`);
- this.tryConnectAndSendBuffer();
+
+ this.getFilterFromStorage().then(enabledScopes => {
+ this._enabledScopes = enabledScopes;
+ let enabled = false;
+ for (let localScope in enabledScopes) {
+ let severityString = enabledScopes[localScope];
+ let severity = LogLevel[severityString] || LogLevel.WARN;
+ if (localScope.indexOf('*') >= 0) {
+ let theScope = localScope.substring(
+ 0,
+ localScope.indexOf('*')
+ );
+ if (enabled === false) {
+ const item = this._scopes.find(s =>
+ s.name.startsWith(theScope)
+ );
+ enabled = item !== undefined;
+ }
+ } else {
+ if (enabled === false) {
+ //
+ enabled = this._scopes.find(s => s.name === localScope);
+ }
+ }
+ this._severity = severity;
+ }
+
+ this.tryConnectAndSendBuffer();
+ });
}
createContextLogger(scope) {
@@ -65,6 +94,18 @@
}
}
+ async getFilterFromStorage() {
+ try {
+ const { logFilter } = await messenger.storage.local.get(
+ 'logFilter'
+ );
+
+ return logFilter;
+ } catch (exception) {
+ return [];
+ }
+ }
+
async tryConnectAndSendBuffer() {
let count = 0;
while (this._connected === false && this._retry) {
@@ -115,6 +156,10 @@
context
};
+ if (this._severity > severity) {
+ return;
+ }
+
this.trySend(logItem);
switch (severity) {
diff -Nru mailmindr-1.4.0/modules/message-actions.mjs.js mailmindr-1.7.1/modules/message-actions.mjs.js
--- mailmindr-1.4.0/modules/message-actions.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/message-actions.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,187 @@
+import { createCorrelationId, createLogger } from '../modules/logger.mjs.js';
+import {
+ genericFolderToLocalFolder,
+ getFlatFolderList
+} from '../modules/core-utils.mjs.js';
+import {
+ applyActionToMessageInFolder,
+ doMoveMessageToFolder
+} from './message-utils.mjs.js';
+
+const logger = createLogger('modules/message-actions');
+
+export const executeMindr = async mindr => {
+ const { headerMessageId, metaData, action, guid } = mindr;
+ logger.log(`START executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
+ guid,
+ headerMessageId
+ });
+ const {
+ author,
+ subject,
+ folderAccountId,
+ folderName,
+ folderPath,
+ folderType,
+ folderAccountIdentityMailAddress
+ } = metaData;
+ const correlationId = createCorrelationId('executeMindr');
+ const executionStart = Date.now();
+ const { copyMessageTo, moveMessageTo } = action;
+
+ const applyAction = async (messageId, action) => {
+ const {
+ flag,
+ markUnread,
+ showReminder,
+ tagWithLabel,
+ copyMessageTo,
+ moveMessageTo
+ } = action;
+ const messageProps = {
+ ...(flag && { flagged: true }),
+ ...(markUnread && { read: false })
+ };
+
+ logger.info(`? apply update to message ${messageId}`, messageProps);
+
+ await messenger.messages.update(messageId, messageProps);
+ };
+
+ const destinationFolder = moveMessageTo
+ ? await genericFolderToLocalFolder(moveMessageTo)
+ : null;
+ const possibleSourceFolder = await genericFolderToLocalFolder({
+ accountId: folderAccountId,
+ path: folderPath,
+ identityEmailAddress: folderAccountIdentityMailAddress
+ });
+ const flatFolderList = await getFlatFolderList();
+ const localFlatFolderList = await Promise.all(
+ flatFolderList
+ .filter(({ type }) => type === 'folder')
+ .map(async ({ folder }) => await genericFolderToLocalFolder(folder))
+ );
+ const folders = [
+ possibleSourceFolder,
+ ...localFlatFolderList.filter(
+ fldr =>
+ fldr.accountId !== possibleSourceFolder.accountId &&
+ fldr.path !== possibleSourceFolder.path
+ )
+ ];
+
+ logger.log(`BEGIN execution of ${mindr.guid}`, {
+ correlationId,
+ guid
+ });
+ const startTime = performance.now();
+
+ const targetFolders = folders;
+
+ let hasError = false;
+ let iterationCount = 0;
+
+ logger.log(`BEGIN targetFolder iteration`, {
+ guid,
+ correlationId,
+ targetFolderCount: (targetFolders || []).length,
+ targetFolders
+ });
+
+ const applyActionToMessage = async (message, messageFolder) => {
+ const { id } = message;
+
+ logger.log(`BEGIN applyActionToMessage`);
+ await applyAction(id, action);
+ logger.log(
+ `Do we have a destination folder? ${
+ destinationFolder ? 'yes' : 'no'
+ }`,
+ destinationFolder
+ );
+ if (destinationFolder) {
+ await doMoveMessageToFolder(
+ message,
+ destinationFolder,
+ correlationId
+ );
+ }
+ logger.log(`END applyActionToMessage`);
+ };
+
+ const applyActionToFirstMessageInFolders = async () => {
+ for await (let folder of targetFolders) {
+ logger.log(` -- executeMindr: folder loop (${folder.name})`, {
+ correlationId,
+ folder
+ });
+
+ try {
+ const actionResult = await applyActionToMessageInFolder(
+ folder,
+ { headerMessageId, author },
+ applyActionToMessage,
+ true
+ );
+ const { done, value } = await actionResult.next();
+ const success = Boolean(done && value && value.executed);
+ if (success) {
+ return true;
+ }
+ } catch (ex) {
+ logger.error('ERROR: execute mindr // mailmindr: >> !!', {
+ correlationId,
+ guid,
+ exception: ex
+ });
+ hasError = true;
+ }
+ iterationCount++;
+ }
+ return false;
+ };
+
+ await applyActionToFirstMessageInFolders();
+
+ logger.log(`END targetFolder iteration`, {
+ correlationId,
+ guid,
+ targetFolderCount: (targetFolders || []).length,
+ targetFolders
+ });
+
+ const endTime = performance.now();
+ logger.log(
+ `mailmindr: execution finished in ${endTime - startTime}ms`,
+ moveMessageTo
+ );
+
+ const executionEnd = Date.now();
+ const executionDuration = (executionEnd - executionStart) / 1000;
+
+ if (executionDuration > 3 * 60) {
+ logger.error(
+ `Execution of mindr '${guid}' took more than 180 seconds`,
+ { guid, correlationId, executionDuration }
+ );
+ } else if (executionDuration > 60) {
+ logger.warn(`Execution of mindr '${guid}' took more than 60 seconds`, {
+ guid,
+ correlationId,
+ executionDuration
+ });
+ } else {
+ logger.warn(
+ `Execution of mindr '${guid}' took ${executionDuration} seconds`,
+ { guid, correlationId, executionDuration }
+ );
+ }
+ logger.log(`END execution of ${mindr.guid}`, { correlationId, guid });
+ logger.log(`END executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
+ guid,
+ headerMessageId
+ });
+
+ return !hasError;
+};
diff -Nru mailmindr-1.4.0/modules/message-utils.mjs.js mailmindr-1.7.1/modules/message-utils.mjs.js
--- mailmindr-1.4.0/modules/message-utils.mjs.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/modules/message-utils.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -1,10 +1,27 @@
import {
buildQueryInfoForMessageAndFolder,
- getMailAddress
+ getMailAddress,
+ sanitizeHeaderMessageId
} from './core-utils.mjs.js';
import { createLogger, createCorrelationId } from './logger.mjs.js';
-const logger = createLogger('modules.message-utils');
+const logger = createLogger('modules/message-utils');
+
+export const getMessages = async function*(
+ /** @type {messenger.messages.MessageList} */ list
+) {
+ let page = list;
+ for (let message of page.messages) {
+ yield message;
+ }
+
+ while (page.id) {
+ page = await messenger.messages.continueList(page.id);
+ for (let message of page.messages) {
+ yield message;
+ }
+ }
+};
const findMessage = async (
messages,
@@ -55,9 +72,11 @@
}
const msgWithHeader = await browser.messages.getFull(id);
- const msgHdrId0 = msgWithHeader.headers['message-id'][0];
+ const msgHdrId0 = sanitizeHeaderMessageId(
+ msgWithHeader.headers['message-id'][0]
+ );
- if (msgHdrId0 === headerMessageId) {
+ if (msgHdrId0 === sanitizeHeaderMessageId(headerMessageId)) {
logger.log(
`SUCCESS headerMessageId found: '${headerMessageId}' === '${msgHdrId0}'`
);
@@ -106,7 +125,21 @@
queryInfo
});
- let queryResult = await messenger.messages.query(queryInfo);
+ let queryResult = null;
+ try {
+ queryResult = await messenger.messages.query(queryInfo);
+ } catch (queryError) {
+ //
+ queryResult = null;
+ logger.error(`applyActionToMessageInFolder: initial query failed`, {
+ queryError,
+ queryInfo
+ });
+
+ throw queryError;
+
+ return Promise.resolve(null);
+ }
logger.log(`messages query done, result is`, {
correlationId,
@@ -159,3 +192,95 @@
return Promise.resolve(null);
}
+
+export const doMoveMessageToFolder = async (
+ message,
+ destinationFolder,
+ parentCorrelationId
+) => {
+ const correlationId = createCorrelationId(
+ 'doMoveMessageToFolder',
+ parentCorrelationId
+ );
+ try {
+ const { id } = message;
+ const { accountId, path } = destinationFolder;
+ logger.log(`BEGIN move message ${id}`, {
+ correlationId,
+ message
+ });
+ await messenger.messages.move([id], { accountId, path });
+ logger.log(`END move message ${id}`, {
+ correlationId,
+ message
+ });
+ return true;
+ } catch (error) {
+ logger.error(error, { correlationId });
+ return false;
+ }
+};
+
+export const moveMessageToFolder = async (
+ messageDetails,
+ sourceFolder,
+ targetFolder
+) => {
+ const correlationId = createCorrelationId('moveMessagesToFolder');
+ try {
+ logger.info('moveMessageToFolder target', {
+ correlationId,
+ targetFolder
+ });
+
+ const moveMessageToFolderAction = async (
+ message,
+ _messageSourceFolder
+ ) => {
+ await doMoveMessageToFolder(message, targetFolder, correlationId);
+ };
+
+ logger.info(`BEGIN applying action to folder`, {
+ correlationId,
+ sourceFolder,
+ messageDetails
+ });
+ const actionResult = await applyActionToMessageInFolder(
+ sourceFolder,
+ messageDetails,
+ moveMessageToFolderAction,
+ true
+ );
+ const { done, value } = await actionResult.next();
+
+ //
+ const success = Boolean(done);
+
+ const logMessage = `moveMessageToFolder : applyActionToMessageInFolder returns { done: ${done}, value: ${value} }`;
+ if (success) {
+ logger.info(logMessage, { correlationId, result: { done, value } });
+ } else {
+ logger.warn(logMessage, { correlationId, result: { done, value } });
+ }
+
+ if (value === null) {
+ logger.error(
+ `moveMessageToFolder : applyActionToMessageInFolder message not found in folder`
+ );
+ }
+
+ logger.info(`END applying action to folder`, {
+ correlationId,
+ sourceFolder,
+ messageDetails
+ });
+ } catch (e) {
+ logger.error(`moveMessageToFolder failed: ${e.message}`, {
+ correlationId,
+ error: e
+ });
+ return false;
+ }
+
+ return true;
+};
diff -Nru mailmindr-1.4.0/modules/storage.mjs.js mailmindr-1.7.1/modules/storage.mjs.js
--- mailmindr-1.4.0/modules/storage.mjs.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/modules/storage.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -1,6 +1,6 @@
import { createCorrelationId, createLogger } from './logger.mjs.js';
-const logger = createLogger('background');
+const logger = createLogger('modules/storage');
const tryParseOrReturnDefault = (content, defaultValue) => {
if (typeof content === 'object' && content !== null) {
diff -Nru mailmindr-1.4.0/modules/store/actions/actions.mjs.js mailmindr-1.7.1/modules/store/actions/actions.mjs.js
--- mailmindr-1.4.0/modules/store/actions/actions.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/actions.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,91 @@
+import {
+ ACTION__CONNECTION_CLOSE,
+ ACTION__CONNECTION_OPEN,
+ ACTION__DIALOG_CLOSE,
+ ACTION__DIALOG_OPEN,
+ ACTION__HEARTBEAT,
+ ACTION__LOCK_MINDR_FOR_EXECUTION,
+ ACTION__MINDR_CREATE_OR_UPDATE,
+ ACTION__MINDR_CREATE_OR_UPDATE_DRAFT,
+ ACTION__MINDR_REMOVE,
+ ACTION__MINDR_REMOVE_DRAFT,
+ ACTION__PRESET_TIMESPAN_CREATE,
+ ACTION__PRESET_TIMESPAN_REMOVE,
+ ACTION__PRESET_TIMESPAN_UPDATE,
+ ACTION__SETTINGS_UPDATE,
+ ACTION__UNLOCK_MINDR
+} from './actionTypes.mjs.js';
+
+export const heartBeat = () => ({
+ type: ACTION__HEARTBEAT
+});
+
+export const lockMindrForExecution = mindr => ({
+ type: ACTION__LOCK_MINDR_FOR_EXECUTION,
+ payload: { guid: mindr.guid }
+});
+
+export const unlockMindr = mindr => ({
+ type: ACTION__UNLOCK_MINDR,
+ payload: { guid: mindr.guid }
+});
+
+export const createOrUpdateDraft = draft => ({
+ type: ACTION__MINDR_CREATE_OR_UPDATE_DRAFT,
+ payload: draft
+});
+
+export const createOrUpdateMindr = mindr => ({
+ type: ACTION__MINDR_CREATE_OR_UPDATE,
+ payload: { mindr }
+});
+
+export const openDialog = (dialogId, dialogType, details) => ({
+ type: ACTION__DIALOG_OPEN,
+ payload: { dialogId, dialogType, details }
+});
+
+export const closeDialog = dialogId => ({
+ type: ACTION__DIALOG_CLOSE,
+ payload: { dialogId }
+});
+
+export const removeMindr = guid => ({
+ type: ACTION__MINDR_REMOVE,
+ payload: { guid }
+});
+
+export const removeDraft = (/** @type {MindrDraft} */ draft) => ({
+ type: ACTION__MINDR_REMOVE_DRAFT,
+ payload: { draft }
+});
+
+export const connectionOpened = port => ({
+ type: ACTION__CONNECTION_OPEN,
+ payload: { port }
+});
+
+export const connectionClosed = port => ({
+ type: ACTION__CONNECTION_CLOSE,
+ payload: { port }
+});
+
+export const createTimespanPreset = current => ({
+ type: ACTION__PRESET_TIMESPAN_CREATE,
+ payload: { current }
+});
+
+export const updateTimespanPreset = (current, source) => ({
+ type: ACTION__PRESET_TIMESPAN_UPDATE,
+ payload: { current, source }
+});
+
+export const removeTimespanPreset = presets => ({
+ type: ACTION__PRESET_TIMESPAN_REMOVE,
+ payload: { presets }
+});
+
+export const updateSetting = (name, value) => ({
+ type: ACTION__SETTINGS_UPDATE,
+ payload: { name, value }
+});
diff -Nru mailmindr-1.4.0/modules/store/actions/actionTypes.mjs.js mailmindr-1.7.1/modules/store/actions/actionTypes.mjs.js
--- mailmindr-1.4.0/modules/store/actions/actionTypes.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/actionTypes.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,16 @@
+export const ACTION__HEARTBEAT = 'heartbeat';
+export const ACTION__LOCK_MINDR_FOR_EXECUTION = 'state:lock-execution';
+export const ACTION__UNLOCK_MINDR = 'state:unlock-execution';
+export const ACTION__MINDR_CREATE_OR_UPDATE_DRAFT =
+ 'mindr:create-or-update-draft';
+export const ACTION__MINDR_CREATE_OR_UPDATE = 'mindr:create-or-update';
+export const ACTION__MINDR_REMOVE = 'mindr:remove';
+export const ACTION__MINDR_REMOVE_DRAFT = 'mindr:remove-draft';
+export const ACTION__SETTINGS_UPDATE = 'setting:update';
+export const ACTION__DIALOG_OPEN = 'dialog:open';
+export const ACTION__DIALOG_CLOSE = 'dialog:close';
+export const ACTION__CONNECTION_OPEN = 'connection:open';
+export const ACTION__CONNECTION_CLOSE = 'connection:close';
+export const ACTION__PRESET_TIMESPAN_CREATE = 'preset:timespan-create';
+export const ACTION__PRESET_TIMESPAN_UPDATE = 'preset:timespan-update';
+export const ACTION__PRESET_TIMESPAN_REMOVE = 'preset:timespan-remove';
diff -Nru mailmindr-1.4.0/modules/store/actions/executeMindr.mjs.js mailmindr-1.7.1/modules/store/actions/executeMindr.mjs.js
--- mailmindr-1.4.0/modules/store/actions/executeMindr.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/executeMindr.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,215 @@
+import {
+ genericFolderToLocalFolder,
+ getFlatFolderList
+} from '../../core-utils.mjs.js';
+import { createCorrelationId, createLogger } from '../../logger.mjs.js';
+import {
+ applyActionToMessageInFolder,
+ doMoveMessageToFolder
+} from '../../message-utils.mjs.js';
+import {
+ lockMindrForExecution,
+ unlockMindr,
+ createOrUpdateMindr
+} from './actions.mjs.js';
+
+const logger = createLogger('modules/store/actions/executeMindr');
+
+export const executeMindr = mindr => async (dispatch, getState) => {
+ const { headerMessageId, metaData, action, guid } = mindr;
+ logger.log(`START executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
+ guid,
+ headerMessageId
+ });
+
+ dispatch(lockMindrForExecution(mindr));
+
+ const {
+ author,
+ subject,
+ folderAccountId,
+ folderName,
+ folderPath,
+ folderType,
+ folderAccountIdentityMailAddress
+ } = metaData;
+ const correlationId = createCorrelationId('executeMindr');
+ const executionStart = Date.now();
+ const { copyMessageTo, moveMessageTo } = action;
+
+ const applyAction = async (messageId, action) => {
+ const {
+ flag,
+ markUnread,
+ showReminder,
+ tagWithLabel,
+ copyMessageTo,
+ moveMessageTo
+ } = action;
+ const messageProps = {
+ ...(flag && { flagged: true }),
+ ...(markUnread && { read: false })
+ };
+
+ logger.info(`? apply update to message ${messageId}`, messageProps);
+
+ const timeout = new Promise(resolve => setTimeout(resolve, 1000));
+ const updater = messenger.messages.update(messageId, messageProps);
+
+ await Promise.all([updater, timeout]);
+ };
+
+ const destinationFolder = moveMessageTo
+ ? await genericFolderToLocalFolder(moveMessageTo)
+ : null;
+ const possibleSourceFolder = await genericFolderToLocalFolder({
+ accountId: folderAccountId,
+ path: folderPath,
+ identityEmailAddress: folderAccountIdentityMailAddress
+ });
+ const flatFolderList = await getFlatFolderList();
+ const localFlatFolderList = await Promise.all(
+ flatFolderList
+ .filter(({ type }) => type === 'folder')
+ .map(async ({ folder }) => await genericFolderToLocalFolder(folder))
+ );
+ const folders = possibleSourceFolder
+ ? [
+ possibleSourceFolder,
+ ...localFlatFolderList.filter(
+ fldr =>
+ fldr.accountId !== possibleSourceFolder.accountId &&
+ fldr.path !== possibleSourceFolder.path
+ )
+ ]
+ : localFlatFolderList;
+
+ logger.log(`BEGIN execution of ${mindr.guid}`, {
+ correlationId,
+ guid
+ });
+ const startTime = performance.now();
+
+ const targetFolders = folders;
+
+ let hasError = false;
+ let iterationCount = 0;
+
+ logger.log(`BEGIN targetFolder iteration`, {
+ guid,
+ correlationId,
+ targetFolderCount: (targetFolders || []).length,
+ targetFolders
+ });
+
+ const applyActionToMessage = async (message, messageFolder) => {
+ const { id } = message;
+
+ logger.log(`BEGIN applyActionToMessage`);
+ await applyAction(id, action);
+ logger.log(
+ `Do we have a destination folder? ${
+ destinationFolder ? 'yes' : 'no'
+ }`,
+ destinationFolder
+ );
+ if (destinationFolder) {
+ await doMoveMessageToFolder(
+ message,
+ destinationFolder,
+ correlationId
+ );
+ }
+ logger.log(`END applyActionToMessage`);
+ };
+
+ const applyActionToFirstMessageInFolders = async () => {
+ for await (let folder of targetFolders) {
+ logger.log(
+ ` -- executeMindr: folder loop, apply action to message in folder '(${folder.name})'`,
+ {
+ correlationId,
+ folder,
+ targetFolders
+ }
+ );
+
+ try {
+ const actionResult = await applyActionToMessageInFolder(
+ folder,
+ { headerMessageId, author },
+ applyActionToMessage,
+ true
+ );
+ const { done, value } = await actionResult.next();
+ const success = Boolean(done && value && value.executed);
+ if (success) {
+ return true;
+ }
+ } catch (ex) {
+ logger.error('ERROR: execute mindr // mailmindr: >> !!', {
+ correlationId,
+ guid,
+ exception: ex
+ });
+ hasError = true;
+ }
+ iterationCount++;
+ }
+ return false;
+ };
+
+ logger.log(`BEFORE applying actions`, { correlationId });
+ await applyActionToFirstMessageInFolders();
+ logger.log(`END applying actions`, { correlationId });
+
+ logger.log(`END targetFolder iteration`, {
+ correlationId,
+ guid,
+ targetFolderCount: (targetFolders || []).length,
+ targetFolders
+ });
+
+ const endTime = performance.now();
+ logger.log(
+ `mailmindr: execution finished in ${endTime - startTime}ms`,
+ moveMessageTo
+ );
+
+ const executionEnd = Date.now();
+ const executionDuration = (executionEnd - executionStart) / 1000;
+
+ if (executionDuration > 3 * 60) {
+ logger.error(
+ `Execution of mindr '${guid}' took more than 180 seconds`,
+ { guid, correlationId, executionDuration }
+ );
+ } else if (executionDuration > 60) {
+ logger.warn(`Execution of mindr '${guid}' took more than 60 seconds`, {
+ guid,
+ correlationId,
+ executionDuration
+ });
+ } else {
+ logger.warn(
+ `Execution of mindr '${guid}' took ${executionDuration} seconds`,
+ { guid, correlationId, executionDuration }
+ );
+ }
+ logger.log(`END execution of ${mindr.guid}`, { correlationId, guid });
+ logger.log(`END executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
+ guid,
+ headerMessageId
+ });
+
+ const modifiedMindr = structuredClone(mindr);
+ modifiedMindr.isExecuted = true;
+
+ dispatch(unlockMindr(modifiedMindr));
+
+ if (!hasError) {
+ dispatch(createOrUpdateMindr(modifiedMindr));
+ }
+
+ return !hasError;
+};
diff -Nru mailmindr-1.4.0/modules/store/actions/heartBeat.mjs.js mailmindr-1.7.1/modules/store/actions/heartBeat.mjs.js
--- mailmindr-1.4.0/modules/store/actions/heartBeat.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/heartBeat.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,49 @@
+import { isExecuted, showReminderForMindr } from '../../core-utils.mjs.js';
+import { createLogger } from '../../logger.mjs.js';
+import { executeMindr } from './executeMindr.mjs.js';
+import { showMindrAlert } from './index.mjs.js';
+
+const logger = createLogger('modules/store/actions/heartBeat');
+
+export const heartBeatEx = () => async (dispatch, getState) => {
+ const { overdue, active, __inExecution } = getState();
+ const overdueNotExecuted = overdue.filter(item => !item.isExecuted);
+
+ logger.log(`heartBeatEx -- `, { overdue, active, overdueNotExecuted });
+
+ if (Array.isArray(__inExecution) && __inExecution.length > 0) {
+ logger.warn(
+ `Mindr is executing (${__inExecution.length} in total), skipping further executions`,
+ { overdue, active, __inExecution }
+ );
+ return;
+ }
+
+ logger.log(`overdueNotExecuted: `, overdueNotExecuted);
+ for (let mindr of overdueNotExecuted) {
+ dispatch(executeMindr(mindr));
+ }
+
+ //
+ //
+ const overdueAndUnexecuted = overdue.filter(
+ mindr => !isExecuted(mindr) && showReminderForMindr(mindr)
+ );
+
+ const activeMindrs = active.filter(showReminderForMindr);
+
+ if (overdueAndUnexecuted.length > 0 || activeMindrs.length > 0) {
+ logger.info(`heartbeat: show dialog with mindrs `, {
+ overdueAndUnexecuted,
+ active: activeMindrs
+ });
+ dispatch(
+ showMindrAlert({
+ overdue: overdueAndUnexecuted,
+ active: activeMindrs
+ })
+ );
+ } else {
+ logger.log('No reason to show a dialog', { overdueAndUnexecuted });
+ }
+};
diff -Nru mailmindr-1.4.0/modules/store/actions/index.mjs.js mailmindr-1.7.1/modules/store/actions/index.mjs.js
--- mailmindr-1.4.0/modules/store/actions/index.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/index.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,7 @@
+export { executeMindr } from './executeMindr.mjs.js';
+export { snoozeMindrs } from './snoozeMindrs.mjs.js';
+export { heartBeatEx } from './heartBeat.mjs.js';
+export { showMindrAlert } from './showMindrAlert.mjs.js';
+export { setReplyReceived } from './setReplyReceived.mjs.js';
+
+export * from './actions.mjs.js';
diff -Nru mailmindr-1.4.0/modules/store/actions/setReplyReceived.mjs.js mailmindr-1.7.1/modules/store/actions/setReplyReceived.mjs.js
--- mailmindr-1.4.0/modules/store/actions/setReplyReceived.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/setReplyReceived.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,24 @@
+import { createLogger } from '../../logger.mjs.js';
+import { createOrUpdateMindr } from './index.mjs.js';
+
+const logger = createLogger('actions/setReplyReceived');
+
+export const setReplyReceived = (
+ /** @type {Mindr} */ theMindr,
+ /** @type {string} */ replyHeaderMessageId
+) => async (dispatch, _getState) => {
+ if (theMindr) {
+ const mindr = structuredClone(theMindr); // { ...theMindr };
+ const modifiedMindr = {
+ ...mindr,
+ /** @type {Mindr['metaData']} */ metaData: {
+ ...mindr.metaData,
+ replyHeaderMessageId
+ }
+ };
+
+ await dispatch(createOrUpdateMindr(modifiedMindr));
+ } else {
+ logger.error(`mindr is not defined: ${theMindr}`);
+ }
+};
diff -Nru mailmindr-1.4.0/modules/store/actions/showMindrAlert.mjs.js mailmindr-1.7.1/modules/store/actions/showMindrAlert.mjs.js
--- mailmindr-1.4.0/modules/store/actions/showMindrAlert.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/showMindrAlert.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,105 @@
+import {
+ createMailmindrId,
+ sendConnectionMessageEx
+} from '../../core-utils.mjs.js';
+import { createLogger } from '../../logger.mjs.js';
+import {
+ selectDialogForType,
+ selectOpenConnections
+} from '../selectors/index.mjs.js';
+import { closeDialog, openDialog } from './actions.mjs.js';
+
+const logger = createLogger('modules/store/actions/showMindrAlert');
+
+export const showMindrAlert = ({ overdue, active }) => async (
+ dispatch,
+ getState
+) => {
+ const dialogType = 'mailmindr:mindr-alert';
+ const state = getState();
+ const openConnections = selectOpenConnections(state);
+
+ const dialogs = (await messenger.tabs.query({})).filter(tab => {
+ if (tab && tab.url) {
+ return tab.url.indexOf('/mindr-alert/') > 0;
+ }
+ return false;
+ });
+ const alertDialog = dialogs && dialogs.length && dialogs[0];
+ const dialog = alertDialog
+ ? { details: { id: alertDialog.windowId, tabId: alertDialog.id } }
+ : null;
+
+ if (dialog) {
+ logger.log('we already have a message dialog', dialog);
+
+ if ((overdue || []).length === 0 && (active || []).length === 0) {
+ logger.log(`no overdue or active mindrs ? we can close the dialog`);
+
+ //
+ dispatch(closeDialog(dialog.details.id));
+ messenger.windows.remove(dialog.details.id);
+ } else {
+ logger.log(`we have a dialog and send data to it`, {
+ overdue,
+ active
+ });
+
+ //
+ await sendConnectionMessageEx(
+ openConnections,
+ {
+ overdue,
+ active
+ },
+ 'connection:mindr-alert'
+ );
+ await messenger.windows.update(dialog.details.id, {
+ focused: false,
+ drawAttention: true
+ });
+ }
+ } else {
+ logger.log(
+ 'need to open a new dialog with active/overdue mindrs',
+ active,
+ overdue
+ );
+ if ((active || []).length === 0 && (overdue || []).length === 0) {
+ logger.log('no dialog needed');
+ return;
+ }
+ const dialogId = createMailmindrId('mailmindr:dialog:mindr-alert');
+
+ const parameters = new URLSearchParams();
+ parameters.set('dialogId', dialogId);
+
+ const { width: screenWidth, availHeight: screenHeight } = screen;
+ const height = 200;
+ const width = 400;
+ const left = screenWidth - width;
+ const top = screenHeight - height;
+ const url = `/views/dialogs/mindr-alert/index.html?${parameters}`;
+ const details = await messenger.windows.create({
+ left,
+ top,
+ height,
+ width,
+ url,
+ type: 'popup',
+ state: 'normal',
+ allowScriptsToClose: true
+ });
+
+ await messenger.windows.update(details.id, {
+ top,
+ left,
+ width,
+ height,
+ focused: true,
+ drawAttention: true
+ });
+
+ dispatch(openDialog(dialogId, dialogType, details));
+ }
+};
diff -Nru mailmindr-1.4.0/modules/store/actions/snoozeMindrs.mjs.js mailmindr-1.7.1/modules/store/actions/snoozeMindrs.mjs.js
--- mailmindr-1.4.0/modules/store/actions/snoozeMindrs.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/actions/snoozeMindrs.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,29 @@
+import { snoozeMindr } from '../../core-utils.mjs.js';
+import { createLogger } from '../../logger.mjs.js';
+import { createOrUpdateMindr } from './index.mjs.js';
+
+const logger = createLogger('modules/store/actions/snoozeMindrs');
+
+export const snoozeMindrs = (
+ mindrGuidList,
+ mindrs,
+ snoozeTimeMinutes,
+ correlationId
+) => async (dispatch, getState) => {
+ do {
+ const guid = mindrGuidList.pop();
+ const theMindr = mindrs.find(mindr => mindr.guid === guid);
+
+ if (theMindr) {
+ const mindr = structuredClone(theMindr); // { ...theMindr };
+ const modifiedMindr = snoozeMindr(mindr, {
+ snoozeTimeMinutes,
+ correlationId
+ });
+
+ await dispatch(createOrUpdateMindr(modifiedMindr));
+ } else {
+ logger.log(`ugh: ${theMindr}`);
+ }
+ } while (mindrGuidList.length);
+};
diff -Nru mailmindr-1.4.0/modules/store/reducers/createOrUpdateDraft.mjs.js mailmindr-1.7.1/modules/store/reducers/createOrUpdateDraft.mjs.js
--- mailmindr-1.4.0/modules/store/reducers/createOrUpdateDraft.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/reducers/createOrUpdateDraft.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,33 @@
+import { createLogger } from '../../logger.mjs.js';
+import {
+ ACTION__MINDR_CREATE_OR_UPDATE,
+ ACTION__MINDR_CREATE_OR_UPDATE_DRAFT
+} from '../actions/actionTypes.mjs.js';
+
+const logger = createLogger('reducers/createOrUpdateDraft');
+
+export const createOrUpdateDraftReducer = (
+ /** @type {MailmindrState} */ state,
+ action
+) => {
+ const { type, payload } = action;
+ if (type !== ACTION__MINDR_CREATE_OR_UPDATE_DRAFT) {
+ return state;
+ }
+
+ const { mindr, sender } = payload;
+ const { __drafts: drafts, ...stateWithoutMindrs } = state;
+ const mindrsWithoutUpdatedMindr = (drafts || []).filter(
+ item =>
+ item.sender.id !== sender.id &&
+ item.sender.windowId !== sender.windowId
+ );
+ const updatedDrafts = [...mindrsWithoutUpdatedMindr, payload];
+
+ const localState = {
+ ...stateWithoutMindrs,
+ __drafts: updatedDrafts
+ };
+
+ return localState;
+};
diff -Nru mailmindr-1.4.0/modules/store/reducers/createOrUpdateMindr.mjs.js mailmindr-1.7.1/modules/store/reducers/createOrUpdateMindr.mjs.js
--- mailmindr-1.4.0/modules/store/reducers/createOrUpdateMindr.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/reducers/createOrUpdateMindr.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,26 @@
+import { isSameMindr } from '../../core-utils.mjs.js';
+import { createLogger } from '../../logger.mjs.js';
+import { ACTION__MINDR_CREATE_OR_UPDATE } from '../actions/actionTypes.mjs.js';
+
+const logger = createLogger('modules/store/reducers/createOrUpdateMindr');
+
+export const createOrUpdateMindrReducer = (state, action) => {
+ const { type, payload } = action;
+ if (type !== ACTION__MINDR_CREATE_OR_UPDATE) {
+ return state;
+ }
+
+ const { mindr } = payload;
+ const { mindrs: allMindrs, ...stateWithoutMindrs } = state;
+ const mindrsWithoutUpdatedMindr = allMindrs.filter(
+ item => !isSameMindr(item, mindr)
+ );
+ const mindrs = [...mindrsWithoutUpdatedMindr, mindr];
+
+ const localState = {
+ ...stateWithoutMindrs,
+ mindrs
+ };
+
+ return localState;
+};
diff -Nru mailmindr-1.4.0/modules/store/reducers/index.mjs.js mailmindr-1.7.1/modules/store/reducers/index.mjs.js
--- mailmindr-1.4.0/modules/store/reducers/index.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/reducers/index.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,216 @@
+import {
+ equalTimePresetValues,
+ getActiveAndOverdueMindrs
+} from '../../core-utils.mjs.js';
+import { createLogger } from '../../logger.mjs.js';
+import {
+ ACTION__CONNECTION_CLOSE,
+ ACTION__CONNECTION_OPEN,
+ ACTION__DIALOG_CLOSE,
+ ACTION__DIALOG_OPEN,
+ ACTION__HEARTBEAT,
+ ACTION__LOCK_MINDR_FOR_EXECUTION,
+ ACTION__MINDR_CREATE_OR_UPDATE,
+ ACTION__MINDR_CREATE_OR_UPDATE_DRAFT,
+ ACTION__MINDR_REMOVE,
+ ACTION__MINDR_REMOVE_DRAFT,
+ ACTION__PRESET_TIMESPAN_CREATE,
+ ACTION__PRESET_TIMESPAN_REMOVE,
+ ACTION__PRESET_TIMESPAN_UPDATE,
+ ACTION__SETTINGS_UPDATE,
+ ACTION__UNLOCK_MINDR
+} from '../actions/actionTypes.mjs.js';
+import { selectOpenConnections } from '../selectors/index.mjs.js';
+import { createOrUpdateDraftReducer } from './createOrUpdateDraft.mjs.js';
+import { createOrUpdateMindrReducer } from './createOrUpdateMindr.mjs.js';
+import { removeMindrReducer } from './removeMindr.mjs.js';
+
+const logger = createLogger('modules/store/reducers/root');
+
+const rootReducer = (/** @type {MailmindrState} */ state, action) => {
+ const { type, payload } = action;
+ switch (type) {
+ case ACTION__HEARTBEAT:
+ const { mindrs: mindrList } = state;
+
+ const { mindrs, overdue, active } = getActiveAndOverdueMindrs(
+ mindrList
+ );
+
+ //
+ //
+ //
+
+ return { ...state, mindrs, active, overdue };
+ case ACTION__CONNECTION_OPEN: {
+ const { port } = payload;
+ const openConnections = [...selectOpenConnections(state), port];
+
+ logger.log(
+ `open connections: ${openConnections.length}`,
+ openConnections
+ );
+
+ return { ...state, openConnections };
+ }
+ case ACTION__CONNECTION_CLOSE: {
+ const { port } = payload;
+ const { name } = port;
+ const { openConnections: connections } = state;
+
+ const openConnections = connections.filter(
+ connection => connection.name !== name
+ );
+
+ return { ...state, openConnections };
+ }
+ case ACTION__MINDR_CREATE_OR_UPDATE_DRAFT:
+ return createOrUpdateDraftReducer(state, action);
+ case ACTION__MINDR_CREATE_OR_UPDATE:
+ return createOrUpdateMindrReducer(state, action);
+ case ACTION__MINDR_REMOVE:
+ return removeMindrReducer(state, action);
+ case ACTION__MINDR_REMOVE_DRAFT: {
+ const { /** @type {MindrDraft}*/ draft } = payload;
+ const localState = {
+ ...state,
+ __drafts: state.__drafts.filter(
+ item =>
+ item.sender.id !== draft.sender.id &&
+ item.sender.windowId !== draft.sender.windowId
+ )
+ };
+ return localState;
+ }
+ case ACTION__LOCK_MINDR_FOR_EXECUTION: {
+ //
+ //
+ const { guid } = payload;
+
+ const localState = {
+ ...state,
+ __inExecution: [...state.__inExecution, guid]
+ };
+
+ return localState;
+ }
+ case ACTION__UNLOCK_MINDR: {
+ //
+ const { guid } = payload;
+
+ const localState = {
+ ...state,
+ __inExecution: state.__inExecution.filter(item => item !== guid)
+ };
+
+ return localState;
+ }
+ case ACTION__SETTINGS_UPDATE: {
+ const { name, value } = payload;
+
+ const localState = {
+ ...state,
+ settings: { ...state.settings, [name]: value }
+ };
+
+ return localState;
+ }
+ case ACTION__PRESET_TIMESPAN_CREATE: {
+ const { presets } = state;
+ const { time } = presets;
+ const { current } = payload;
+
+ const localState = {
+ ...state,
+ presets: {
+ ...presets,
+ time: [...time, current]
+ }
+ };
+
+ return localState;
+ }
+ case ACTION__PRESET_TIMESPAN_UPDATE: {
+ const { presets } = state;
+ const { time: timePresets } = presets;
+ const { current, source } = payload;
+
+ const time = timePresets.map(item =>
+ equalTimePresetValues(item, source) ? current : item
+ );
+
+ logger.info(`new presets:`, time);
+
+ const newState = {
+ ...state,
+ presets: {
+ ...presets,
+ time
+ }
+ };
+
+ return newState;
+ }
+ case ACTION__PRESET_TIMESPAN_REMOVE: {
+ const { presets } = state;
+ const { time: timePresets } = presets;
+ const { presets: toBeRemoved = [] } = payload;
+
+ let time = [...timePresets];
+ toBeRemoved.forEach(toBeRemovedPreset => {
+ time = time.filter(
+ preset => !equalTimePresetValues(preset, toBeRemovedPreset)
+ );
+ });
+
+ const newState = {
+ ...state,
+ presets: {
+ ...presets,
+ time
+ }
+ };
+
+ return newState;
+ }
+ case ACTION__DIALOG_OPEN: {
+ const newDialogDetails = payload;
+ const openDialogs = [...state.openDialogs, newDialogDetails];
+
+ return { ...state, openDialogs };
+ }
+ case ACTION__DIALOG_CLOSE: {
+ const { dialogId } = payload;
+ const { openDialogs: dialogs } = state;
+ const dialogDetails = dialogs.find(
+ dialogInfo => dialogId === dialogInfo.dialogId
+ );
+
+ if (dialogDetails) {
+ const openDialogs = dialogs.filter(
+ openDialog => openDialog.dialogId !== dialogId
+ );
+ logger.info(`remaining open dialogs: ${openDialogs.length}`);
+ return {
+ ...state,
+ openDialogs
+ };
+ } else {
+ logger.warn(
+ `cannot find details for open dialog ID: '${dialogId}'`,
+ {
+ payload,
+ dialogId,
+ openDialogs: dialogs
+ }
+ );
+ }
+
+ return state;
+ }
+ default:
+ return state;
+ }
+};
+
+export default rootReducer;
diff -Nru mailmindr-1.4.0/modules/store/reducers/removeMindr.mjs.js mailmindr-1.7.1/modules/store/reducers/removeMindr.mjs.js
--- mailmindr-1.4.0/modules/store/reducers/removeMindr.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/reducers/removeMindr.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,52 @@
+import { createLogger } from '../../logger.mjs.js';
+import { ACTION__MINDR_REMOVE } from '../actions/actionTypes.mjs.js';
+
+const logger = createLogger('modules/store/reducers/removeMindr');
+
+export const removeMindrReducer = (state, action) => {
+ const { type, payload } = action;
+ if (type !== ACTION__MINDR_REMOVE) {
+ return state;
+ }
+
+ const { guid } = payload;
+ const {
+ mindrs: stateMindrs,
+ overdue: stateOverdue,
+ active: stateActive
+ } = state;
+
+ const mindrCount = {
+ mindrs: (stateMindrs || []).length,
+ overdue: (stateOverdue || []).length,
+ active: (stateActive || []).length
+ };
+
+ logger.log(`mindr to be removed: ${guid}`, { guid, mindrCount });
+
+ //
+ const mindrs = stateMindrs.filter(item => item.guid !== guid);
+
+ const overdue = (stateOverdue || []).filter(mindr => mindr.guid !== guid);
+ const active = (stateActive || []).filter(mindr => mindr.guid !== guid);
+
+ if (
+ mindrCount.overdue === overdue.length &&
+ mindrCount.active === active.length &&
+ mindrCount.mindrs === mindrs.length
+ ) {
+ //
+ logger.log(`no mindr was removed, state remains untouched`);
+
+ return state;
+ }
+
+ const localState = {
+ ...state,
+ mindrs,
+ active,
+ overdue
+ };
+
+ return localState;
+};
diff -Nru mailmindr-1.4.0/modules/store/selectors/index.mjs.js mailmindr-1.7.1/modules/store/selectors/index.mjs.js
--- mailmindr-1.4.0/modules/store/selectors/index.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/selectors/index.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,68 @@
+import { sanitizeHeaderMessageId } from '../../core-utils.mjs.js';
+import { createLogger } from '../../logger.mjs.js';
+
+const logger = createLogger('modules/store/selectors');
+
+export const selectMindrs = (/** @type {MailmindrState} */ state) =>
+ state.mindrs || [];
+
+export const selectMindrByGuid = (
+ /** @type {MailmindrState} */ state,
+ guid
+) => {
+ const mindrs = selectMindrs(state);
+ const result = (mindrs || []).find(item => item.guid === guid);
+
+ return result;
+};
+
+export const selectMindrByHeaderMessageId = (
+ /** @type {MailmindrState} */ state,
+ headerMessageId
+) => {
+ const mindrs = selectMindrs(state);
+ const result = (mindrs || []).find(
+ item =>
+ sanitizeHeaderMessageId(item.headerMessageId) ===
+ sanitizeHeaderMessageId(headerMessageId)
+ );
+
+ return result;
+};
+
+export const selectOpenDialogs = (/** @type {MailmindrState} */ state) =>
+ state.openDialogs || [];
+
+export const selectDialogForType = (
+ /** @type {MailmindrState} */ state,
+ dialogType
+) => {
+ const openDialogs = selectOpenDialogs(state);
+ logger.log(selectDialogForType.name, { state, openDialogs });
+ return openDialogs.find(dialog => dialog.dialogType === dialogType);
+};
+
+export const selectSettings = (/** @type {MailmindrState} */ state) =>
+ state.settings;
+
+export const selectPresets = (/** @type {MailmindrState} */ state) =>
+ state.presets;
+
+export const selectOpenConnections = (/** @type {MailmindrState} */ state) =>
+ state.openConnections || [];
+
+export const selectDrafts = (/** @type {MailmindrState} */ state) => {
+ return state.__drafts || [];
+};
+
+export const selectDraftForSenderTabOrNull = (
+ /** @type {MailmindrState} */ state,
+ /** @type {MindrDraft['sender']} */ sender
+) => {
+ const drafts = selectDrafts(state);
+ const result = drafts.find(
+ ({ sender: { id, windowId } }) =>
+ id === sender.id && windowId === sender.windowId
+ );
+ return result || null;
+};
diff -Nru mailmindr-1.4.0/modules/store/state-manager.mjs.js mailmindr-1.7.1/modules/store/state-manager.mjs.js
--- mailmindr-1.4.0/modules/store/state-manager.mjs.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/modules/store/state-manager.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,142 @@
+import { createLogger } from '../logger.mjs.js';
+
+const logger = createLogger('modules/store/state-manager');
+
+export const createStore = (reducer, initialState) => {
+ logger.log(`Initializing state w/ `, { initialState });
+ let localState = initialState;
+ let dispatching = false;
+ let isCorrupt = false;
+ let actions = [];
+ const handlers = {
+ change: new Set()
+ };
+
+ const defaultGetState = function() {
+ if (dispatching) {
+ throw new MailmindrStateError(
+ 'Cannot get state while in dispatching mode'
+ );
+ }
+ return localState;
+ };
+
+ const defaultDispatch = function(action) {
+ if (dispatching) {
+ throw new MailmindrStateError(
+ 'Cannot update state while state is in dispatching mode'
+ );
+ }
+ try {
+ dispatching = true;
+ logger.log(`? start reduce '${action.type}'`, {
+ localState,
+ action
+ });
+ actions.push({ name: action.type });
+ localState = reducer(localState, action);
+ if (!localState) {
+ logger.error(`Action corrupted the state: ${action.type}`);
+ }
+ if (handlers.change.size) {
+ for (let handler of handlers.change.values()) {
+ setTimeout(() => handler(localState), 0);
+ }
+ }
+ logger.log(`? end reduce '${action.type}'`, { localState });
+ dispatching = false;
+ } catch (error) {
+ dispatching = false;
+ isCorrupt = true;
+ logger.error(
+ `Something went wrong during dispatching the action '${action.type}'`,
+ {
+ action,
+ actions,
+ error
+ }
+ );
+ }
+
+ return action;
+ };
+
+ const extendedDispatch = function(action) {
+ if (action && action.constructor && action.constructor.name) {
+ const constructorName = action.constructor.name.toLocaleLowerCase();
+ switch (constructorName) {
+ case 'promise':
+ logger.warn('Promise as action?', action);
+ return action;
+ case 'asyncfunction':
+ return new Promise(async (success, failure) => {
+ try {
+ const result = await action(
+ extendedDispatch,
+ defaultGetState
+ );
+ actions.push({
+ name: `[async] ${action.name}`
+ });
+ success(result);
+ } catch (asyncFunctionError) {
+ failure(asyncFunctionError);
+ }
+ });
+ case 'function':
+ try {
+ actions.push({ name: `[func] ${action.name}` });
+ return action(extendedDispatch, defaultGetState);
+ } catch (functionError) {
+ throw new MailmindrStateError(
+ `Error in function ${action.name}`,
+ functionError
+ );
+ }
+ default:
+ return defaultDispatch(action);
+ }
+ }
+ };
+
+ let def = {
+ dispatch: extendedDispatch,
+ getState: defaultGetState,
+ addEventListener: (eventName, eventHandler) => {
+ const eventNameNormalized = eventName.toLocaleLowerCase();
+ if (Object.keys(handlers).includes(eventNameNormalized)) {
+ if (handlers[eventNameNormalized].has(eventHandler)) {
+ logger.warn(`Handler for ${eventName} already defined.`);
+ } else {
+ handlers[eventNameNormalized].add(eventHandler);
+ }
+ } else {
+ logger.error(
+ `No event handler for event '${eventNameNormalized}' exist.`
+ );
+ }
+ },
+ removeEventListener: (eventName, eventHandler) => {
+ const eventNameNormalized = eventName.toLocaleLowerCase();
+ if (Object.keys(handlers).includes(eventNameNormalized)) {
+ if (handlers[eventNameNormalized].has(eventHandler)) {
+ handlers[eventNameNormalized].delete(eventHandler);
+ } else {
+ logger.warn(`Handler for ${eventName} is not registered.`);
+ }
+ } else {
+ logger.error(
+ `No event handler for event '${eventNameNormalized}' exist.`
+ );
+ }
+ }
+ };
+
+ return def;
+};
+
+export class MailmindrStateError extends Error {
+ constructor(...args) {
+ super(...args);
+ }
+}
diff -Nru mailmindr-1.4.0/modules/string-utils.mjs.js mailmindr-1.7.1/modules/string-utils.mjs.js
--- mailmindr-1.4.0/modules/string-utils.mjs.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/modules/string-utils.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -1,6 +1,6 @@
import { createLogger } from './logger.mjs.js';
-const logger = createLogger('string-utils');
+const logger = createLogger('modules/string-utils');
const simplePluralize = (num, identifier) => {
const pluralizer = new Intl.PluralRules(navigator.language, {
diff -Nru mailmindr-1.4.0/modules/ui-utils.mjs.js mailmindr-1.7.1/modules/ui-utils.mjs.js
--- mailmindr-1.4.0/modules/ui-utils.mjs.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/modules/ui-utils.mjs.js 2024-08-04 22:14:20.000000000 +0200
@@ -3,8 +3,9 @@
equalTimePresetValues
} from './core-utils.mjs.js';
import { createLogger } from './logger.mjs.js';
+import { pluralize } from './string-utils.mjs.js';
-const logger = createLogger('ui-utils');
+const logger = createLogger('modules/ui-utils');
export const appendI18n = element => {
if (!element) {
@@ -61,19 +62,135 @@
};
export const selectDefaultActionPreset = (element, defaultActionPreset) => {
- const availablePresets = Array.from(element.options)
- .map(item => ({
- index: item.index,
- actionPreset: JSON.parse(item.value)
- }))
+ const presetsFromOptions = Array.from(element.options).map(item => ({
+ index: item.index,
+ actionPreset: JSON.parse(item.value)
+ }));
+ const availablePresets = presetsFromOptions
.filter(({ actionPreset }) =>
- equalActionPresetValues(actionPreset, defaultActionPreset)
+ //
+ areActionsEqual(actionPreset, defaultActionPreset, true)
)
.map(({ index }) => index);
const selectedIndex = availablePresets.shift() || 0;
+
element.selectedIndex = selectedIndex;
};
+export const areActionsEqual = (
+ someAction,
+ someOtherAction,
+ ignoreShowReminder = false
+) => {
+ if (!someAction || !someOtherAction) {
+ return false;
+ }
+
+ const props = [
+ 'flag',
+ 'markUnread',
+ 'tagWithLabel',
+ 'copyMessageTo',
+ 'moveMessageTo',
+ ignoreShowReminder ? void 0 : 'showReminder'
+ ];
+
+ let result = true;
+ props.forEach(prop => {
+ let equal = someAction[prop] === someOtherAction[prop];
+ if (!equal) {
+ logger.info(
+ `prop '${prop}' failed: '${someAction[prop]}' !== '${someOtherAction[prop]}'`
+ );
+ result = result && false;
+ }
+ });
+
+ logger.info(`--- checks: ${result}`);
+ return result;
+};
+
+/**
+ * Selects the index of an action preset from a list of action presets
+ * @param {Array<{ readonly index: number; readonly actionPreset: MailmindrAction }>} list
+ * @param {MailmindrAction} defaultActionPreset
+ */
+export const selectDefaultActionPresetIndexFromList = (
+ list,
+ defaultActionPreset
+) => {
+ const availablePresets = list
+ .filter(({ actionPreset }) =>
+ equalActionPresetValues(actionPreset, defaultActionPreset)
+ )
+ .map(({ index }) => index);
+ const selectedIndex = availablePresets.shift() || 0;
+
+ return selectedIndex;
+};
+
+export const selectDefaultRemindeMeMinutesBeforePreset = (
+ element,
+ presets,
+ selectedRemindMeBeforeValue
+) => {
+ const remindMeMinutesBefore = presets;
+ if (selectedRemindMeBeforeValue !== null) {
+ const selectedIndex = remindMeMinutesBefore.findIndex(
+ item =>
+ parseInt(item.minutes, 10) ===
+ parseInt(selectedRemindMeBeforeValue, 10)
+ );
+ if (selectedIndex >= 0) {
+ element.selectedIndex = selectedIndex;
+ }
+ }
+};
+
+export const createRemindMeBeforePicker = (
+ document,
+ element,
+ presets,
+ selectedRemindMeBeforeValue = null
+) => {
+ const remindMeMinutesBefore = presets;
+ remindMeMinutesBefore.forEach(item => {
+ const option = document.createElement('option');
+ const { minutes, unit, display: displayedValue } = item;
+
+ option.value = String(minutes);
+
+ if (unit === 'on-time') {
+ option.innerText = pluralize(
+ displayedValue,
+ 'mailmindr.utils.core.remindme.before.on-time'
+ );
+ } else if (unit === 'minutes') {
+ option.innerText = pluralize(
+ displayedValue,
+ 'mailmindr.utils.core.remindme.before.minutes'
+ );
+ } else if (unit === 'hours') {
+ option.innerText = pluralize(
+ displayedValue,
+ 'mailmindr.utils.core.remindme.before.hours'
+ );
+ } else if (unit === 'no-reminder') {
+ option.innerText = pluralize(
+ displayedValue,
+ 'mailmindr.utils.core.remindme.before.no-reminder'
+ );
+ }
+ element.appendChild(option);
+ });
+
+ selectDefaultRemindeMeMinutesBeforePreset(
+ element,
+ presets,
+ selectedRemindMeBeforeValue
+ );
+};
+
//
//
//
@@ -114,3 +231,8 @@
export const clearContents = parentElement => {
Array.from(parentElement.children).forEach(child => child.remove());
};
+
+export const isDarkMode = () => {
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+ return mediaQuery.matches;
+};
diff -Nru mailmindr-1.4.0/schema.json mailmindr-1.7.1/schema.json
--- mailmindr-1.4.0/schema.json 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/schema.json 1970-01-01 01:00:00.000000000 +0100
@@ -1,20 +0,0 @@
-[
- {
- "namespace": "mailmindrMessagesApi",
- "functions": [
- {
- "name": "openMessageByMessageHeaderId",
- "type": "function",
- "description": "Open message by given messageHeaderId.",
- "async": true,
- "parameters": [
- {
- "name": "messageHeaderId",
- "type": "string",
- "description": "headerMessageId of the message that should be opened."
- }
- ]
- }
- ]
- }
-]
diff -Nru mailmindr-1.4.0/scripts/mailmindr-background.js mailmindr-1.7.1/scripts/mailmindr-background.js
--- mailmindr-1.4.0/scripts/mailmindr-background.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/scripts/mailmindr-background.js 2024-08-04 22:14:20.000000000 +0200
@@ -2,70 +2,66 @@
/// <reference types="../../.typings/browser" />
/// <reference types="../../.typings/mailmindr" />
import {
- createMindrFromActionTemplate,
createMailmindrId,
+ createMindrFromActionTemplate,
+ createRemindMeMinutesBefore,
createSystemActions,
createSystemTimespans,
- localFolderToGenericFolder,
- isSameMindr,
- equalTimePresetValues,
+ findThunderbirdVersion,
genericFolderToLocalFolder,
- getFlatFolderList,
- isExecuted,
- getActiveAndOverdueMindrs,
- snoozeMindrs,
- getGenericIceboxFolderForIdentityOrNull
+ getGenericIceboxFolderForIdentityOrNull,
+ localFolderToGenericFolder,
+ sanitizeHeaderMessageId,
+ sendConnectionMessageEx,
+ showReminderForMindr,
+ throttle
} from '../modules/core-utils.mjs.js';
import {
getCurrentStorageAdapterVersion,
getStorageAdapter
} from '../modules/storage.mjs.js';
import { createCorrelationId, createLogger } from '../modules/logger.mjs.js';
-import { applyActionToMessageInFolder } from '../modules/message-utils.mjs.js';
+import { createStore } from '../modules/store/state-manager.mjs.js';
+import { initialState } from '../modules/defaults.mjs.js';
+import {
+ closeDialog,
+ connectionClosed,
+ connectionOpened,
+ createOrUpdateDraft,
+ createOrUpdateMindr,
+ createTimespanPreset,
+ heartBeat,
+ heartBeatEx,
+ openDialog,
+ removeDraft,
+ removeMindr,
+ removeTimespanPreset,
+ setReplyReceived,
+ showMindrAlert,
+ snoozeMindrs,
+ updateSetting,
+ updateTimespanPreset
+} from '../modules/store/actions/index.mjs.js';
+import {
+ selectDialogForType,
+ selectDraftForSenderTabOrNull,
+ selectMindrByGuid,
+ selectMindrByHeaderMessageId,
+ selectMindrs,
+ selectOpenConnections,
+ selectOpenDialogs,
+ selectPresets,
+ selectSettings
+} from '../modules/store/selectors/index.mjs.js';
+import {
+ getMessages,
+ moveMessageToFolder
+} from '../modules/message-utils.mjs.js';
+import rootReducer from '../modules/store/reducers/index.mjs.js';
const logger = createLogger('background');
-/** @type {MailmindrState} */
-let state = {
- presets: {
- actions: [],
- time: []
- },
- settings: {
- defaultActionPreset: {
- copyMessageTo: null,
- moveMessageTo: null,
- text: null,
- tagWithLabel: null,
- flag: null,
- isSystemAction: false,
- markUnread: false,
- showReminder: false
- },
- defaultIceboxFolder: '',
- defaultTimepreset: {
- days: 0,
- hours: 0,
- minutes: 0,
- isGenerated: true,
- isRelative: false,
- isSelectable: false,
- text: null
- },
- iceboxFolders: [],
- snoozeTime: 15
- },
- mindrs: [],
- active: [],
- overdue: [],
- openDialogs: [],
- openConnections: [],
- __inExecution: []
-};
-
-const getState = () => {
- return state;
-};
+let store; // = createStore(() => {}, initialState);
const createInitialSettings = (presets, _settings) => ({
snoozeTime: 15, // 15 minutes is initial default,
@@ -74,23 +70,36 @@
defaultTimePreset:
presets.time?.[presets.time?.find(item => item.isSelectable) || 0], // get first preset (if any)
defaultActionPreset:
- presets.actions?.[presets.actions?.find(item => item.isSelectable) || 0] // get first preset (if any)
+ presets.actions?.[
+ presets.actions?.find(item => item.isSelectable) || 0
+ ], // get first preset (if any)
+ defaultRemindMeMinutesBefore: 15
});
const createSystemPresets = () => ({
time: createSystemTimespans(),
- actions: createSystemActions()
+ actions: createSystemActions(),
+ remindMeMinutesBefore: createRemindMeMinutesBefore()
});
+const getStorage = async () => {
+ //
+ const storage = await browser.storage.local.get(null);
+ const storageVersion = storage?.storageVersion || 1;
+
+ //
+ const { loadState, storeState } = getStorageAdapter(storageVersion);
+ return { loadState, storeState, storageVersion };
+};
+
const initializeStorage = async () => {
try {
- //
- const storage = await browser.storage.local.get(null);
- const persistedStorageVersion = storage?.storageVersion || 1;
-
- //
+ const {
+ loadState,
+ storeState,
+ storageVersion: persistedStorageVersion
+ } = await getStorage();
const storageVersion = getCurrentStorageAdapterVersion();
- const { loadState, storeState } = getStorageAdapter(storageVersion);
//
if (storageVersion > persistedStorageVersion) {
@@ -114,7 +123,8 @@
//
const {
time: systemGeneratedTimePresets,
- actions
+ actions,
+ remindMeMinutesBefore
} = createSystemPresets();
const defaultSettings = createInitialSettings({
systemGeneratedTimePresets,
@@ -142,7 +152,8 @@
settings,
presets: {
time,
- actions
+ actions,
+ remindMeMinutesBefore
},
overdue: [], // can be computed
active: [], // can be computed
@@ -163,66 +174,13 @@
}
};
-const getSettings = () => {
- const { presets, settings } = getState();
- return { presets, settings };
-};
-
-const onHeartBeat = async () => {
- const windows = await messenger.windows.getAll({
- windowTypes: ['normal', 'app']
- });
- if (!windows.length) {
- return;
- }
-
- await dispatch('heartbeat');
-
- const executeOverdueMindrs = async () => {
- const { overdue, active, __inExecution } = getState();
- const overdueNotExecuted = overdue.filter(item => !item.isExecuted);
-
- if (Array.isArray(__inExecution) && __inExecution.length > 0) {
- logger.warn(
- `Mindr is executing (${__inExecution.length} in total), skipping further executions`,
- { overdue, active, __inExecution }
- );
- return;
- }
-
- for (let mindr of overdueNotExecuted) {
- const { guid } = mindr;
- await dispatch('state:lock-execution', { guid });
- const executed = await executeMindr(mindr);
- await dispatch('state:unlock-execution', { guid });
- if (executed) {
- await dispatch('mindr:create-or-update', {
- ...mindr,
- isExecuted: true
- });
- }
- }
- };
-
- const showAlertDialog = async () => {
- const { overdue, active } = getState();
- const overdueAndUnexecuted = overdue.filter(
- mindr =>
- !isExecuted(mindr) &&
- String(mindr.remindMeMinutesBefore) !== '-1'
- );
-
- if (overdueAndUnexecuted.length > 0 || active.length > 0) {
- await showMindrAlert({ overdue: overdueAndUnexecuted, active });
- }
- };
-
- await executeOverdueMindrs();
- await showAlertDialog();
+const onHeartBeatHandler = async () => {
+ store.dispatch(heartBeat());
+ store.dispatch(heartBeatEx());
};
const startHeartbeat = () => {
- setInterval(onHeartBeat, 1000 * 45);
+ setInterval(onHeartBeatHandler, 1000 * 45);
};
const setMindrForCurrentMessage = async optionalCurrentMessage => {
@@ -247,7 +205,7 @@
await showCreateOrUpdateMindrDialog(currentMessage, mindr);
}
} else {
- console.warn('?? The current message cannot be determined.');
+ logger.warn('?? The current message cannot be determined.');
}
};
@@ -255,523 +213,17 @@
/* intentionally left blank */
};
-const createOrUpdateMindr = async (state, mindr) => {
- const { mindrs: allMindrs, ...stateWithoutMindrs } = state;
- const mindrsWithoutUpdatedMindr = allMindrs.filter(
- item => !isSameMindr(item, mindr)
- );
- const mindrs = [...mindrsWithoutUpdatedMindr, mindr];
-
- const storageVersion = getCurrentStorageAdapterVersion();
- const { storeState } = getStorageAdapter(storageVersion);
-
- const localState = {
- ...stateWithoutMindrs,
- mindrs
- };
-
- await storeState(localState);
-
- return localState;
-};
-
-const executeMindr = async mindr => {
- const { headerMessageId, metaData, action, guid } = mindr;
- logger.log(`START executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
- guid,
- headerMessageId
- });
- const {
- author,
- subject,
- folderAccountId,
- folderName,
- folderPath,
- folderType,
- folderAccountIdentityMailAddress
- } = metaData;
- const correlationId = createCorrelationId('executeMindr');
- const executionStart = Date.now();
- const { copyMessageTo, moveMessageTo } = action;
-
- const applyAction = async (messageId, action) => {
- const {
- flag,
- markUnread,
- showReminder,
- tagWithLabel,
- copyMessageTo,
- moveMessageTo
- } = action;
- const messageProps = {
- ...(flag && { flagged: true }),
- ...(markUnread && { read: false })
- };
-
- logger.info(`? apply update to message ${messageId}`, messageProps);
-
- await messenger.messages.update(messageId, messageProps);
- };
-
- const destinationFolder = moveMessageTo
- ? await genericFolderToLocalFolder(moveMessageTo)
- : null;
- const possibleSourceFolder = await genericFolderToLocalFolder({
- accountId: folderAccountId,
- path: folderPath,
- identityEmailAddress: folderAccountIdentityMailAddress
- });
- const flatFolderList = await getFlatFolderList();
- const localFlatFolderList = await Promise.all(
- flatFolderList
- .filter(({ type }) => type === 'folder')
- .map(async ({ folder }) => await genericFolderToLocalFolder(folder))
- );
- const folders = [
- possibleSourceFolder,
- ...localFlatFolderList.filter(
- fldr =>
- fldr.accountId !== possibleSourceFolder.accountId &&
- fldr.path !== possibleSourceFolder.path
- )
- ];
-
- logger.log(`BEGIN execution of ${mindr.guid}`, {
- correlationId,
- guid
- });
- const startTime = performance.now();
-
- const targetFolders = folders;
-
- let hasError = false;
- let iterationCount = 0;
-
- logger.log(`BEGIN targetFolder iteration`, {
- guid,
- correlationId,
- targetFolderCount: (targetFolders || []).length,
- targetFolders
- });
-
- const applyActionToMessage = async (message, messageFolder) => {
- const { id } = message;
-
- logger.log(`BEGIN applyActionToMessage`);
- await applyAction(id, action);
- logger.log(
- `Do we have a destination folder? ${
- destinationFolder ? 'yes' : 'no'
- }`,
- destinationFolder
- );
- if (destinationFolder) {
- await doMoveMessageToFolder(
- message,
- destinationFolder,
- correlationId
- );
- }
- logger.log(`END applyActionToMessage`);
- };
-
- const applyActionToFirstMessageInFolders = async () => {
- for await (let folder of targetFolders) {
- logger.log(` -- executeMindr: folder loop (${folder.name})`, {
- correlationId,
- folder
- });
-
- try {
- const actionResult = await applyActionToMessageInFolder(
- folder,
- { headerMessageId, author },
- applyActionToMessage,
- true
- );
- const { done, value } = await actionResult.next();
- const success = Boolean(done && value && value.executed);
- if (success) {
- return true;
- }
- } catch (ex) {
- logger.error('ERROR: execute mindr // mailmindr: >> !!', {
- correlationId,
- guid,
- exception: ex
- });
- hasError = true;
- }
- iterationCount++;
- }
- return false;
- };
-
- await applyActionToFirstMessageInFolders();
-
- logger.log(`END targetFolder iteration`, {
- correlationId,
- guid,
- targetFolderCount: (targetFolders || []).length,
- targetFolders
- });
-
- const endTime = performance.now();
- logger.log(
- `mailmindr: execution finished in ${endTime - startTime}ms`,
- moveMessageTo
- );
-
- const executionEnd = Date.now();
- const executionDuration = (executionEnd - executionStart) / 1000;
-
- if (executionDuration > 3 * 60) {
- logger.error(
- `Execution of mindr '${guid}' took more than 180 seconds`,
- { guid, correlationId, executionDuration }
- );
- } else if (executionDuration > 60) {
- logger.warn(`Execution of mindr '${guid}' took more than 60 seconds`, {
- guid,
- correlationId,
- executionDuration
- });
- } else {
- logger.warn(
- `Execution of mindr '${guid}' took ${executionDuration} seconds`,
- { guid, correlationId, executionDuration }
- );
- }
- logger.log(`END execution of ${mindr.guid}`, { correlationId, guid });
- logger.log(`END executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
- guid,
- headerMessageId
- });
-
- return !hasError;
-};
-
-const dispatch = async (action, payload) => {
- const correlationId = createCorrelationId('dispatch');
-
- const getUpdatedState = async (theAction, thePayload) => {
- const storageVersion = getCurrentStorageAdapterVersion();
- const { storeState } = getStorageAdapter(storageVersion);
-
- try {
- switch (theAction) {
- case 'heartbeat': {
- const { mindrs: mindrList } = getState();
-
- const {
- mindrs,
- overdue,
- active
- } = getActiveAndOverdueMindrs(mindrList);
-
- //
- //
- //
-
- return { ...state, mindrs, active, overdue };
- }
- case 'connection:open': {
- const { port } = payload;
-
- const openConnections = [...state.openConnections, port];
- logger.log(
- `open connections: ${openConnections.length}`,
- openConnections
- );
- return { ...state, openConnections };
- }
- case 'connection:close': {
- const { port } = payload;
- const { name } = port;
-
- const openConnections = getState().openConnections.filter(
- connection => connection.name !== name
- );
-
- return { ...state, openConnections };
- }
- case 'dialog:open': {
- const newDialogDetails = thePayload;
- const openDialogs = [
- ...state.openDialogs,
- newDialogDetails
- ];
- return { ...state, openDialogs };
- }
- case 'dialog:close': {
- const { dialogId } = thePayload;
- const { openDialogs: dialogs } = getState();
- const dialogDetails = dialogs.find(
- dialogInfo => dialogId === dialogInfo.dialogId
- );
- if (dialogDetails) {
- const openDialogs = dialogs.filter(
- openDialog => openDialog.dialogId !== dialogId
- );
- logger.info(
- `remaining open dialogs: ${openDialogs.length}`
- );
- return {
- ...state,
- openDialogs
- };
- } else {
- logger.warn(
- `cannot find details for open dialog ID: '${dialogId}'`,
- {
- payload: thePayload,
- dialogId,
- openDialogs: dialogs
- }
- );
- }
- return state;
- }
- case 'state:initialize':
- return await initializeStorage();
- case 'mindr:create-or-update': {
- return await createOrUpdateMindr(state, thePayload);
- }
- case 'mindr:remove': {
- const { guid } = thePayload;
- const currentState = getState();
- const {
- mindrs: stateMindrs,
- overdue: stateOverdue,
- active: stateActive
- } = currentState;
-
- const mindrCount = {
- mindrs: (stateMindrs || []).length,
- overdue: (stateOverdue || []).length,
- active: (stateActive || []).length
- };
-
- //
- const mindrs = stateMindrs.filter(
- item => item.guid !== guid
- );
-
- const overdue = (stateOverdue || []).filter(
- mindr => mindr.guid !== guid
- );
- const active = (stateActive || []).filter(
- mindr => mindr.guid !== guid
- );
-
- if (
- mindrCount.overdue === overdue.length &&
- mindrCount.active === active.length &&
- mindrCount.mindrs === mindrs.length
- ) {
- //
- logger.log(
- `no mindr was removed, state remains untouched`
- );
-
- return getState();
- }
-
- const localState = {
- ...currentState,
- mindrs,
- active,
- overdue
- };
-
- await storeState(localState);
-
- return localState;
- }
- case 'preset:timespan-create': {
- const currentState = getState();
- const { presets } = currentState;
- const { time } = presets;
- const { current } = thePayload;
-
- const localState = {
- ...currentState,
- presets: {
- ...presets,
- time: [...time, current]
- }
- };
-
- await storeState(localState);
-
- return localState;
- }
- case 'preset:timespan-update': {
- const currentState = getState();
- const { presets } = currentState;
- const { time: timePresets } = presets;
- const { current, source } = thePayload;
- const time = timePresets.map(item =>
- equalTimePresetValues(item, source) ? current : item
- );
-
- logger.info(`new presets:`, time);
-
- const newState = {
- ...currentState,
- presets: {
- ...presets,
- time
- }
- };
-
- await browser.storage.local.set({
- presets: newState.presets
- });
-
- return newState;
- }
- case 'preset:timespan-remove': {
- const currentState = getState();
- const { presets } = currentState;
- const { time: timePresets } = presets;
- const { presets: toBeRemoved = [] } = thePayload;
-
- let time = [...timePresets];
- toBeRemoved.forEach(toBeRemovedPreset => {
- time = time.filter(
- preset =>
- !equalTimePresetValues(
- preset,
- toBeRemovedPreset
- )
- );
- });
-
- const newState = {
- ...currentState,
- presets: {
- ...presets,
- time
- }
- };
-
- await browser.storage.local.set({
- presets: newState.presets
- });
-
- return newState;
- }
- case 'setting:update': {
- const currentState = getState();
- const { name, value } = thePayload;
-
- const localState = {
- ...currentState,
- settings: { ...state.settings, [name]: value }
- };
-
- await storeState(localState);
-
- return localState;
- }
- case 'state:lock-execution': {
- //
- //
- const currentState = getState();
- const { guid } = thePayload;
-
- const localState = {
- ...currentState,
- __inExecution: [...currentState.__inExecution, guid]
- };
-
- storeState(localState);
-
- return localState;
- }
- case 'state:unlock-execution': {
- //
- const currentState = getState();
- const { guid } = thePayload;
-
- const localState = {
- ...currentState,
- __inExecution: currentState.__inExecution.filter(
- item => item !== guid
- )
- };
-
- storeState(localState);
-
- return localState;
- }
- }
- } catch (exception) {
- logger.error(
- `ugh, we're compromising the state w/ msg '${theAction}' :`,
- exception
- );
- throw exception;
- }
- };
-
- try {
- logger.log(`:: acn :: ${action}`, { correlationId, action, payload });
- const mutatedState = await getUpdatedState(action, payload);
- if (!mutatedState) {
- logger.error(`:: acn :: ${action} failed`, {
- correlationId,
- action,
- payload
- });
- return false;
- }
-
- state = mutatedState;
-
- try {
- await refreshButtons();
- } catch (buttonUpdateException) {
- /* there's silence */
- }
- } catch (e) {
- logger.error(`mailmindr crashed due to ${e.message}`, {
- correlationId,
- error: e
- });
-
- return false;
- }
-
- return true;
-};
-
const sendConnectionMessage = async (connectionName, message) => {
- const { openConnections } = getState();
- logger.info(`open connections?`, openConnections, 'send message', message);
- const connection = openConnections.find(con => con.name === connectionName);
- if (connection) {
- await connection.postMessage(message);
- }
-};
-
-const findDialogForType = dialogType => {
- const { openDialogs } = getState();
- return openDialogs.find(dialog => dialog.dialogType === dialogType);
-};
-
-const findMindrByGuid = guid => {
- const { mindrs } = getState();
- const result = (mindrs || []).find(item => item.guid === guid);
-
- return result;
+ const state = store.getState();
+ const openConnections = selectOpenConnections(state);
+ await sendConnectionMessageEx(openConnections, message, connectionName);
};
const editMindrByGuid = async guid => {
const correlationId = createCorrelationId('editMindrByGuid');
logger.log('BEGIN editMindrByGuid', { correlationId, guid });
- const mindr = findMindrByGuid(guid);
+ const state = store.getState();
+ const mindr = selectMindrByGuid(state, guid);
const {
headerMessageId,
author,
@@ -799,7 +251,8 @@
};
const bringDialogToFront = async dialogType => {
- const dialog = await findDialogForType(dialogType);
+ const state = store.getState();
+ const dialog = selectDialogForType(state, dialogType);
if (!dialog) {
return false;
@@ -812,32 +265,6 @@
return true;
};
-const closeCreateOrUpdateMindrDialog = async () => {
- const correlationId = createCorrelationId('closeCreateOrUpdateMindrDialog');
- const dialogType = 'mailmindr:dialog:set-mindr';
- const dialog = findDialogForType(dialogType);
-
- logger.log(`BEGIN closeCreateOrUpdateMindrDialog`, {
- correlationId,
- dialogType,
- dialog
- });
-
- if (dialog) {
- await dispatch('dialog:close', {
- dialogId: dialog.dialogId
- });
-
- await messenger.windows.remove(dialog.details.id);
- }
-
- logger.log(`END closeCreateOrUpdateMindrDialog`, {
- correlationId,
- dialogType,
- dialog
- });
-};
-
const showCreateOrUpdateMindrDialog = async (currentMessage, mindr) => {
const dialogType = 'mailmindr:dialog:set-mindr';
const { headerMessageId, author, folder, subject } = currentMessage;
@@ -846,13 +273,6 @@
logger.info('generic folder', genericFolder);
const { accountId, name, path, type, identityEmailAddress } = genericFolder;
- //
- //
- //
- //
- //
- //
-
const dialogId = createMailmindrId('mailmindr:dialog:set-mindr');
const parameters = new URLSearchParams();
@@ -883,7 +303,7 @@
allowScriptsToClose: true
});
- await dispatch('dialog:open', { dialogId, dialogType, details });
+ store.dispatch(openDialog(dialogId, dialogType, details));
//
//
@@ -891,75 +311,6 @@
//
};
-const showMindrAlert = async ({ overdue, active }) => {
- const dialogType = 'mailmindr:mindr-alert';
- const dialog = findDialogForType(dialogType);
-
- if (dialog) {
- logger.log('we already have a message dialog', dialog);
-
- if ((overdue || []).length === 0 && (active || []).length === 0) {
- //
- await dispatch('dialog:close', {
- dialogId: dialog.details.id
- });
- messenger.windows.remove(dialog.details.id);
- } else {
- //
- await sendConnectionMessage('connection:mindr-alert', {
- overdue,
- active
- });
- await messenger.windows.update(dialog.details.id, {
- //
- drawAttention: true
- });
- }
- } else {
- logger.log(
- 'need to open a new dialog with active/overdue mindrs',
- active,
- overdue
- );
- if ((active || []).length === 0 && (overdue || []).length === 0) {
- logger.log('no dialog needed');
- return;
- }
- const dialogId = createMailmindrId('mailmindr:dialog:mindr-alert');
-
- const parameters = new URLSearchParams();
- parameters.set('dialogId', dialogId);
-
- const { width: screenWidth, availHeight: screenHeight } = screen;
- const height = 200;
- const width = 400;
- const left = screenWidth - width;
- const top = screenHeight - height;
- const url = `/views/dialogs/mindr-alert/index.html?${parameters}`;
- const details = await messenger.windows.create({
- left,
- top,
- height,
- width,
- url,
- type: 'popup',
- state: 'normal',
- allowScriptsToClose: true
- });
-
- await messenger.windows.update(details.id, {
- top,
- left,
- width,
- height,
- focused: true,
- drawAttention: true
- });
-
- await dispatch('dialog:open', { dialogId, dialogType, details });
- }
-};
-
const showTimespanPresetEditor = async timePreset => {
const dialogType = 'mailmindr:time-preset-editor';
const hasDialog = await bringDialogToFront(dialogType);
@@ -988,33 +339,31 @@
allowScriptsToClose: true
});
- await dispatch('dialog:open', { dialogId, dialogType, details });
+ store.dispatch(openDialog(dialogId, dialogType, details));
};
const handleStartup = async () => {
- const MAX_RETRIES = 5;
- const initailze = async () => {
- for (let retryCount = 0; retryCount < MAX_RETRIES; retryCount++) {
- const success = await dispatch('state:initialize');
- if (success) {
- return true;
- }
- await new Promise(resolve => setTimeout(() => resolve(), 1000));
- }
- logger.error(
- `Initialization failed after ${MAX_RETRIES} attempts. Aborting mailmindr.`
- );
- return false;
- };
+ try {
+ const storedState = await initializeStorage();
+ const localState = storedState || initialState;
+
+ const { storeState } = await getStorage();
+
+ const saveState = async () => {
+ const state = store.getState();
+
+ await storeState(state);
+ };
+ const saveStateThrottled = throttle(saveState, 250);
+
+ store = createStore(rootReducer, localState);
+ store.addEventListener('change', saveStateThrottled);
- const success = await initailze();
- if (success) {
setupUI();
startHeartbeat();
- messenger.messageDisplayScripts.register({
- js: [{ file: '/scripts/mailmindr-message-script.js' }]
- });
- } else {
+ } catch (startupError) {
+ logger.error(`mailmindr failed to start`, startupError);
+ //
const errorButtonCaption = browser.i18n.getMessage(
'errorInitializationBrowserButtonText'
);
@@ -1026,11 +375,16 @@
const { id, windowId } = tab;
const msg = message || (await getCurrentDisplayedMessage({ id, windowId }));
if (msg) {
+ logger.log(`findExistingMindrForTab`, store);
const { headerMessageId } = msg;
- const { mindrs } = getState();
+ const sanitizedHeaderMessageId = sanitizeHeaderMessageId(
+ headerMessageId
+ );
+ const { mindrs } = store.getState();
const mindr = mindrs.find(
mindr =>
- mindr.headerMessageId === headerMessageId && !!headerMessageId
+ sanitizeHeaderMessageId(mindr.headerMessageId) ===
+ sanitizedHeaderMessageId && !!sanitizedHeaderMessageId
);
return mindr;
@@ -1040,15 +394,22 @@
};
const handleSelectedMessagesChanged = async (tab, messageList) => {
- const mindr = await findExistingMindrForTab(tab);
+ const { id, windowId } = tab;
+ const msg = await getCurrentDisplayedMessage({ id, windowId });
+ if (!msg) {
+ logger.log(`handleSelectedMessagesChanged: no message selected, exit.`);
+ return;
+ }
+
+ const mindr = await findExistingMindrForTab(tab, msg);
const title = !!mindr ? '!' : null;
const details = { text: title };
- const { id, windowId: _ } = tab;
messenger.messageDisplayAction.setBadgeText(details);
const tabInfo = await messenger.tabs.get(id);
- if (!tabInfo.mailTab) {
+ if (!tabInfo.mailTab && tabInfo.type !== 'messageDisplay') {
+ logger.error(`this is not a mail tab`, tabInfo);
return;
}
@@ -1057,19 +418,26 @@
);
if (!mindr) {
+ logger.log(`There's no mindr present for this tab`, { tabInfo, msg });
await messenger.tabs.executeScript(id, {
code: `typeof removeExistingMessageBars === 'function' && removeExistingMessageBars()`
});
return;
}
- messenger.messageDisplayAction.setBadgeBackgroundColor({
+ await messenger.messageDisplayAction.setBadgeBackgroundColor({
color: '#ad3bff'
});
- messenger.tabs.insertCSS(id, {
+ await messenger.tabs.insertCSS(id, {
file: '/styles/message-bar.css'
});
+
+ await messenger.tabs.executeScript(id, {
+ file: '/scripts/mailmindr-message-script.js'
+ //
+ });
await messenger.tabs.executeScript(id, {
+ //
code: `createMindrBar('${JSON.stringify(mindr.guid)}')`
});
};
@@ -1077,14 +445,6 @@
const getCurrentDisplayedMessage = async tab => {
const correlationId = createCorrelationId('getCurrentDisplayedMessage');
const { id: tabId, windowId = messenger.windows.WINDOW_ID_CURRENT } = tab;
- const query = {
- windowId
- };
- const tabs = await browser.tabs.query(query);
-
- if (!tabs || tabs.length === 0) {
- return undefined;
- }
const message = await browser.messageDisplay.getDisplayedMessage(tabId);
if (!message) {
@@ -1133,16 +493,15 @@
return {
...message,
- headerMessageId
+ headerMessageId: sanitizeHeaderMessageId(headerMessageId)
};
}
return message;
};
-const refreshButtons = async () => {
+const refreshButtons = async mindrs => {
try {
- const { mindrs } = getState();
const hasMindrs = mindrs.length;
const current = await messenger.tabs.query({
currentWindow: true,
@@ -1168,14 +527,22 @@
}
};
-async function onLoadDialog(dialogOpenInfo) {
+const onLoadDialog = dialogOpenInfo => {
const { name, ...rest } = dialogOpenInfo;
+ const state = store.getState();
+
+ if (!store || !state) {
+ logger.error(`State '(${state})' or store '(${store})' isn't defined`, {
+ state,
+ store
+ });
+ }
switch (name) {
case 'set-mindr': {
- const { settings, presets, mindrs } = getState();
const { guid } = rest;
-
- const mindr = mindrs.find(mindr => mindr.guid === guid);
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
+ const mindr = selectMindrByGuid(state, guid);
return {
settings,
@@ -1183,9 +550,30 @@
mindr
};
}
+ case 'set-outgoing-mindr': {
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
+ const {
+ sender: { windowId, id }
+ } = rest;
+ const draft = selectDraftForSenderTabOrNull(state, {
+ windowId,
+ id
+ });
+
+ return {
+ settings,
+ presets,
+ draft
+ };
+ }
case 'mindr-alert': {
- const { active, overdue } = getState();
- return { active, overdue };
+ //
+ const { active, overdue } = store.getState();
+ return {
+ active: active.filter(showReminderForMindr),
+ overdue: overdue.filter(showReminderForMindr)
+ };
}
case 'time-preset-editor': {
logger.log('REST', rest);
@@ -1197,12 +585,16 @@
`mailmindr:onLoadDialog // no data loadable for dialog '${name}'`
);
}
-}
+};
const onWindowRemoved = async windowId => {
const log = logger.createContextLogger({ name: 'onWindowRemoved' });
- const { openDialogs } = getState();
+ const state = store.getState();
+ const openDialogs = selectOpenDialogs(state);
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
+
const dialogInfo = openDialogs.find(
dialog => dialog.details.id === windowId
);
@@ -1216,119 +608,35 @@
case 'mailmindr:time-preset-editor':
await sendConnectionMessage('connection:mailmindr-options', {
topic: 'settings:unlock',
- message: { settings: getSettings() }
+ message: { settings: { settings, presets } }
});
break;
}
- await dispatch('dialog:close', { dialogId });
+ store.dispatch(closeDialog(dialogId));
}
};
-const doMoveMessageToFolder = async (
- message,
- destinationFolder,
- parentCorrelationId
-) => {
- const correlationId = createCorrelationId(
- 'doMoveMessageToFolder',
- parentCorrelationId
+const findIceboxFolderForAccountIdentityMailAddress = async identityMailAddress => {
+ const { settings } = store.getState();
+ const defaultIceboxFolder = await getGenericIceboxFolderForIdentityOrNull(
+ identityMailAddress,
+ settings
);
- try {
- const { id } = message;
- const { accountId, path } = destinationFolder;
- logger.log(`BEGIN move message ${id}`, {
- correlationId,
- message
- });
- await messenger.messages.move([id], { accountId, path });
- logger.log(`END move message ${id}`, {
- correlationId,
- message
- });
- return true;
- } catch (error) {
- logger.error(error, { correlationId });
- return false;
- }
-};
-
-const moveMessageToFolder = async (
- messageDetails,
- sourceFolder,
- targetFolder
-) => {
- const correlationId = createCorrelationId('moveMessagesToFolder');
- try {
- logger.info('moveMessageToFolder target', {
- correlationId,
- targetFolder
- });
-
- const moveMessageToFolderAction = async (
- message,
- _messageSourceFolder
- ) => {
- await doMoveMessageToFolder(message, targetFolder, correlationId);
- };
-
- logger.info(`BEGIN applying action to folder`, {
- correlationId,
- sourceFolder,
- messageDetails
- });
- const actionResult = await applyActionToMessageInFolder(
- sourceFolder,
- messageDetails,
- moveMessageToFolderAction,
- true
- );
- const { done, value } = await actionResult.next();
-
- //
- const success = Boolean(done);
-
- const logMessage = `moveMessageToFolder : applyActionToMessageInFolder returns { done: ${done}, value: ${value} }`;
- if (success) {
- logger.info(logMessage, { correlationId, result: { done, value } });
- } else {
- logger.warn(logMessage, { correlationId, result: { done, value } });
- }
-
- if (value === null) {
- logger.error(
- `moveMessageToFolder : applyActionToMessageInFolder message not found in folder`
- );
- }
- logger.info(`END applying action to folder`, {
- correlationId,
- sourceFolder,
- messageDetails
- });
- } catch (e) {
- logger.error(`moveMessageToFolder failed: ${e.message}`, {
- correlationId,
- error: e
- });
- return false;
- }
-
- return true;
+ return defaultIceboxFolder;
};
const moveToIcebox = async (headerMessageId, metaData) => {
- const { settings } = getState();
-
const { folderAccountIdentityMailAddress } = metaData;
- const defaultIceboxFolder = await getGenericIceboxFolderForIdentityOrNull(
- folderAccountIdentityMailAddress,
- settings
+ const defaultIceboxFolder = await findIceboxFolderForAccountIdentityMailAddress(
+ folderAccountIdentityMailAddress
);
if (!defaultIceboxFolder) {
return false;
}
+
const correlationId = createCorrelationId('moveToIcebox');
logger.log('BEGIN moveToIcebox', {
correlationId,
@@ -1367,26 +675,30 @@
const messageHandler = async (request, _sender, _sendResponse) => {
const { action, payload } = request;
+
let result = null;
switch (action) {
case 'dialog:open':
result = {
status: 'ok',
- payload: await onLoadDialog(payload)
+ payload: onLoadDialog(payload)
};
return result;
case 'dialog:close':
- const dialogId = payload;
- const { openDialogs } = getState();
- const dialog = openDialogs.find(
- dialog => dialog.dialogId === dialogId
- );
+ {
+ const dialogId = payload;
+ const state = store.getState();
+ const openDialogs = selectOpenDialogs(state);
+ const dialog = openDialogs.find(
+ dialog => dialog.dialogId === dialogId
+ );
- await dispatch('dialog:close', { dialogId });
+ store.dispatch(closeDialog(dialogId));
- return { status: 'ok', dialog };
+ return { status: 'ok', dialog };
+ }
break;
case 'dialog:bring-to-front':
const { dialogType } = payload;
@@ -1397,14 +709,39 @@
return { status: 'error', paylad: null };
}
break;
- case 'mindrs:list':
+ case 'mindrs:list': {
+ const state = store.getState();
+ const mindrsList = selectMindrs(state);
result = {
status: 'ok',
payload: {
- mindrs: state.mindrs
+ mindrs: mindrsList
}
};
return result;
+ }
+ case 'mindr:create-outgoing': {
+ const correlationId = createCorrelationId(
+ `messageHandler:mindr:create`
+ );
+ logger.log(`BEGIN messageHandler:mindr:create-outgoing`, {
+ correlationId,
+ payload
+ });
+
+ store.dispatch(createOrUpdateDraft(payload));
+
+ const result = {
+ status: 'ok',
+ payload: {}
+ };
+ logger.log(`END messageHandler:mindr:create-outgoing`, {
+ correlationId,
+ payload
+ });
+
+ return result;
+ }
case 'mindr:create': {
const { guid, headerMessageId, metaData, doMoveToIcebox } = payload;
const correlationId = createCorrelationId(
@@ -1431,7 +768,11 @@
//
//
if (!createdMindr.action.moveMessageTo) {
- const existingMindr = findMindrByGuid(guid);
+ const state = store.getState();
+ const existingMindr = selectMindrByGuid(
+ state,
+ guid
+ );
if (existingMindr) {
logger.log(
`updating existing mindr, set icebox folder from original mindr`,
@@ -1457,9 +798,9 @@
};
//
- const {
- settings: { defaultIceboxFolder }
- } = getState();
+ const defaultIceboxFolder = await findIceboxFolderForAccountIdentityMailAddress(
+ metaData.folderAccountIdentityMailAddress
+ );
const iceBoxFolder = await genericFolderToLocalFolder(
defaultIceboxFolder
);
@@ -1481,7 +822,7 @@
}
}
- await dispatch('mindr:create-or-update', createdMindr);
+ store.dispatch(createOrUpdateMindr(createdMindr));
result = {
status: createdMindr ? 'ok' : 'error',
@@ -1491,7 +832,9 @@
};
logger.log('refresh buttons', { correlationId });
- await refreshButtons();
+ const state = store.getState();
+
+ await refreshButtons(selectMindrs(state));
//
} catch (error) {
logger.error(`FAIL messageHandler:mindr:create`, {
@@ -1508,9 +851,18 @@
return result;
}
+ case 'mindr:remove-outgoing-mindr': {
+ const { sender } = payload;
+ const state = store.getState();
+ const draft = selectDraftForSenderTabOrNull(state, sender);
+
+ store.dispatch(removeDraft(draft));
+
+ return { status: 'ok', paylad: null };
+ }
case 'mindr:remove': {
const { guid } = payload;
- await dispatch('mindr:remove', { guid });
+ store.dispatch(removeMindr(guid));
return { status: 'ok', paylad: null };
}
@@ -1518,7 +870,7 @@
logger.log('mindr:get-information', payload);
const { guid } = payload;
- const { mindrs } = getState();
+ const { mindrs } = store.getState();
const mindr = mindrs.find(mindr => mindr.guid === guid);
logger.log(`mindr:get-information: ${mindr.headerMessageId}`);
@@ -1531,25 +883,34 @@
break;
}
case 'navigate:open-message-by-mindr-guid':
- //
- //
- const { guid } = payload;
- const { mindrs } = getState();
- const mindr = mindrs.find(mindr => mindr.guid === guid);
- if (mindr) {
- const { headerMessageId } = mindr;
- if (messenger.messageDisplay.open) {
- await messenger.messageDisplay.open({
- headerMessageId,
- active: true
- });
- } else {
- await browser.mailmindrMessagesApi.openMessageByMessageHeaderId(
- headerMessageId
+ {
+ //
+ //
+ const { guid } = payload;
+ const state = store.getState();
+ const mindr = selectMindrByGuid(state, guid);
+
+ if (mindr) {
+ const headerMessageId = sanitizeHeaderMessageId(
+ mindr.headerMessageId
);
+ if (messenger.messageDisplay.open) {
+ logger.log(
+ `Open message by headerMessageId [nativeAPI] < '${headerMessageId}' >`
+ );
+ try {
+ await messenger.messageDisplay.open({
+ headerMessageId,
+ active: true
+ });
+ } catch (openMessageError) {
+ logger.error(
+ `The message cannot be opened`,
+ openMessageError
+ );
+ }
+ }
}
-
- //
}
break;
case 'refresh:buttons':
@@ -1561,24 +922,30 @@
`messageHandler:mindr:snooze`
);
- const { mindrs } = getState();
- const { presets, settings } = getSettings();
+ const localState = store.getState();
+ const mindrs = selectMindrs(localState);
+ const presets = selectPresets(localState);
+ const settings = selectSettings(localState);
+
const { snoozeTime } = settings;
- await snoozeMindrs(
- dispatch,
- list,
- mindrs,
+ logger.log(`mindr:snooze // snoozeTime: ${snoozeTime}`, {
snoozeTime,
- correlationId
+ mindrs
+ });
+
+ store.dispatch(
+ snoozeMindrs(list, mindrs, snoozeTime, correlationId)
);
- await dispatch('heartbeat');
+ store.dispatch(heartBeat());
- const { active, overdue } = getState();
+ const { active, overdue } = store.getState();
const overdueNotExecuted = overdue.filter(item => !item.isExecuted);
- await showMindrAlert({ overdue: overdueNotExecuted, active });
+ store.dispatch(
+ showMindrAlert({ overdue: overdueNotExecuted, active })
+ );
return { status: 'ok', paylad: null };
}
@@ -1588,13 +955,15 @@
do {
const guid = list.pop();
- await dispatch('mindr:remove', { guid });
+ store.dispatch(removeMindr(guid));
} while (list.length);
- const { active, overdue } = getState();
+ const { active, overdue } = store.getState();
const overdueNotExecuted = overdue.filter(item => !item.isExecuted);
- await showMindrAlert({ overdue: overdueNotExecuted, active });
+ store.dispatch(
+ showMindrAlert({ overdue: overdueNotExecuted, active })
+ );
return { status: 'ok', paylad: null };
}
@@ -1604,39 +973,53 @@
if (source) {
//
logger.warn(`updating timespan`, current, source);
- await dispatch('preset:timespan-update', { current, source });
+ store.dispatch(updateTimespanPreset(current, source));
} else {
//
logger.info(`creating new timespan`, current);
- await dispatch('preset:timespan-create', { current });
+ store.dispatch(createTimespanPreset(current));
}
+ const state = store.getState();
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
+
await sendConnectionMessage('connection:mailmindr-options', {
topic: 'settings:unlock',
- message: { settings: getSettings() }
+ message: { settings: { settings, presets } }
});
return { status: 'ok', paylad: null };
}
case 'preset:remove-timespans': {
- const { presets } = payload;
+ const { presets: currentPresets } = payload;
+
+ if (
+ currentPresets &&
+ Array.isArray(currentPresets) &&
+ currentPresets.length
+ ) {
+ store.dispatch(removeTimespanPreset(currentPresets));
+ const state = store.getState();
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
- if (presets && Array.isArray(presets) && presets.length) {
- await dispatch('preset:timespan-remove', { presets: presets });
await sendConnectionMessage('connection:mailmindr-options', {
topic: 'settings:updated',
- message: { settings: getSettings() }
+ message: { settings: { settings, presets } }
});
}
return { status: 'ok', paylad: null };
}
case 'options:get-settings': {
- const payload = getSettings();
+ const state = store.getState();
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
await sendConnectionMessage('connection:mailmindr-options', {
topic: 'settings:updated',
- message: { settings: payload }
+ message: { settings: { settings, presets } }
});
break;
}
@@ -1644,7 +1027,8 @@
let { name, value } = payload;
logger.info(`? set '${name}' to '${value}'`, value);
- const { settings } = getSettings();
+ const state = store.getState();
+ const settings = selectSettings(state);
const propNames = Object.keys(settings);
switch (name) {
@@ -1681,13 +1065,14 @@
}
if (propNames.indexOf(name) >= 0) {
- await dispatch('setting:update', { name, value });
-
- const payload = getSettings();
+ store.dispatch(updateSetting(name, value));
+ const state = store.getState();
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
await sendConnectionMessage('connection:mailmindr-options', {
topic: 'settings:updated',
- message: { settings: payload }
+ message: { settings: { settings, presets } }
});
} else {
logger.error(
@@ -1697,9 +1082,12 @@
break;
}
case 'settings:force-unlock': {
+ const state = store.getState();
+ const settings = selectSettings(state);
+ const presets = selectPresets(state);
await sendConnectionMessage('connection:mailmindr-options', {
topic: 'settings:unlock',
- message: { settings: getSettings() }
+ message: { settings: { settings, presets } }
});
break;
}
@@ -1710,8 +1098,9 @@
}
case 'do:mindr-action-remove': {
const { guid } = payload;
- const { mindrs } = getState();
- const mindr = mindrs.find(mindr => mindr.guid === guid);
+ const state = store.getState();
+ const mindr = selectMindrByGuid(state, guid);
+
if (!mindr) {
return;
}
@@ -1743,7 +1132,10 @@
}
//
- if (state.mindrs.length) {
+ const localState = store.getState();
+ const mindrs = selectMindrs(localState);
+
+ if (mindrs.length) {
messenger.browserAction.enable();
} else {
messenger.browserAction.disable();
@@ -1771,63 +1163,71 @@
return;
}
+ logger.log(`connectionHandler called for ${name}`);
if (name === 'connection:mindr-alert') {
port.onMessage.addListener(onConnectionMessage);
- port.onDisconnect.addListener(
- async () => await dispatch('connection:close', { port })
+ port.onDisconnect.addListener(() =>
+ store.dispatch(connectionClosed(port))
);
- await dispatch('connection:open', { port });
+ store.dispatch(connectionOpened(port));
//
- const { overdue, active } = getState();
+ const { overdue, active } = store.getState();
await sendConnectionMessage(name, {
- overdue,
- active
+ overdue: overdue.filter(showReminderForMindr),
+ active: active.filter(showReminderForMindr)
});
} else if (name === 'connection:mailmindr-options') {
port.onMessage.addListener(onConnectionMessage);
- port.onDisconnect.addListener(
- async () => await dispatch('connection:close', { port })
+ port.onDisconnect.addListener(() =>
+ store.dispatch(connectionClosed(port))
);
- await dispatch('connection:open', { port });
-
- //
-
- //
- //
- //
- //
+ store.dispatch(connectionOpened(port));
}
};
browser.tabs.onMoved.addListener(async ({}) => {});
browser.tabs.onActivated.addListener(async activeInfo => {
+ if (!store) {
+ logger.error(`tabs.onActivated:handler: store is undefined`);
+ return;
+ }
+
const { tabId, windowId } = activeInfo || {};
- console.log(`tab activated: ${tabId}, ${windowId} <- ${activeInfo}`);
- await refreshButtons();
+ const { mindrs } = store.getState();
+ //
+ await refreshButtons(mindrs);
});
browser.mailTabs.onSelectedMessagesChanged.addListener(
handleSelectedMessagesChanged
);
-browser.messageDisplayAction.onClicked.addListener(async ({ id, windowId }) => {
- const currentMessage = await getCurrentDisplayedMessage({ id, windowId });
- const mindr = await findExistingMindrForTab(
- {
+browser.messageDisplayAction.onClicked.addListener(
+ async ({ id, windowId, type }) => {
+ const currentMessage = await getCurrentDisplayedMessage({
id,
windowId
- },
- currentMessage
- );
+ });
+ const mindr = await findExistingMindrForTab(
+ {
+ id,
+ windowId
+ },
+ currentMessage
+ );
- await showCreateOrUpdateMindrDialog(currentMessage, mindr);
- //
-});
+ await showCreateOrUpdateMindrDialog(currentMessage, mindr);
+ //
+ }
+);
browser.menus.onShown.addListener(info => {
- const contexts = ['message_list', 'page'];
+ const /** @type {browser.menus.ContextType[]} */ contexts = [
+ 'message_list',
+ 'page'
+ ];
//
if (
info &&
@@ -1871,6 +1271,77 @@
messageHandler
);
+//
+//
+//
+//
+//
+//
+//
+//
+//
+
+messenger.compose.onBeforeSend.addListener((tab, details) => {
+ logger.log(`ONBEFORESEND -- `, { tab, details });
+ return { cancel: false };
+});
+
+messenger.compose.onAfterSend.addListener(async (tab, sendInfo) => {
+ const { messages, mode, error, headerMessageId } = sendInfo;
+
+ const findHeaderMessageId = ({ messages, headerMessageId }) => {
+ if (headerMessageId) {
+ logger.log(`? sent headerMessageId: ${headerMessageId}`);
+ return headerMessageId;
+ } else if (messages && messages.length) {
+ const message = messages[0];
+ return message.headerMessageId;
+ } else {
+ return null;
+ }
+ };
+
+ const hdrMsgId = findHeaderMessageId({ messages, headerMessageId });
+ if (hdrMsgId) {
+ //
+ const maybeDraft = selectDraftForSenderTabOrNull(store.getState(), tab);
+ if (maybeDraft && maybeDraft.mindr) {
+ const { mindr } = maybeDraft;
+ const {
+ author = '',
+ subject = '',
+ folder = null
+ } = sendInfo?.messages?.[0];
+ const sourceFolder = await localFolderToGenericFolder(folder);
+ const {
+ accountId: folderAccountId,
+ identityEmailAddress: folderAccountIdentityMailAddress,
+ name: folderName,
+ path: folderPath,
+ type: folderType
+ } = sourceFolder;
+ const createdMindr = await createMindrFromActionTemplate({
+ ...mindr,
+ headerMessageId,
+ metaData: {
+ headerMessageId,
+ author,
+ subject,
+ folderAccountId,
+ folderName,
+ folderPath,
+ folderType,
+ folderAccountIdentityMailAddress
+ }
+ });
+
+ store.dispatch(createOrUpdateMindr(createdMindr));
+ }
+ }
+
+ logger.log(`ONAFTERSEND -- `, { tab, sendInfo });
+});
+
browser.runtime.onConnect.addListener(connectionHandler);
browser.commands.onCommand.addListener(async command => {
@@ -1885,4 +1356,56 @@
}
});
+const thunderbirdVersion = findThunderbirdVersion();
+const handleNewMailReceived = async (
+ /** @type {messenger.folders.MailFolder} */ folder,
+ /** @type {messenger.messages.MessageList} */ messages
+) => {
+ const /** @type {(mindr: string) => Mindr} */ selectByHeaderMessageId = selectMindrByHeaderMessageId.bind(
+ null,
+ store.getState()
+ );
+ const /** @type { { messageHeaderId: string, replyToHeaderMessageId: string }[] } */ headerMessageIds = [];
+ for await (let msg of getMessages(messages)) {
+ const messageId = msg.id;
+ const message = await messenger.messages.getFull(messageId);
+ //
+ const { headers } = message;
+ const messageHeaderId = msg.headerMessageId;
+ const inReplyTo = headers?.['in-reply-to'];
+ if (inReplyTo) {
+ headerMessageIds.push(
+ ...inReplyTo.map(item => ({
+ messageHeaderId,
+ replyToHeaderMessageId: sanitizeHeaderMessageId(item)
+ }))
+ );
+ }
+ }
+
+ const mindrs = headerMessageIds
+ .map(({ replyToHeaderMessageId, messageHeaderId }) => {
+ const mindr = selectByHeaderMessageId(replyToHeaderMessageId);
+ if (mindr) {
+ return { mindr, messageHeaderId };
+ } else return undefined;
+ })
+ .filter(Boolean);
+
+ await Promise.all(
+ mindrs.map(async ({ mindr, messageHeaderId }) => {
+ await store.dispatch(setReplyReceived(mindr, messageHeaderId));
+ })
+ );
+};
+
+if (thunderbirdVersion <= 121) {
+ browser.messages.onNewMailReceived.addListener(handleNewMailReceived);
+} else {
+ browser.messages.onNewMailReceived.addListener(
+ handleNewMailReceived,
+ false
+ );
+}
+
handleStartup();
diff -Nru mailmindr-1.4.0/scripts/mailmindr-message-script.js mailmindr-1.7.1/scripts/mailmindr-message-script.js
--- mailmindr-1.4.0/scripts/mailmindr-message-script.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/scripts/mailmindr-message-script.js 2024-08-04 22:14:20.000000000 +0200
@@ -1,4 +1,4 @@
-const removeExistingMessageBars = () => {
+var removeExistingMessageBars = () => {
const messageBars = document.querySelectorAll('.mailmindr-message-bar');
if (!messageBars) {
return;
@@ -11,10 +11,10 @@
/** @typedef {() => HTMLDivElement} getExistingessageBarFunction */
/** @type {getExistingessageBarFunction} */
-const getExistingMessageBar = () =>
+var getExistingMessageBar = () =>
document.querySelector('.mailmindr-message-bar');
-const createUI = (guid, dueDateTimeText) => {
+var createUI = (guid, dueDateTimeText) => {
const mailmindrBar = document.createElement('div');
mailmindrBar.className = 'mailmindr-message-bar';
@@ -54,6 +54,21 @@
});
});
+ const mailmindrRemoveBtn = document.createElement('button');
+ mailmindrRemoveBtn.className = 'mailmindr-button mailmindr-button--micro';
+ mailmindrRemoveBtn.innerText = browser.i18n.getMessage(
+ 'view.message-display.notification.button.remove'
+ );
+ mailmindrRemoveBtn.addEventListener('click', async () => {
+ await messenger.runtime.sendMessage({
+ action: 'do:mindr-action-remove',
+ payload: {
+ guid
+ }
+ });
+ });
+
+ mailmindrButtonWrapper.appendChild(mailmindrRemoveBtn);
mailmindrButtonWrapper.appendChild(mailmindrEditBtn);
mailmindrButtonWrapper.appendChild(mailmindrCloseBtn);
@@ -68,7 +83,7 @@
window.document.body.insertBefore(mailmindrBar, messageWrapper);
};
-const createMindrBar = async guid => {
+var createMindrBar = async guid => {
const mindrGuid = JSON.parse(guid)
.replace(/"/g, '')
.replace(/'/g, '');
diff -Nru mailmindr-1.4.0/views/dialogs/create-mindr/index.css mailmindr-1.7.1/views/dialogs/create-mindr/index.css
--- mailmindr-1.4.0/views/dialogs/create-mindr/index.css 2022-08-12 15:32:09.000000000 +0200
+++ mailmindr-1.7.1/views/dialogs/create-mindr/index.css 2024-08-04 22:14:20.000000000 +0200
@@ -23,7 +23,9 @@
}
input[type='checkbox'] {
- display: none;
+ /* display: none; */
+ height: 16px;
+ width: 16px;
}
input[type='checkbox'] + label {
@@ -34,7 +36,7 @@
color: silver;
}
-input[type='checkbox'] + label::before {
+/* input[type='checkbox'] + label::before {
content: '';
display: block;
float: left;
@@ -43,17 +45,19 @@
border: 1px solid rgba(12, 12, 13, 0.1);
border-radius: 2px;
background-color: var(--grey-90-a10);
-}
+} */
-input[type='checkbox']:checked + label::before {
+/* input[type='checkbox']:checked + label::before {
background-image: url(chrome://global/skin/icons/check.svg);
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAHklEQVQ4T2NkoBAwUqifYdQAhtEwACai0XQwGMIAACaYABGnE9aRAAAAAElFTkSuQmCC');
background-position: center;
background-repeat: no-repeat;
-}
+ background-color: blue;
+} */
-input[type='checkbox'] + label:hover::before {
+/* input[type='checkbox'] + label:hover::before {
background-color: var(--grey-90-a20);
-}
+} */
/* input[type='checkbox']:checked:hover + label::before {
background-color: var(--blue-70);
@@ -130,6 +134,15 @@
}
@media (prefers-color-scheme: dark) {
+ * {
+ color: #f9f9fa;
+ }
+
+ body {
+ color: #f9f9fa;
+ background-color: #2a2a2e;
+ }
+
input[type='text'],
input[type='number'],
select {
diff -Nru mailmindr-1.4.0/views/dialogs/create-mindr/index.js mailmindr-1.7.1/views/dialogs/create-mindr/index.js
--- mailmindr-1.4.0/views/dialogs/create-mindr/index.js 2022-08-12 15:32:09.000000000 +0200
+++ mailmindr-1.7.1/views/dialogs/create-mindr/index.js 2024-08-04 22:14:20.000000000 +0200
@@ -6,11 +6,11 @@
} from '../../../modules/core-utils.mjs.js';
import {
clearContents,
+ createRemindMeBeforePicker,
createTimePresetPicker,
selectDefaultActionPreset,
selectDefaultTimePreset
} from '../../../modules/ui-utils.mjs.js';
-import { pluralize } from '../../../modules/string-utils.mjs.js';
import {
createCorrelationId,
createLogger
@@ -104,37 +104,46 @@
} = getDialogParameters();
const remindMe = document.getElementById('mailmindr--preset-remind-me');
- const remindMeMinutesBefore =
- /** @type {HTMLInputElement} */ (remindMe).value || 0;
+ const remindMeMinutesBefore = parseInt(
+ /** @type {HTMLInputElement} */ (remindMe).value || '0',
+ 10
+ );
const due = getDateTimefromPickers();
- const actionTemplate = getActionTemplateFromPicker();
+ const actionTemplate = /** @type {} */ getActionTemplateFromPicker();
+ if (typeof remindMeMinutesBefore === 'number') {
+ if (remindMeMinutesBefore >= 0) {
+ actionTemplate.showReminder = true;
+ } else {
+ actionTemplate.showReminder = false;
+ }
+ }
const iceBox = /** @type {HTMLInputElement} */ (document.getElementById(
'mailmindr--icebox'
));
const doMoveToIcebox = iceBox.disabled ? false : iceBox.checked;
-
+ const payload = {
+ guid,
+ headerMessageId,
+ due,
+ remindMeMinutesBefore,
+ actionTemplate,
+ notes: '',
+ metaData: {
+ headerMessageId,
+ author,
+ subject,
+ folderAccountId,
+ folderName,
+ folderPath,
+ folderType,
+ folderAccountIdentityMailAddress
+ },
+ doMoveToIcebox
+ };
const result = await messenger.runtime.sendMessage({
action: 'mindr:create',
- payload: {
- guid,
- headerMessageId,
- due,
- remindMeMinutesBefore,
- actionTemplate,
- notes: '',
- metaData: {
- headerMessageId,
- author,
- subject,
- folderAccountId,
- folderName,
- folderPath,
- folderType,
- folderAccountIdentityMailAddress
- },
- doMoveToIcebox
- }
+ payload
});
doCancel();
};
@@ -280,7 +289,7 @@
const {
payload: {
settings,
- presets: { time, actions },
+ presets: { time, actions, remindMeMinutesBefore },
mindr
}
} = result;
@@ -325,7 +334,15 @@
const moveToIceBox = /** @type {HTMLInputElement} */ (document.getElementById(
'mailmindr--icebox'
));
- moveToIceBox.disabled = editMindr || !isIceboxFolderSet;
+
+ if (editMindr) {
+ moveToIceBox.disabled = true;
+ } else if (!isIceboxFolderSet) {
+ moveToIceBox.disabled = true;
+ } else {
+ moveToIceBox.disabled = false;
+ }
+
moveToIceBox.checked = editMindr
? sourceFolderIsIceboxFolder
: isIceboxFolderSet;
@@ -425,46 +442,13 @@
)} | mailmindr`;
}
- const remindMeMinutesBefore = [
- { minutes: 0, display: 0, unit: 'on-time' },
- { minutes: 5, display: 5, unit: 'minutes' },
- { minutes: 15, display: 15, unit: 'minutes' },
- { minutes: 30, display: 30, unit: 'minutes' },
- { minutes: 60, display: 1, unit: 'hours' },
- { minutes: 120, display: 2, unit: 'hours' },
- { minutes: 240, display: 4, unit: 'hours' },
- { minutes: -1, display: null, unit: 'no-reminder' }
- ];
const remindMe = document.getElementById('mailmindr--preset-remind-me');
- remindMeMinutesBefore.forEach(item => {
- const option = document.createElement('option');
- const { minutes, unit, display: displayedValue } = item;
-
- option.value = String(minutes);
-
- if (unit === 'on-time') {
- option.innerText = pluralize(
- displayedValue,
- 'mailmindr.utils.core.remindme.before.on-time'
- );
- } else if (unit === 'minutes') {
- option.innerText = pluralize(
- displayedValue,
- 'mailmindr.utils.core.remindme.before.minutes'
- );
- } else if (unit === 'hours') {
- option.innerText = pluralize(
- displayedValue,
- 'mailmindr.utils.core.remindme.before.hours'
- );
- } else if (unit === 'no-reminder') {
- option.innerText = pluralize(
- displayedValue,
- 'mailmindr.utils.core.remindme.before.no-reminder'
- );
- }
- remindMe.appendChild(option);
- });
+ createRemindMeBeforePicker(
+ document,
+ remindMe,
+ remindMeMinutesBefore,
+ settings.defaultRemindMeMinutesBefore
+ );
translateDocument(document);
diff -Nru mailmindr-1.4.0/views/dialogs/mindr-alert/index.js mailmindr-1.7.1/views/dialogs/mindr-alert/index.js
--- mailmindr-1.4.0/views/dialogs/mindr-alert/index.js 2022-08-12 15:32:09.000000000 +0200
+++ mailmindr-1.7.1/views/dialogs/mindr-alert/index.js 2024-08-04 22:14:20.000000000 +0200
@@ -24,6 +24,15 @@
//
+const openMindrByGuid = async guid => {
+ await messenger.runtime.sendMessage({
+ action: 'navigate:open-message-by-mindr-guid',
+ payload: {
+ guid
+ }
+ });
+};
+
const onMindrClick = mindr => {
const headerItem = document.getElementsByTagName('header').item(0);
[...headerItem.childNodes].forEach(child => child.remove());
@@ -31,12 +40,7 @@
headerItem.appendChild(item);
item.addEventListener('click', async () => {
- await messenger.runtime.sendMessage({
- action: 'navigate:open-message-by-mindr-guid',
- payload: {
- guid: mindr.guid
- }
- });
+ await openMindrByGuid(mindr.guid);
});
selectListItem(mindr);
@@ -105,6 +109,7 @@
mindrs.forEach(mindr => {
const item = createListItem(mindr);
item.onclick = () => onMindrClick(mindr);
+ item.ondblclick = () => openMindrByGuid(mindr.guid);
listElement.appendChild(item);
});
};
@@ -187,7 +192,7 @@
const { active, overdue } = message;
const selectedGuid = findSelectedGuid();
- logger.log(`active: `, active, `overdue: `, overdue);
+ logger.log(`alert -- active/overdue: `, { active, overdue });
const mindrs = [...overdue, ...active].filter(
(item, index, list) =>
list.findIndex(value => value.guid === item.guid) === index
@@ -209,7 +214,7 @@
name: 'connection:mindr-alert'
});
- await port.postMessage({
+ const _result = await port.postMessage({
action: 'dialog:open',
payload: { name: 'mindr-alert' }
});
@@ -255,6 +260,7 @@
};
document.addEventListener('DOMContentLoaded', onLoad, { once: true });
+
document.addEventListener('keydown', async event => {
if (event.code === 'Escape') {
clearList();
diff -Nru mailmindr-1.4.0/views/options/index.html mailmindr-1.7.1/views/options/index.html
--- mailmindr-1.4.0/views/options/index.html 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/views/options/index.html 2024-08-04 22:14:20.000000000 +0200
@@ -144,6 +144,25 @@
<div class="mailmindr-options mailmindr-options--oneline">
<div>
<label
+ data-i18n="view.options.default-reminder-preset.label"
+ ></label>
+ <span
+ class="mailmindr-option-description"
+ id="mailmindr-options_default-reminder-preset--description"
+ data-i18n="view.options.default-reminder-preset.description"
+ >
+ </span>
+ </div>
+ <div>
+ <select
+ id="mailmindr-options_default-reminder-preset"
+ >
+ </select>
+ </div>
+ </div>
+ <div class="mailmindr-options mailmindr-options--oneline">
+ <div>
+ <label
data-i18n="view.options.default-icebox-folder.label"
></label>
<span
diff -Nru mailmindr-1.4.0/views/options/index.js mailmindr-1.7.1/views/options/index.js
--- mailmindr-1.4.0/views/options/index.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/views/options/index.js 2024-08-04 22:14:20.000000000 +0200
@@ -5,6 +5,7 @@
} from '../../modules/core-utils.mjs.js';
import {
clearContents,
+ createRemindMeBeforePicker,
createTimePresetPicker,
selectDefaultActionPreset,
selectDefaultTimePreset,
@@ -147,7 +148,7 @@
const displaySettings = async options => {
const { presets, settings } = options;
- const { time, actions } = presets;
+ const { time, actions, remindMeMinutesBefore } = presets;
displayTimePresets(time);
@@ -156,6 +157,7 @@
defaultTimePreset,
defaultActionPreset,
defaultIceboxFolder,
+ defaultRemindMeMinutesBefore,
snoozeTime
} = settings;
@@ -177,6 +179,16 @@
selectDefaultActionPreset(defaultActionPresetPicker, defaultActionPreset);
+ const defaultRemindMeBeforePicker = document.getElementById(
+ 'mailmindr-options_default-reminder-preset'
+ );
+ createRemindMeBeforePicker(
+ document,
+ defaultRemindMeBeforePicker,
+ remindMeMinutesBefore,
+ defaultRemindMeMinutesBefore
+ );
+
//
//
//
@@ -201,7 +213,10 @@
const defaultIceBoxPicker = findIceboxPickerForIdentity('default');
if (defaultIceBoxPicker && defaultIceboxFolder) {
- selectDefaultIceboxFolder(defaultIceBoxPicker, defaultIceboxFolder);
+ selectDefaultIceboxFolder(
+ defaultIceBoxPicker,
+ defaultIceboxFolder.folder
+ );
}
};
@@ -228,8 +243,6 @@
const onMessage = async message => {
const { topic = '' } = message;
- logger.warn(`received topic: '${topic}'`);
-
switch (topic) {
case 'settings:updated': {
const {
@@ -380,6 +393,15 @@
logger.log(`select default action preset:`, value);
});
+ const defaultRemindMeBeforePicker = document.getElementById(
+ 'mailmindr-options_default-reminder-preset'
+ );
+ defaultRemindMeBeforePicker.addEventListener('change', async sender => {
+ const value = JSON.parse(sender.target.value);
+ logger.log(`select default remindMeMinutesBefore preset:`, value);
+ await set('defaultRemindMeMinutesBefore', value);
+ });
+
//
//
//
@@ -433,9 +455,9 @@
});
};
-document.addEventListener('DOMContentLoaded', async () => {
- await onLoad();
-});
+//
+//
+//
const createIceboxSettingLabel = (text, uniqueId) => {
const label = document.createElement('label');
@@ -462,7 +484,10 @@
const value = JSON.parse(sender.target.value);
const accountIdentity = sender.srcElement.dataset.accountIdentity;
if (accountIdentity === 'default') {
- await set('defaultIceboxFolder', { accountIdentity, value });
+ await set('defaultIceboxFolder', {
+ accountIdentity,
+ folder: value
+ });
} else {
await set('setIceBoxFolder', { accountIdentity, folder: value });
}
@@ -479,3 +504,5 @@
return iceBoxPicker;
};
+
+onLoad();
diff -Nru mailmindr-1.4.0/views/popups/create-outgoing-mindr/index.css mailmindr-1.7.1/views/popups/create-outgoing-mindr/index.css
--- mailmindr-1.4.0/views/popups/create-outgoing-mindr/index.css 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/views/popups/create-outgoing-mindr/index.css 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,173 @@
+ at import url('../../mailmindr-core.css');
+
+html {
+ padding: 0.5em;
+}
+
+body.mailmindr-inline-popup main * {
+ font-size: 13px;
+}
+
+body {
+ /* min-height: 320px; */
+ /* min-width: 430px; */
+ background-color: -moz-dialog;
+ /* -moz-default-background-color; */
+}
+
+h1 {
+ margin: 0 0 8px;
+ font-size: 22px;
+ font-weight: 300;
+ line-height: 1.3em;
+}
+
+label {
+ user-select: none;
+}
+
+input[type='checkbox'] {
+ /* display: none; */
+ height: 16px;
+ width: 16px;
+}
+
+input[type='checkbox'] + label {
+ padding: 0 0.5em;
+}
+
+input[type='checkbox']:disabled + label {
+ color: silver;
+}
+
+/* input[type='checkbox'] + label::before {
+ content: '';
+ display: block;
+ float: left;
+ height: 16px;
+ width: 16px;
+ border: 1px solid rgba(12, 12, 13, 0.1);
+ border-radius: 2px;
+ background-color: var(--grey-90-a10);
+} */
+
+/* input[type='checkbox']:checked + label::before {
+ background-image: url(chrome://global/skin/icons/check.svg);
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAHklEQVQ4T2NkoBAwUqifYdQAhtEwACai0XQwGMIAACaYABGnE9aRAAAAAElFTkSuQmCC');
+ background-position: center;
+ background-repeat: no-repeat;
+ background-color: blue;
+} */
+
+/* input[type='checkbox'] + label:hover::before {
+ background-color: var(--grey-90-a20);
+} */
+
+/* input[type='checkbox']:checked:hover + label::before {
+ background-color: var(--blue-70);
+} */
+
+input[type='date'],
+input[type='time'] {
+ font-size: 100%;
+ padding: 4px 8px;
+ margin-bottom: 5px;
+ width: 100%;
+}
+
+main {
+ display: flex;
+ flex-direction: column;
+}
+
+.mailmindr-dialog {
+ display: grid;
+ /* grid-template-columns: 100px auto; */
+ grid-template-columns: auto;
+ column-gap: 10px;
+ row-gap: 4px;
+ grid-auto-flow: row;
+}
+
+.mailmindr-fieldwrapper--caption {
+ font-weight: bold;
+ min-height: 21px;
+}
+
+.mailmindr-fieldwrapper--caption > label {
+ vertical-align: -webkit-baseline-middle;
+}
+
+.mailmindr-fieldwrapper--content {
+ min-width: 0;
+}
+
+.mailmindr-fieldwrapper--content > label {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ display: block;
+}
+
+.mailmindr-buttonbar {
+ margin: 0.5em -0.25em;
+ color: initial;
+ /* grid-column: 2; */
+ display: flex;
+ justify-content: flex-end;
+ /* width: 100%; */
+
+ /* bottom: 0;
+ position: fixed;
+ left: 0;
+ right: 0; */
+}
+
+.mailmindr-buttonbar--wrapper {
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+}
+
+button {
+ text-align: center;
+}
+
+select {
+ color: inherit !important;
+ border: 1px solid transparent;
+ border-radius: 2px;
+ min-height: 30px;
+ font-weight: 400;
+ padding: 0 8px;
+ text-decoration: none;
+ font-size: 1em;
+ background-color: rgba(12, 12, 13, 0.1);
+ width: 100%;
+}
+
+ at media (prefers-color-scheme: dark) {
+ * {
+ color: #f9f9fa;
+ }
+
+ body {
+ color: #f9f9fa;
+ background-color: #2a2a2e;
+ }
+
+ input[type='text'],
+ input[type='number'],
+ select {
+ border-color: rgba(249, 249, 250, 0.2);
+ }
+
+ input[type='date'],
+ input[type='time'] {
+ color: #f9f9fa;
+ background-color: #2a2a2e;
+ border-color: rgba(249, 249, 250, 0.2);
+ border-width: 1px;
+ border-radius: 2px;
+ }
+}
diff -Nru mailmindr-1.4.0/views/popups/create-outgoing-mindr/index.html mailmindr-1.7.1/views/popups/create-outgoing-mindr/index.html
--- mailmindr-1.4.0/views/popups/create-outgoing-mindr/index.html 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/views/popups/create-outgoing-mindr/index.html 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" href="chrome://global/locale/global.css" />
+ <link rel="stylesheet" href="./index.css" />
+ <title>mailmindr</title>
+ </head>
+ <body class="mailmindr-inline-popup">
+ <header>
+ <h1 data-i18n="view.dialog.create-outgoing-mindr.header"></h1>
+ </header>
+ <main>
+ <form class="mailmindr-dialog">
+ <div class="mailmindr-fieldwrapper--caption">
+ <label
+ data-i18n="view.dialog.create-outgoing-mindr.label.due"
+ ></label>
+ </div>
+ <div class="mailmindr-fieldwrapper--content">
+ <div>
+ <select id="mailmindr--preset-time"></select>
+ </div>
+ </div>
+ <div></div>
+ <div class="mailmindr-fieldwrapper--content">
+ <div>
+ <input id="mailmindr--date-picker" type="date" />
+ <input id="mailmindr--time-picker" type="time" />
+ </div>
+ </div>
+ <div class="mailmindr-fieldwrapper--caption">
+ <label
+ data-i18n="view.dialog.create-outgoing-mindr.label.action"
+ ></label>
+ </div>
+ <div class="mailmindr-fieldwrapper--content">
+ <select id="mailmindr--preset-action"></select>
+ </div>
+ <div class="mailmindr-fieldwrapper--caption">
+ <label
+ data-i18n="view.dialog.create-outgoing-mindr.label.remind-me"
+ ></label>
+ </div>
+ <div class="mailmindr-fieldwrapper--content">
+ <select id="mailmindr--preset-remind-me"></select>
+ </div>
+ </form>
+ </main>
+ <footer class="mailmindr-buttonbar">
+ <div class="mailmindr-buttonbar--wrapper">
+ <button
+ style="display: none;"
+ class="mailmindr-button mailmindr-button--remove"
+ id="mailmindr--do-remove-mindr"
+ data-i18n="view.dialog.create-outgoing-mindr.caption.remove-follow-up"
+ ></button>
+ <button
+ class="mailmindr-button"
+ id="mailmindr--do-create-mindr"
+ type="submit"
+ data-i18n="view.dialog.create-outgoing-mindr.caption.create-mindr"
+ ></button>
+ <button
+ class="mailmindr-button"
+ id="mailmindr--do-cancel-create-mindr"
+ data-i18n="view.dialog.create-outgoing-mindr.caption.cancel"
+ ></button>
+ </div>
+ </footer>
+ <script type="module" src="./index.js"></script>
+ </body>
+</html>
diff -Nru mailmindr-1.4.0/views/popups/create-outgoing-mindr/index.js mailmindr-1.7.1/views/popups/create-outgoing-mindr/index.js
--- mailmindr-1.4.0/views/popups/create-outgoing-mindr/index.js 1970-01-01 01:00:00.000000000 +0100
+++ mailmindr-1.7.1/views/popups/create-outgoing-mindr/index.js 2024-08-04 22:14:20.000000000 +0200
@@ -0,0 +1,417 @@
+import {
+ genericFoldersAreEqual,
+ getGenericIceboxFolderForIdentityOrNull,
+ getParsedJsonOrDefaultValue,
+ localFolderToGenericFolder
+} from '../../../modules/core-utils.mjs.js';
+import {
+ clearContents,
+ createRemindMeBeforePicker,
+ createTimePresetPicker,
+ isDarkMode,
+ selectDefaultActionPreset,
+ selectDefaultTimePreset
+} from '../../../modules/ui-utils.mjs.js';
+import {
+ createCorrelationId,
+ createLogger
+} from '../../../modules/logger.mjs.js';
+import { translateDocument } from '../../../modules/ui-utils.mjs.js';
+
+const logger = createLogger('views/popups/create-outgoing-mindr');
+
+const getDialogParameters = () => {
+ const correlationId = createCorrelationId(`getDialogParameters`);
+ logger.log('BEGIN getDialogParameters', { correlationId });
+ const params = new URLSearchParams(window.location.search.substr(1));
+ const result = {
+ dialogId: params.get('dialogId'),
+ headerMessageId: params.get('headerMessageId'),
+ author: params.get('author'),
+ subject: params.get('subject'),
+ folderAccountId: params.get('folderAccountId'),
+ folderName: params.get('folderName'),
+ folderPath: params.get('folderPath'),
+ folderType: params.get('folderType'),
+ guid: params.get('guid'),
+ folderAccountIdentityMailAddress: params.get(
+ 'folderAccountIdentityMailAddress'
+ )
+ };
+
+ logger.log('dialog result', { correlationId, result });
+ logger.log('END getDialogParameters', { correlationId });
+
+ return result;
+};
+
+const closePopup = async () => {
+ const correlationId = createCorrelationId('closePopup');
+ logger.log(`BEGIN closePopup`, { correlationId });
+ logger.log(`try to get dialog parameters`);
+ try {
+ const { dialogId } = getDialogParameters();
+ logger.log(`closing dialog ${dialogId}`);
+ window.close();
+ } catch (e) {
+ logger.error('create-outgoing-mindr::closePopup', e);
+ }
+ logger.log('END closePopup', { correlationId });
+};
+
+const doCancel = async () => await closePopup();
+
+const resetComposeActionIconAndLabel = async id => {
+ const path = isDarkMode()
+ ? '/images/mailmindr-flag--white.svg'
+ : '/images/mailmindr-flag.svg';
+ await Promise.all([
+ messenger.composeAction.setIcon({
+ path,
+ tabId: id
+ }),
+ messenger.composeAction.setLabel({
+ label: messenger.i18n.getMessage('mailmindrComposeMessageButton'),
+ tabId: id
+ })
+ ]);
+};
+
+const handleChangeTimePreset = event => {
+ const {
+ target: { value }
+ } = event;
+
+ const preset = getParsedJsonOrDefaultValue(value, {});
+ const { days, hours, minutes, isRelative, isSelectable } = preset;
+
+ if (!isSelectable) {
+ logger.log('nothing to change here');
+ return;
+ }
+
+ const millisecondsForDays = days * 24 * 60 * 60 * 1000;
+ let now = Date.now() + millisecondsForDays;
+
+ if (isRelative) {
+ now +=
+ hours * 60 * 60 * 1000 /* add hours */ +
+ minutes * 60 * 1000; /* add minutes */
+ }
+
+ const nowAsDate = new Date(now);
+ const newDate = new Date(
+ Date.UTC(
+ nowAsDate.getFullYear(),
+ nowAsDate.getMonth(),
+ nowAsDate.getDate()
+ )
+ );
+ /** @type {HTMLInputDateElement} */
+ const datePicker = document.getElementById('mailmindr--date-picker');
+ /** @type {HTMLInputTimeElement} */
+ const timePicker = document.getElementById('mailmindr--time-picker');
+
+ datePicker.valueAsDate = newDate;
+
+ if (isRelative) {
+ const hourString = `0${nowAsDate.getHours()}`.substr(-2);
+ const minuteString = `0${nowAsDate.getMinutes()}`.substr(-2);
+ const newTimePickerValue = `${hourString}:${minuteString}`;
+
+ timePicker.value = newTimePickerValue;
+ } else {
+ const hourString = `0${hours}`.substr(-2);
+ const minuteString = `0${minutes}`.substr(-2);
+ const newTimePickerValue = `${hourString}:${minuteString}`;
+
+ timePicker.value = newTimePickerValue;
+
+ const pickedDateTime = getDateTimefromPickers();
+
+ logger.info(pickedDateTime, new Date());
+
+ if (pickedDateTime <= new Date()) {
+ logger.info(
+ 'adjusting time by adding a day, adjusted to: ',
+ getDateTimefromPickers()
+ );
+ //
+ datePicker.valueAsDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
+ }
+ }
+};
+
+const getDateTimefromPickers = () => {
+ /** @type {HTMLInputDateElement} */
+ const datePicker = document.getElementById('mailmindr--date-picker');
+ /** @type {HTMLInputTimeElement} */
+ const timePicker = document.getElementById('mailmindr--time-picker');
+
+ const timePickerValue = timePicker.value;
+ const [hours, minutes] = timePickerValue
+ .split(':')
+ .map(item => Number.parseInt(item, 10));
+
+ const [year, month, day] = datePicker.value.split('-');
+ const newDate = new Date(year, month - 1, day, hours, minutes, 0, 0);
+
+ logger.warn(
+ 'current date from datepicker without adjusting time and timezone',
+ newDate
+ );
+
+ return newDate;
+};
+
+const getActionTemplateFromPicker = () => {
+ const actionTemplatePicker = /** @type {HTMLInputElement} */ (document.getElementById(
+ 'mailmindr--preset-action'
+ ));
+ const actionTemplate = JSON.parse(actionTemplatePicker.value);
+
+ return actionTemplate;
+};
+
+const doRemove = async () => {
+ const { windowId, id } = await messenger.tabs.getCurrent();
+ const sender = { windowId, id };
+ const removed = await messenger.runtime.sendMessage({
+ action: 'mindr:remove-outgoing-mindr',
+ payload: { sender }
+ });
+
+ if (removed && removed.status && removed.status === 'ok') {
+ await resetComposeActionIconAndLabel(id);
+ doCancel();
+ }
+};
+
+const onLoad = async () => {
+ const { windowId, id } = await messenger.tabs.getCurrent();
+ const result = await messenger.runtime.sendMessage({
+ action: 'dialog:open',
+ payload: { name: 'set-outgoing-mindr', sender: { windowId, id } }
+ });
+
+ const {
+ payload: {
+ settings,
+ presets: { time, actions, remindMeMinutesBefore },
+ draft
+ }
+ } = result;
+
+ const editMindr = Boolean(draft);
+ const removeButton = document.getElementById('mailmindr--do-remove-mindr');
+ removeButton.addEventListener('click', doRemove);
+ removeButton.setAttribute(
+ 'style',
+ editMindr ? 'display: block;' : 'display: none;'
+ );
+
+ const cancelButton = document.getElementById(
+ 'mailmindr--do-cancel-create-mindr'
+ );
+ cancelButton.addEventListener('click', doCancel);
+
+ const acceptButton = document.getElementById('mailmindr--do-create-mindr');
+ acceptButton.addEventListener('click', async (
+ source,
+ /** @type{ MouseEvent} */ event
+ ) => {
+ const sender = await messenger.windows.getLastFocused({
+ windowTypes: ['messageCompose']
+ });
+ doAccept(sender);
+ });
+
+ /** @type {HTMLInputDateElement} */
+ const datePicker = document.getElementById('mailmindr--date-picker');
+ /** @type {HTMLInputTimeElement} */
+ const timePicker = document.getElementById('mailmindr--time-picker');
+
+ const now = new Date();
+ const hours = `0${now.getHours()}`.substr(-2);
+ const minutes = `0${now.getMinutes()}`.substr(-2);
+
+ timePicker.value = [hours, minutes].join(':');
+ datePicker.valueAsDate = now;
+
+ const actionPresets = document.getElementById('mailmindr--preset-action');
+ actions.map(actionPreset => {
+ const actionOption = document.createElement('option');
+ actionOption.value = JSON.stringify(actionPreset);
+ actionOption.innerText = actionPreset.text;
+
+ actionPresets.appendChild(actionOption);
+ });
+
+ const timePresets = createTimePresetPicker(
+ document.getElementById('mailmindr--preset-time'),
+ time
+ );
+ const handlePickerChange = () => {
+ const presetTimePicker = /** @type {HTMLSelectElement} */ (document.getElementById(
+ 'mailmindr--preset-time'
+ ));
+ presetTimePicker.selectedIndex = 0;
+ };
+
+ datePicker.addEventListener('change', handlePickerChange);
+ timePicker.addEventListener('change', handlePickerChange);
+ timePresets.addEventListener('change', handleChangeTimePreset);
+
+ if (editMindr) {
+ const { mindr } = draft;
+ const due = mindr.due;
+ const dueHours = `0${due.getHours()}`.substr(-2);
+ const dueMinutes = `0${due.getMinutes()}`.substr(-2);
+
+ timePicker.value = [dueHours, dueMinutes].join(':');
+ datePicker.valueAsDate = due;
+
+ timePresets.selectedIndex = 0;
+
+ const preselectedAction = mindr?.actionTemplate
+ ? { ...mindr?.actionTemplate, moveMessageTo: undefined }
+ : undefined;
+
+ selectDefaultActionPreset(actionPresets, preselectedAction);
+ } else {
+ const defaultTimePreset = settings?.defaultTimePreset;
+ const defaultActionPreset = settings?.defaultActionPreset;
+
+ selectDefaultTimePreset(timePresets, defaultTimePreset);
+ selectDefaultActionPreset(actionPresets, defaultActionPreset);
+
+ const changeEvent = new Event('change');
+ timePresets.dispatchEvent(changeEvent);
+ }
+
+ const remindMe = document.getElementById('mailmindr--preset-remind-me');
+ //
+ createRemindMeBeforePicker(
+ document,
+ remindMe,
+ remindMeMinutesBefore,
+ settings.defaultRemindMeMinutesBefore
+ );
+
+ translateDocument(document);
+};
+
+document.addEventListener('DOMContentLoaded', onLoad, { once: true });
+document.addEventListener('keydown', async event => {
+ if (event.code === 'Escape') {
+ await doCancel();
+ }
+});
+
+const doAccept = async (/** @type {Window} */ sender) => {
+ //
+ const remindMe = document.getElementById('mailmindr--preset-remind-me');
+ const remindMeMinutesBefore = parseInt(
+ /** @type {HTMLInputElement} */ (remindMe).value || '0',
+ 10
+ );
+
+ const due = getDateTimefromPickers();
+ const actionTemplate = getActionTemplateFromPicker();
+
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+
+ //
+
+ const {
+ guid,
+ headerMessageId,
+ author,
+ subject,
+ folderAccountId,
+ folderName,
+ folderPath,
+ folderType,
+ folderAccountIdentityMailAddress
+ } = getDialogParameters();
+
+ if (remindMeMinutesBefore) {
+ if (remindMeMinutesBefore >= 0) {
+ actionTemplate.showReminder = true;
+ } else {
+ actionTemplate.showReminder = false;
+ }
+ }
+
+ const { windowId, id } = await messenger.tabs.getCurrent();
+ const payload = {
+ mindr: {
+ guid,
+ headerMessageId,
+ due,
+ remindMeMinutesBefore,
+ actionTemplate,
+ notes: '',
+ metaData: {
+ headerMessageId,
+ author,
+ subject,
+ folderAccountId,
+ folderName,
+ folderPath,
+ folderType,
+ folderAccountIdentityMailAddress
+ },
+ isWaitingForReply: true,
+ doMoveToIcebox: false
+ },
+ sender: { windowId, id }
+ };
+ const result = await messenger.runtime.sendMessage({
+ action: 'mindr:create-outgoing',
+ payload
+ });
+ if (result && result.status === 'ok') {
+ //
+ const path = isDarkMode()
+ ? '/images/mailmindr-flag_marker--white.svg'
+ : '/images/mailmindr-flag_marker.svg';
+ const currentLocale = navigator.language;
+ const formattedTime = new Intl.DateTimeFormat(currentLocale, {
+ timeStyle: 'short'
+ }).format(due);
+ const formattedDate = new Intl.DateTimeFormat(currentLocale, {
+ dateStyle: 'short'
+ }).format(due);
+ const formattedDateAndTime = new Intl.DateTimeFormat(currentLocale, {
+ dateStyle: 'medium',
+ timeStyle: 'short'
+ }).format(due);
+
+ await Promise.all([
+ messenger.composeAction.setIcon({
+ path,
+ tabId: id
+ }),
+ messenger.composeAction.setLabel({
+ label: messenger.i18n.getMessage(
+ 'mailmindrComposeMessageButton.detailed',
+ [formattedDate, formattedTime, formattedDateAndTime]
+ ),
+ tabId: id
+ })
+ ]);
+
+ doCancel();
+ } else {
+ await resetComposeActionIconAndLabel(id);
+ }
+};
diff -Nru mailmindr-1.4.0/views/popups/list-all/index.css mailmindr-1.7.1/views/popups/list-all/index.css
--- mailmindr-1.4.0/views/popups/list-all/index.css 2022-08-12 15:32:09.000000000 +0200
+++ mailmindr-1.7.1/views/popups/list-all/index.css 2024-08-04 22:14:20.000000000 +0200
@@ -23,15 +23,28 @@
text-align: center;
}
+button.mailmindr-button--micro {
+ font-size: 12px;
+ height: unset;
+ min-height: 21px;
+}
+
button:hover {
border-color: transparent;
}
.mailmindr-popup-header {
border-bottom: 0.5px solid silver;
- padding: 0.75em 1em;
+ /* padding: 0.75em 1em; */
+ padding: 0.25em 0.5em;
text-align: center;
- background: linear-gradient(45deg, var(--purple-40), var(--purple-50));
+ /* background: linear-gradient(45deg, var(--purple-40), var(--purple-50)); */
+ background-color: #eee;
+}
+
+.mailmindr-popup-header button {
+ margin: 0.25em;
+ min-height: 23px;
}
.mailmindr-popup-header a {
@@ -195,11 +208,53 @@
color: var(--grey-90);
}
+.mailmindr-list-label-wrapper {
+ padding-left: 12px;
+ margin-bottom: 2px;
+}
+
+.mailmindr-list-label {
+ border-radius: 4px;
+ color: var(--grey-90);
+ text-transform: uppercase;
+ padding: 2px 6px;
+ margin: 2px;
+ font-size: smaller;
+ background-color: var(--yellow-50);
+ display: inline;
+}
+
+.mailmindr-list-label--awaiting::before {
+ content: '';
+ height: 12px;
+ width: 12px;
+ background-image: url(chrome://messenger/skin/icons/hourglass.svg);
+ background-repeat: no-repeat;
+ background-position: center center;
+ padding: 10px;
+}
+.mailmindr-list-label--awaiting-reply-received {
+ text-decoration: line-through;
+}
+
@media (prefers-color-scheme: dark) {
+ * {
+ color: #f9f9fa;
+ }
+
+ body {
+ color: #f9f9fa;
+ background-color: #2a2a2e;
+ }
+
footer {
background-color: var(--grey-90-a80);
}
+ .mailmindr-popup-header {
+ background-color: var(--grey-90-a80);
+ }
+
.mailmindr-list-placeholder p {
color: var(--grey-10);
}
diff -Nru mailmindr-1.4.0/views/popups/list-all/index.html mailmindr-1.7.1/views/popups/list-all/index.html
--- mailmindr-1.4.0/views/popups/list-all/index.html 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/views/popups/list-all/index.html 2024-08-04 22:14:20.000000000 +0200
@@ -34,11 +34,30 @@
</ul> -->
<main>
<div class="mailmindr-popup-header">
+ <!--
<a
href="https://mailmindr.net/release-notes"
target="_blank"
data-i18n="view.popups.list-all.link.whats.new"
></a>
+ !-->
+ <label>
+ <span aria-hidden="true">
+ Filter:
+ </span>
+ <input
+ id="mailmindr--search-field"
+ type="text"
+ aria-label="Filter follow-ups"
+ />
+ </label>
+ <button
+ class="mailmindr-button mailmindr-button--micro"
+ aria-label="Clear filter"
+ id="mailmindr--search-field-clear"
+ >
+ <span aria-hidden="true" title="Clear filter">?</span>
+ </button>
</div>
<ul id="mailmindr--list" class="mailmindr-list" tabindex="0">
<div class="mailmindr-list-placeholder">
diff -Nru mailmindr-1.4.0/views/popups/list-all/index.js mailmindr-1.7.1/views/popups/list-all/index.js
--- mailmindr-1.4.0/views/popups/list-all/index.js 2022-08-12 15:32:08.000000000 +0200
+++ mailmindr-1.7.1/views/popups/list-all/index.js 2024-08-04 22:14:20.000000000 +0200
@@ -5,6 +5,7 @@
import { createLogger } from '../../../modules/logger.mjs.js';
import { webHandler } from '../../../modules/ui-utils.mjs.js';
import { translateDocument } from '../../../modules/ui-utils.mjs.js';
+import { hasReply } from '../../../modules/core-utils.mjs.js';
let currentMindrGuid = null;
@@ -38,11 +39,18 @@
window.close();
};
+/**
+ * Render item markup for a mindr
+ *
+ * @param {Mindr} mindr
+ * @returns HTMLElement
+ */
const createMindrItem = mindr => {
const {
guid,
due,
- metaData: { author, subject }
+ metaData: { author, subject },
+ isWaitingForReply = false
} = mindr;
const relative = due - Date.now();
const item = document.createElement('li');
@@ -111,6 +119,28 @@
contentWrapper.appendChild(authorWrapper);
contentWrapper.appendChild(dueWrapper);
+ if (isWaitingForReply) {
+ const markerWrapper = document.createElement('div');
+ const markerItem = document.createElement('div');
+
+ markerWrapper.className = 'mailmindr-list-label-wrapper';
+
+ markerItem.classList.add('mailmindr-list-label');
+ markerItem.classList.add('mailmindr-list-label--awaiting');
+
+ if (hasReply(mindr)) {
+ markerItem.classList.add(
+ 'mailmindr-list-label--awaiting-reply-received'
+ );
+ }
+
+ const markerLabel = document.createTextNode('awaiting reply');
+
+ markerItem.appendChild(markerLabel);
+ markerWrapper.appendChild(markerItem);
+ contentWrapper.appendChild(markerWrapper);
+ }
+
item.appendChild(contentWrapper);
item.addEventListener('click', async () => openMessageByGuid(guid));
item.addEventListener('mouseover', async () => {
@@ -155,26 +185,16 @@
const selectNextElement = (listElement, guid, direction) => {
const children = Array.from(listElement.children);
if (guid) {
- /** @type {HTMLLIElement} */
- const selectedElement = document.querySelector(
- `li[data-guid='${guid}'`
- );
toggleItemSelection(guid, false);
- let nextElement = selectedElement;
const index = children.findIndex(
element => element.dataset.guid === guid
);
- if (direction !== 0) {
- if (index === children.length - 1) {
- nextElement = children[0];
- } else if (index === 0 && direction < 0) {
- nextElement = children[children.length - 1];
- } else {
- nextElement = children[index + direction];
- }
- }
+ const newIndex =
+ (index + children.length + direction) % children.length;
+ const nextElement = children[newIndex];
+
currentMindrGuid = nextElement.dataset.guid;
toggleItemSelection(currentMindrGuid, true);
} else {
@@ -210,6 +230,7 @@
if (mindrs.length) {
clearList(listElement);
displayList(listElement, mindrs);
+ selectNextElement(listElement, mindrs[0].guid, 0);
//
/** @type {HTMLElement} */
@@ -233,6 +254,45 @@
//
}
+ const searchField = /** @type {HTMLInputElement} */ document.getElementById(
+ 'mailmindr--search-field'
+ );
+ searchField.addEventListener('keydown', event => {
+ const { code, shiftKey, metaKey, altKey, ctrlKey } = event;
+
+ const text = searchField.value;
+ let items = mindrs;
+ if (code === 'Delete' && shiftKey && !metaKey && !altKey && !ctrlKey) {
+ //
+ searchField.value = '';
+ } else if (text.length >= 3) {
+ items = mindrs.filter(item => {
+ const {
+ metaData: { author, subject }
+ } = item;
+ return (
+ (author || '').toLowerCase().indexOf(text) >= 0 ||
+ (subject || '').toLowerCase().indexOf(text) >= 0
+ );
+ });
+ }
+
+ clearList(listElement);
+ displayList(listElement, items);
+ if (items && items.length) {
+ selectNextElement(listElement, items[0].guid, 0);
+ }
+ });
+
+ const clearFilterButton = document.getElementById(
+ 'mailmindr--search-field-clear'
+ );
+ clearFilterButton.addEventListener('click', () => {
+ searchField.value = '';
+ clearList(listElement);
+ displayList(listElement, mindrs);
+ });
+
Array.from(
document.querySelectorAll('button.mailmindr-button-web')
).forEach(btn => {
More information about the Pkg-mozext-maintainers
mailing list