Compare commits

..

3 Commits

Author SHA1 Message Date
Jack Kingsman e5e9eab935 Updating changelog + build for 3.11.2 2026-04-12 22:44:46 -07:00
Jack Kingsman 33b2d3c260 Unread DMs are ALWAYS at the top. Closes #185. 2026-04-12 22:41:41 -07:00
Jack Kingsman eccbd0bac5 use-credentials on webmanifest fetches so basic auth behaves. Closes #182. 2026-04-12 22:36:08 -07:00
7 changed files with 52 additions and 5 deletions
+5
View File
@@ -1,3 +1,8 @@
## [3.11.2] - 2026-04-12
* Feature: Unread DMs are always at the top of the DM list no matter what
* Bugfix: Webmanifest needs withCredentials
## [3.11.1] - 2026-04-12
* Feature: Home Assistant MQTT fanout
+1 -1
View File
@@ -13,7 +13,7 @@
<link rel="icon" type="image/png" href="./favicon-96x96.png" sizes="96x96" />
<link rel="shortcut icon" href="./favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" />
<link rel="manifest" href="./site.webmanifest" />
<link rel="manifest" href="./site.webmanifest" crossorigin="use-credentials" />
<script>
// Register minimal service worker for PWA installability.
if ('serviceWorker' in navigator) {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "remoteterm-meshcore-frontend",
"private": true,
"version": "3.11.1",
"version": "3.11.2",
"type": "module",
"scripts": {
"dev": "vite",
+7 -1
View File
@@ -265,6 +265,12 @@ export function Sidebar({
const sortContactsByOrder = useCallback(
(items: Contact[], order: SortOrder) =>
[...items].sort((a, b) => {
// Unread DM contacts always float to the top
const unreadA = unreadCounts[getStateKey('contact', a.public_key)] || 0;
const unreadB = unreadCounts[getStateKey('contact', b.public_key)] || 0;
if (unreadA > 0 && unreadB === 0) return -1;
if (unreadA === 0 && unreadB > 0) return 1;
if (order === 'recent') {
const timeA = getContactRecentTime(a);
const timeB = getContactRecentTime(b);
@@ -274,7 +280,7 @@ export function Sidebar({
}
return (a.name || a.public_key).localeCompare(b.name || b.public_key);
}),
[getContactRecentTime]
[getContactRecentTime, unreadCounts]
);
const sortRepeatersByOrder = useCallback(
+36
View File
@@ -513,6 +513,42 @@ describe('Sidebar section summaries', () => {
expect(contactRows).toEqual(['DM Recent', 'Advert Only', 'No Recency']);
});
it('floats contacts with unread DMs above read contacts regardless of recency', () => {
const publicChannel = makeChannel(PUBLIC_CHANNEL_KEY, 'Public');
const readRecent = makeContact('11'.repeat(32), 'Read Recent', 1, { last_advert: 500 });
const unreadOld = makeContact('22'.repeat(32), 'Unread Old', 1, { last_advert: 100 });
render(
<Sidebar
contacts={[readRecent, unreadOld]}
channels={[publicChannel]}
activeConversation={null}
onSelectConversation={vi.fn()}
onNewMessage={vi.fn()}
lastMessageTimes={{
[getStateKey('contact', readRecent.public_key)]: 500,
[getStateKey('contact', unreadOld.public_key)]: 200,
}}
unreadCounts={{
[getStateKey('contact', unreadOld.public_key)]: 3,
}}
mentions={{}}
showCracker={false}
crackerRunning={false}
onToggleCracker={vi.fn()}
onMarkAllRead={vi.fn()}
/>
);
const contactRows = screen
.getAllByText(/^(Read Recent|Unread Old)$/)
.map((node) => node.textContent)
.filter((text): text is string => Boolean(text));
// Unread Old has unread DMs so it floats above Read Recent despite older recency
expect(contactRows).toEqual(['Unread Old', 'Read Recent']);
});
it('sorts repeaters by heard recency even when message times disagree', () => {
const publicChannel = makeChannel(PUBLIC_CHANNEL_KEY, 'Public');
const staleMessageRelay = makeContact(
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "remoteterm-meshcore"
version = "3.11.1"
version = "3.11.2"
description = "RemoteTerm - Web interface for MeshCore radio mesh networks"
readme = "README.md"
requires-python = ">=3.11"
Generated
+1 -1
View File
@@ -983,7 +983,7 @@ wheels = [
[[package]]
name = "remoteterm-meshcore"
version = "3.11.1"
version = "3.11.2"
source = { virtual = "." }
dependencies = [
{ name = "aiomqtt" },