From 1e471655dcc3e16edda3892b6c60ed9a7316a7e6 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 25 Feb 2025 15:06:57 +0000 Subject: [PATCH] susidns: Add bubble support --- .../java/src/net/i2p/addressbook/Daemon.java | 14 ++++++++-- apps/routerconsole/java/bundle-messages.sh | 1 + .../src/java/src/i2p/susi/dns/BaseBean.java | 22 +++++++++++++++ .../src/i2p/susi/dns/NamingServiceBean.java | 13 +++++++++ apps/susidns/src/jsp/addressbook.jsp | 24 ++++++++++++++-- apps/susidns/src/jsp/index.jsp | 12 +++++++- apps/susidns/src/themes/dark/susidns.css | 28 +++++++++++++------ apps/susidns/src/themes/light/susidns.css | 26 +++++++++++------ 8 files changed, 117 insertions(+), 23 deletions(-) diff --git a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java index 60597a5f9d..1ec1d2f7de 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java @@ -42,6 +42,7 @@ import net.i2p.util.OrderedProperties; import net.i2p.util.PortMapper; import net.i2p.util.SecureDirectory; import net.i2p.util.SystemVersion; +import net.i2p.util.Translate; /** * Main class of addressbook. Performs updates, and runs the main loop. @@ -674,12 +675,21 @@ class Daemon { if (nnew > 0) { ClientAppManager cmgr = I2PAppContext.getGlobalContext().clientAppManager(); if (cmgr != null) { - int nc = cmgr.getBubbleCount(PortMapper.SVC_SUSIDNS); - cmgr.setBubble(PortMapper.SVC_SUSIDNS, nc + nnew, null); + int nc = cmgr.getBubbleCount(PortMapper.SVC_SUSIDNS) + nnew; + String msg = ngettext("1 new host", "{0} new hosts", nc); + cmgr.setBubble(PortMapper.SVC_SUSIDNS, nc, msg); } } } + /** + * translate (ngettext) from the routerconsole bundle + * @since 0.9.66 + */ + private static String ngettext(String s, String p, int n) { + return Translate.getString(n, s, p, I2PAppContext.getGlobalContext(), "net.i2p.router.web.messages"); + } + /** @since 0.9.26 */ private static void logInner(Log log, String action, String name, AddressBook addressbook) { if (log != null) { diff --git a/apps/routerconsole/java/bundle-messages.sh b/apps/routerconsole/java/bundle-messages.sh index 19bcba45c5..2383822e7d 100755 --- a/apps/routerconsole/java/bundle-messages.sh +++ b/apps/routerconsole/java/bundle-messages.sh @@ -51,6 +51,7 @@ ROUTERFILES="\ ../../../router/java/src/net/i2p/router/transport/ntcp/EstablishState.java \ ../../../router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java \ ../../../router/java/src/net/i2p/router/transport/udp/UDPTransport.java \ + ../../addressbook/java/src/net/i2p/addressbook/Daemon.java \ " # add ../java/ so the refs will work in the po file diff --git a/apps/susidns/src/java/src/i2p/susi/dns/BaseBean.java b/apps/susidns/src/java/src/i2p/susi/dns/BaseBean.java index 27f53d32fe..b42b009a25 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/BaseBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/BaseBean.java @@ -8,9 +8,11 @@ import java.util.List; import java.util.Properties; import net.i2p.I2PAppContext; +import net.i2p.app.ClientAppManager; import net.i2p.data.DataHelper; import net.i2p.util.Log; import net.i2p.util.OrderedProperties; +import net.i2p.util.PortMapper; /** * Holds methods common to several Beans. @@ -179,6 +181,26 @@ public class BaseBean this.method = method; } + /** + * @since 0.9.66 + */ + public int getBubbleCount() { + ClientAppManager cmgr = _context.clientAppManager(); + if (cmgr != null) + return cmgr.getBubbleCount(PortMapper.SVC_SUSIDNS); + return 0; + } + + /** + * @since 0.9.66 + */ + public String getBubbleText() { + ClientAppManager cmgr = _context.clientAppManager(); + if (cmgr != null) + return cmgr.getBubbleText(PortMapper.SVC_SUSIDNS); + return null; + } + /** * Translate * @since 0.9.13 moved from subclasses diff --git a/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java b/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java index 34a43e3104..8488bdcc9c 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java @@ -39,12 +39,14 @@ import java.util.Map; import java.util.Properties; import java.util.SortedMap; +import net.i2p.app.ClientAppManager; import net.i2p.client.naming.NamingService; import net.i2p.client.naming.SingleFileNamingService; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.servlet.RequestWrapper; +import net.i2p.util.PortMapper; /** * Talk to the NamingService API instead of modifying the hosts.txt files directly, @@ -216,6 +218,8 @@ public class NamingServiceBean extends AddressbookBean AddressBean array[] = list.toArray(new AddressBean[list.size()]); if (sortByDate) { Arrays.sort(array, new AddressByDateSorter()); + if (getBook().equals("router")) + clearBubbles(); } else if (!(results instanceof SortedMap)) { Arrays.sort(array, sorter); } @@ -528,6 +532,15 @@ public class NamingServiceBean extends AddressbookBean return rv; } + /** + * @since 0.9.66 + */ + private void clearBubbles() { + ClientAppManager cmgr = _context.clientAppManager(); + if (cmgr != null) + cmgr.setBubble(PortMapper.SVC_SUSIDNS, 0, null); + } + /** * @since 0.9.20 */ diff --git a/apps/susidns/src/jsp/addressbook.jsp b/apps/susidns/src/jsp/addressbook.jsp index e2d1d52f86..17030ea419 100644 --- a/apps/susidns/src/jsp/addressbook.jsp +++ b/apps/susidns/src/jsp/addressbook.jsp @@ -69,7 +69,17 @@ <a id="overview" href="index"><%=intl._t("Overview")%></a> <a class="abook private" href="addressbook?book=private&filter=none"><%=intl._t("Private")%></a> <a class="abook local" href="addressbook?book=local&filter=none"><%=intl._t("Local")%></a> -<a class="abook router" href="addressbook?book=router&filter=none"><%=intl._t("Router")%></a> +<a class="abook router" href="addressbook?book=router&filter=none"><%=intl._t("Router")%><% + int bubbleCount = book.getBubbleCount(); + String bubbleText = book.getBubbleText(); + if (bubbleCount > 0) { + %><span class="notifbubble" <% + if (bubbleText != null) { + %> title="<%=bubbleText%>"<% + } + %>><%=bubbleCount%></span><% + } +%></a> <a class="abook published" href="addressbook?book=published&filter=none"><%=intl._t("Published")%></a> <a id="subs" href="subscriptions"><%=intl._t("Subscriptions")%></a> <a id="config" href="config"><%=intl._t("Configuration")%></a> @@ -151,7 +161,7 @@ ${book.loadBookMessages} <c:if test="${book.notEmpty}"> <div id="filter"> <c:if test="${book.hasFilter}"> -<span><%=intl._t("Current filter")%>: +<span id="filterheader"><%=intl._t("Current filter")%>: <% String f = book.getFilter(); if ("latest".equals(f)) @@ -195,7 +205,15 @@ ${book.loadBookMessages} <a href="addressbook?filter=0-9&begin=0&end=49">0-9</a> <a href="addressbook?filter=xn--&begin=0&end=49"><%=intl._t("other")%></a> <% if (!book.getBook().equals("published")) { %> - <a href="addressbook?filter=latest&begin=0&end=49"><%=intl._t("latest")%></a> + <a href="addressbook?filter=latest&begin=0&end=49"><%=intl._t("latest")%><% + if (bubbleCount > 0 && book.getBook().equals("router")) { + %><span class="notifbubble" <% + if (bubbleText != null) { + %> title="<%=bubbleText%>"<% + } + %>><%=bubbleCount%></span><% + } + %></a> <% } %> <a href="addressbook?filter=none&begin=0&end=49"><%=intl._t("all")%></a> </p> diff --git a/apps/susidns/src/jsp/index.jsp b/apps/susidns/src/jsp/index.jsp index c698518d4c..8e782200c1 100644 --- a/apps/susidns/src/jsp/index.jsp +++ b/apps/susidns/src/jsp/index.jsp @@ -56,7 +56,17 @@ <a id="overview" class="active" href="index"><%=intl._t("Overview")%></a> <a class="abook" href="addressbook?book=private&filter=none"><%=intl._t("Private")%></a> <a class="abook" href="addressbook?book=local&filter=none"><%=intl._t("Local")%></a> -<a class="abook" href="addressbook?book=router&filter=none"><%=intl._t("Router")%></a> +<a class="abook" href="addressbook?book=router&filter=none"><%=intl._t("Router")%><% + int bubbleCount = base.getBubbleCount(); + String bubbleText = base.getBubbleText(); + if (bubbleCount > 0) { + %><span class="notifbubble" <% + if (bubbleText != null) { + %> title="<%=bubbleText%>"<% + } + %>><%=bubbleCount%></span><% + } +%></a> <a class="abook" href="addressbook?book=published&filter=none"><%=intl._t("Published")%></a> <a id="subs" href="subscriptions"><%=intl._t("Subscriptions")%></a> <a id="config" href="config"><%=intl._t("Configuration")%></a> diff --git a/apps/susidns/src/themes/dark/susidns.css b/apps/susidns/src/themes/dark/susidns.css index 107c0ba5af..38c2814098 100644 --- a/apps/susidns/src/themes/dark/susidns.css +++ b/apps/susidns/src/themes/dark/susidns.css @@ -59,8 +59,8 @@ body.iframed { /* topnav */ #navi { - margin: -16px auto 30px; - padding: 5px 3px; + margin: -10px auto 30px; + padding: 10px 3px; position: sticky; top: -1px; z-index: 999; @@ -75,7 +75,7 @@ body.iframed { .iframed #navi { margin: -6px -15px 30px; - padding: 5px 0; + padding: 13px 0; position: static; border-right: none; border-left: none; @@ -183,6 +183,16 @@ body.iframed { background: #1F1A24 url(../images/overview.png) 5px center no-repeat !important; } +.notifbubble { + background-color: #e33; + border: 1px solid #e33; + border-radius: 9px; + color: #fff; + margin: -16px 0 0 -4px; + padding: 0 4px; + position: absolute; +} + /* end topnav */ hr { @@ -273,7 +283,7 @@ div#filter + div#search > form { box-shadow: inset 3px 3px 3px #000; } -#filter span { +#filterheader { display: inline-block; border: 1px solid #292929; min-width: 300px; @@ -286,18 +296,18 @@ div#filter + div#search > form { background-image: linear-gradient(to bottom, #1F1A24 0%, #222730 100%) !important; } -#filter span a, #filter span a:hover { +#filterheader a, #filterheader a:hover { border: none; background: none; margin: 0 0 0 10px; padding: 0; } -#filter span a:active { +#filterheader a:active { box-shadow: none; } -#filter span b { +#filterheader b { text-transform: uppercase; font-size: 10pt; margin: 0 0 0 5px; @@ -1232,7 +1242,7 @@ body, input[type="submit"], input[type="reset"], .fakebutton, input, select, h4, } #navi { - padding: 6px 5px !important; + padding: 10px 5px !important; } #navi a { @@ -1240,7 +1250,7 @@ body, input[type="submit"], input[type="reset"], .fakebutton, input, select, h4, padding: 4px 7px 5px 25px !important; } -h3, #filter a, #filter span { +h3, #filter a, #filterheader { font-size: 11pt !important; } diff --git a/apps/susidns/src/themes/light/susidns.css b/apps/susidns/src/themes/light/susidns.css index 6e41f128e8..3774b81b44 100644 --- a/apps/susidns/src/themes/light/susidns.css +++ b/apps/susidns/src/themes/light/susidns.css @@ -63,7 +63,7 @@ object { #navi { margin: -1px 0 0; - padding: 5px 3px; + padding: 10px 3px; text-align: center; border: 1px solid #dee2e6; border-radius: 2px 2px 0 0; @@ -77,7 +77,7 @@ object { } .iframed #navi { margin: -11px -11px 10px; - padding: 7px 5px 6px; + padding: 13px 5px 6px; border-radius: 0; position: static } @@ -116,6 +116,16 @@ object { transition: ease border 0.7s } +.notifbubble { + background-color: #e33; + border: 1px solid #e33; + border-radius: 9px; + color: #fff; + margin: -16px 0 0 -4px; + padding: 0 4px; + position: absolute; +} + .invisible { visibility: hidden; display: none; @@ -132,7 +142,7 @@ object { margin-top: 6px; } -h3, h4, th, #filter span { +h3, h4, th, #filterheader { color: #41465f; } @@ -649,7 +659,7 @@ textarea[name="config"]:focus, textarea[name="content"]:focus { box-shadow: inset 0 0 0 1px #fff, inset 3px 3px 3px #33333f; } -#filter span { +#filterheader { display: inline-block; text-align: center; @@ -665,14 +675,14 @@ textarea[name="config"]:focus, textarea[name="content"]:focus { box-shadow: inset 0 0 0 1px #fff; } -#filter span a { +#filterheader a { margin: -8px 1px; letter-spacing: normal; word-spacing: normal; padding: 2px 5px; } -#filter span b { +#filterheader b { font-size: 11pt; margin: 0 3px 0 0; @@ -1373,7 +1383,7 @@ input[type="checkbox"][disabled]:checked, input[type="radio"][disabled]:checked, @media screen and (max-width: 680px) { #navi { - padding: 5px 3px !important; + padding: 10px 3px !important; } #navi a { @@ -1420,7 +1430,7 @@ code, tt, .destaddress { margin-top: -40px !important; } -#filter span, #filter a { +#filterheader, #filter a { font-size: 11pt !important; } -- GitLab