mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-14 21:36:03 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5e9eab935 | |||
| 33b2d3c260 | |||
| eccbd0bac5 |
@@ -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
|
## [3.11.1] - 2026-04-12
|
||||||
|
|
||||||
* Feature: Home Assistant MQTT fanout
|
* Feature: Home Assistant MQTT fanout
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@
|
|||||||
<link rel="icon" type="image/png" href="./favicon-96x96.png" sizes="96x96" />
|
<link rel="icon" type="image/png" href="./favicon-96x96.png" sizes="96x96" />
|
||||||
<link rel="shortcut icon" href="./favicon.ico" />
|
<link rel="shortcut icon" href="./favicon.ico" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" />
|
<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>
|
<script>
|
||||||
// Register minimal service worker for PWA installability.
|
// Register minimal service worker for PWA installability.
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "remoteterm-meshcore-frontend",
|
"name": "remoteterm-meshcore-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "3.11.1",
|
"version": "3.11.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -265,6 +265,12 @@ export function Sidebar({
|
|||||||
const sortContactsByOrder = useCallback(
|
const sortContactsByOrder = useCallback(
|
||||||
(items: Contact[], order: SortOrder) =>
|
(items: Contact[], order: SortOrder) =>
|
||||||
[...items].sort((a, b) => {
|
[...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') {
|
if (order === 'recent') {
|
||||||
const timeA = getContactRecentTime(a);
|
const timeA = getContactRecentTime(a);
|
||||||
const timeB = getContactRecentTime(b);
|
const timeB = getContactRecentTime(b);
|
||||||
@@ -274,7 +280,7 @@ export function Sidebar({
|
|||||||
}
|
}
|
||||||
return (a.name || a.public_key).localeCompare(b.name || b.public_key);
|
return (a.name || a.public_key).localeCompare(b.name || b.public_key);
|
||||||
}),
|
}),
|
||||||
[getContactRecentTime]
|
[getContactRecentTime, unreadCounts]
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortRepeatersByOrder = useCallback(
|
const sortRepeatersByOrder = useCallback(
|
||||||
|
|||||||
@@ -513,6 +513,42 @@ describe('Sidebar section summaries', () => {
|
|||||||
expect(contactRows).toEqual(['DM Recent', 'Advert Only', 'No Recency']);
|
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', () => {
|
it('sorts repeaters by heard recency even when message times disagree', () => {
|
||||||
const publicChannel = makeChannel(PUBLIC_CHANNEL_KEY, 'Public');
|
const publicChannel = makeChannel(PUBLIC_CHANNEL_KEY, 'Public');
|
||||||
const staleMessageRelay = makeContact(
|
const staleMessageRelay = makeContact(
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "remoteterm-meshcore"
|
name = "remoteterm-meshcore"
|
||||||
version = "3.11.1"
|
version = "3.11.2"
|
||||||
description = "RemoteTerm - Web interface for MeshCore radio mesh networks"
|
description = "RemoteTerm - Web interface for MeshCore radio mesh networks"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|||||||
@@ -983,7 +983,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remoteterm-meshcore"
|
name = "remoteterm-meshcore"
|
||||||
version = "3.11.1"
|
version = "3.11.2"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiomqtt" },
|
{ name = "aiomqtt" },
|
||||||
|
|||||||
Reference in New Issue
Block a user