From 477b25dfa01d60948e52f9b2132c0d3ffbdbaabf Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Mon, 23 Mar 2026 21:43:35 -0400 Subject: [PATCH] v0.12.9 bug fixes (FCM and list ordering) --- backend/package.json | 2 +- build.sh | 2 +- frontend/package.json | 2 +- frontend/src/components/GroupInfoModal.jsx | 2 +- frontend/src/components/NewChatModal.jsx | 2 +- frontend/src/components/SettingsModal.jsx | 78 +--------------------- frontend/src/main.jsx | 17 ++++- frontend/src/pages/GroupManagerPage.jsx | 8 +-- frontend/src/pages/UserManagerPage.jsx | 14 ++-- 9 files changed, 32 insertions(+), 95 deletions(-) diff --git a/backend/package.json b/backend/package.json index 55c1b6c..1df4df5 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-backend", - "version": "0.12.8", + "version": "0.12.9", "description": "RosterChirp backend server", "main": "src/index.js", "scripts": { diff --git a/build.sh b/build.sh index 01e5ee6..4ff347c 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.12.8}" +VERSION="${1:-0.12.9}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="rosterchirp" diff --git a/frontend/package.json b/frontend/package.json index 060141c..7a5eb47 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-frontend", - "version": "0.12.8", + "version": "0.12.9", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/GroupInfoModal.jsx b/frontend/src/components/GroupInfoModal.jsx index 44f5be1..34985fd 100644 --- a/frontend/src/components/GroupInfoModal.jsx +++ b/frontend/src/components/GroupInfoModal.jsx @@ -194,7 +194,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) { Members ({members.length})
- {members.map(m => ( + {[...members].sort((a, b) => a.name.localeCompare(b.name)).map(m => (
{m.name} diff --git a/frontend/src/components/NewChatModal.jsx b/frontend/src/components/NewChatModal.jsx index 285b4bf..f5a344f 100644 --- a/frontend/src/components/NewChatModal.jsx +++ b/frontend/src/components/NewChatModal.jsx @@ -141,7 +141,7 @@ export default function NewChatModal({ onClose, onCreated }) { )}
- {users.filter(u => u.id !== user.id && u.allow_dm !== 0).map(u => ( + {users.filter(u => u.id !== user.id && u.allow_dm !== 0).sort((a, b) => a.name.localeCompare(b.name)).map(u => (
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index fbe12ad..ad840d8 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -66,10 +66,21 @@ if ('serviceWorker' in navigator) { currentScale = Math.round(newScale * 100) / 100; document.documentElement.style.setProperty('--font-scale', currentScale); } else if (e.touches.length === 1 && isStandalone) { - // Single finger: block pull-to-refresh at top of page + // Single finger: block pull-to-refresh only when no scrollable ancestor + // has scrolled content above the viewport. + // Without this ancestor check, document.scrollTop is always 0 in this + // flex layout, so the naive condition blocked ALL upward swipes (dy > 0), + // making any scroll container impossible to scroll back up after reaching + // the bottom — freezing the window. const dy = e.touches[0].clientY - singleStartY; - if (dy > 0 && document.documentElement.scrollTop === 0 && document.body.scrollTop === 0) { - e.preventDefault(); + if (dy > 0) { + let el = e.target; + let canScrollUp = false; + while (el && el !== document.documentElement) { + if (el.scrollTop > 0) { canScrollUp = true; break; } + el = el.parentElement; + } + if (!canScrollUp) e.preventDefault(); } } }, { passive: false }); diff --git a/frontend/src/pages/GroupManagerPage.jsx b/frontend/src/pages/GroupManagerPage.jsx index 8b9c25d..9b534b9 100644 --- a/frontend/src/pages/GroupManagerPage.jsx +++ b/frontend/src/pages/GroupManagerPage.jsx @@ -71,7 +71,7 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) { const [showDelete, setShowDelete] = useState(false); const load = useCallback(() => - api.getUserGroups().then(({ groups }) => setGroups(groups)).catch(() => {}), []); + api.getUserGroups().then(({ groups }) => setGroups([...(groups||[])].sort((a, b) => a.name.localeCompare(b.name)))).catch(() => {}), []); useEffect(() => { load(); }, [load]); const selectGroup = async (g) => { @@ -220,7 +220,7 @@ function DirectMessagesTab({ allUserGroups, onRefresh, refreshKey, isMobile = fa const [showDelete, setShowDelete] = useState(false); const load = useCallback(() => - api.getMultiGroupDms().then(({ dms }) => setDms(dms||[])).catch(() => {}), []); + api.getMultiGroupDms().then(({ dms }) => setDms([...(dms||[])].sort((a, b) => a.name.localeCompare(b.name)))).catch(() => {}), []); useEffect(() => { load(); }, [load, refreshKey]); const clearSelection = () => { setSelected(null); setDmName(''); setGroupIds(new Set()); setSavedGroupIds(new Set()); setShowDelete(false); }; @@ -556,8 +556,8 @@ export default function GroupManagerPage({ isMobile = false, onProfile, onHelp, const onRefresh = () => setRefreshKey(k => k+1); useEffect(() => { - api.searchUsers('').then(({ users }) => setAllUsers(users.filter(u => u.status==='active'))).catch(() => {}); - api.getUserGroups().then(({ groups }) => setAllUserGroups(groups)).catch(() => {}); + api.searchUsers('').then(({ users }) => setAllUsers(users.filter(u => u.status==='active').sort((a, b) => (a.display_name||a.name).localeCompare(b.display_name||b.name)))).catch(() => {}); + api.getUserGroups().then(({ groups }) => setAllUserGroups([...(groups||[])].sort((a, b) => a.name.localeCompare(b.name)))).catch(() => {}); }, [refreshKey]); // Nav item helper — matches Schedule page style diff --git a/frontend/src/pages/UserManagerPage.jsx b/frontend/src/pages/UserManagerPage.jsx index 43414e1..9e8abc0 100644 --- a/frontend/src/pages/UserManagerPage.jsx +++ b/frontend/src/pages/UserManagerPage.jsx @@ -300,11 +300,13 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o api.getSettings().then(({ settings }) => { if (settings.user_pass) setUserPass(settings.user_pass); }).catch(() => {}); }, [load]); - const filtered = users.filter(u => - !search || u.name?.toLowerCase().includes(search.toLowerCase()) || - u.display_name?.toLowerCase().includes(search.toLowerCase()) || - u.email?.toLowerCase().includes(search.toLowerCase()) - ); + const filtered = users + .filter(u => + !search || u.name?.toLowerCase().includes(search.toLowerCase()) || + u.display_name?.toLowerCase().includes(search.toLowerCase()) || + u.email?.toLowerCase().includes(search.toLowerCase()) + ) + .sort((a, b) => a.name.localeCompare(b.name)); // ── Nav item helper (matches Schedule page style) ───────────────────────── const navItem = (label, key) => ( @@ -354,7 +356,7 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o )} {/* Content */} -
+
{tab === 'users' && ( <> setSearch(e.target.value)}