v0.8.4 full refresh
This commit is contained in:
@@ -7,7 +7,7 @@ TZ=UTC
|
||||
# Copy this file to .env and customize
|
||||
|
||||
# Image version to run (set by build.sh, or use 'latest')
|
||||
JAMA_VERSION=0.8.2
|
||||
JAMA_VERSION=0.8.4
|
||||
|
||||
# Default admin credentials (used on FIRST RUN only)
|
||||
ADMIN_NAME=Admin User
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jama-backend",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.4",
|
||||
"description": "TeamChat backend server",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
||||
2
build.sh
2
build.sh
@@ -13,7 +13,7 @@
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:-0.8.2}"
|
||||
VERSION="${1:-0.8.4}"
|
||||
ACTION="${2:-}"
|
||||
REGISTRY="${REGISTRY:-}"
|
||||
IMAGE_NAME="jama"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/icons/jama.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content" />
|
||||
<meta name="theme-color" content="#1a73e8" />
|
||||
<meta name="description" content="jama - just another messaging app" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jama-frontend",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
.own .message-body { align-items: flex-end; }
|
||||
|
||||
.msg-name {
|
||||
font-size: 12px;
|
||||
font-size: calc(0.75rem * var(--font-scale));
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 3px;
|
||||
@@ -98,8 +98,8 @@
|
||||
|
||||
.reply-bar { width: 3px; background: var(--primary); border-radius: 2px; flex-shrink: 0; }
|
||||
|
||||
.reply-name { font-size: 11px; font-weight: 600; color: var(--primary); }
|
||||
.reply-text { font-size: 12px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
|
||||
.reply-name { font-size: calc(0.6875rem * var(--font-scale)); font-weight: 600; color: var(--primary); }
|
||||
.reply-text { font-size: calc(0.75rem * var(--font-scale)); color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
|
||||
|
||||
/* Bubble row */
|
||||
.msg-bubble-wrap {
|
||||
@@ -204,10 +204,10 @@
|
||||
border: 1px dashed var(--border);
|
||||
}
|
||||
|
||||
.deleted-text { font-size: 13px; color: var(--text-tertiary); font-style: italic; }
|
||||
.deleted-text { font-size: calc(0.8125rem * var(--font-scale)); color: var(--text-tertiary); font-style: italic; }
|
||||
|
||||
.msg-text {
|
||||
font-size: 14px;
|
||||
font-size: calc(0.875rem * var(--font-scale));
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
@@ -236,7 +236,7 @@
|
||||
}
|
||||
|
||||
.msg-time {
|
||||
font-size: 11px;
|
||||
font-size: calc(0.6875rem * var(--font-scale));
|
||||
color: var(--text-tertiary);
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
@@ -260,11 +260,11 @@
|
||||
border-radius: 12px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
font-size: 14px;
|
||||
font-size: calc(0.875rem * var(--font-scale));
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
.reaction-count { font-size: 12px; color: var(--text-secondary); }
|
||||
.reaction-count { font-size: calc(0.75rem * var(--font-scale)); color: var(--text-secondary); }
|
||||
.reaction-btn.active { background: var(--primary-light); border-color: var(--primary); }
|
||||
.reaction-btn.active .reaction-count { color: var(--primary); }
|
||||
.reaction-btn:hover { background: var(--primary-light); }
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-size: calc(0.875rem * var(--font-scale));
|
||||
line-height: 1.4;
|
||||
font-family: var(--font);
|
||||
color: var(--text-primary);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
--shadow-lg: 0 4px 20px rgba(0,0,0,0.18);
|
||||
--transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--font: 'Google Sans', 'Roboto', sans-serif;
|
||||
--font-scale: 1;
|
||||
}
|
||||
|
||||
html, body, #root { height: 100%; min-width: 320px; font-family: var(--font); color: var(--text-primary); background: var(--background); }
|
||||
@@ -33,6 +34,7 @@ html, body, #root { height: 100%; min-width: 320px; font-family: var(--font); co
|
||||
/* Disable pull-to-refresh in PWA standalone mode — prevents viewport shift bug */
|
||||
html {
|
||||
overscroll-behavior-y: none;
|
||||
font-size: 100%; /* inherits system font size — allows Android accessibility font scaling */
|
||||
}
|
||||
|
||||
button { font-family: var(--font); cursor: pointer; border: none; background: none; }
|
||||
|
||||
@@ -14,25 +14,72 @@ if ('serviceWorker' in navigator) {
|
||||
|
||||
|
||||
|
||||
// Disable pull-to-refresh in PWA standalone mode to prevent viewport shift bug.
|
||||
// The CSS overscroll-behavior-y:none handles most browsers; this catches the rest.
|
||||
// ─── Touch gesture handler ───────────────────────────────────────────────────
|
||||
// Handles two behaviours in one unified listener set to avoid conflicts:
|
||||
//
|
||||
// 1. PINCH → font scale only (not viewport zoom).
|
||||
// viewport has user-scalable=no so the browser never zooms the layout.
|
||||
// We intercept the pinch and adjust --font-scale on <html> instead,
|
||||
// which scales only text (rem-based font sizes). Persisted to localStorage.
|
||||
// On first launch, html { font-size: 100% } inherits the Android system
|
||||
// font size as the 1rem baseline automatically.
|
||||
//
|
||||
// 2. PULL-TO-REFRESH → blocked in PWA standalone mode only.
|
||||
(function () {
|
||||
const LS_KEY = 'jama_font_scale';
|
||||
const MIN_SCALE = 0.8;
|
||||
const MAX_SCALE = 2.0;
|
||||
|
||||
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
|
||||
|| window.navigator.standalone === true;
|
||||
if (!isStandalone) return;
|
||||
|
||||
let startY = 0;
|
||||
document.addEventListener('touchstart', e => {
|
||||
startY = e.touches[0].clientY;
|
||||
// Restore saved font scale on launch
|
||||
const saved = parseFloat(localStorage.getItem(LS_KEY));
|
||||
let currentScale = (saved >= MIN_SCALE && saved <= MAX_SCALE) ? saved : 1.0;
|
||||
document.documentElement.style.setProperty('--font-scale', currentScale);
|
||||
|
||||
let pinchStartDist = null;
|
||||
let pinchStartScale = currentScale;
|
||||
let singleStartY = 0;
|
||||
|
||||
function getTouchDist(e) {
|
||||
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
||||
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
document.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length === 2) {
|
||||
pinchStartDist = getTouchDist(e);
|
||||
pinchStartScale = currentScale;
|
||||
} else if (e.touches.length === 1) {
|
||||
singleStartY = e.touches[0].clientY;
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', e => {
|
||||
// Only block downward pull at the very top of the document
|
||||
const dy = e.touches[0].clientY - startY;
|
||||
if (dy > 0 && (document.documentElement.scrollTop === 0 || document.body.scrollTop === 0)) {
|
||||
document.addEventListener('touchmove', function (e) {
|
||||
if (e.touches.length === 2 && pinchStartDist !== null) {
|
||||
// Two-finger pinch: scale fonts, not viewport
|
||||
e.preventDefault();
|
||||
const ratio = getTouchDist(e) / pinchStartDist;
|
||||
const newScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, pinchStartScale * ratio));
|
||||
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
|
||||
const dy = e.touches[0].clientY - singleStartY;
|
||||
if (dy > 0 && document.documentElement.scrollTop === 0 && document.body.scrollTop === 0) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
document.addEventListener('touchend', function (e) {
|
||||
if (e.touches.length < 2 && pinchStartDist !== null) {
|
||||
pinchStartDist = null;
|
||||
localStorage.setItem(LS_KEY, currentScale);
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
// Clear badge count when user focuses the app
|
||||
|
||||
Reference in New Issue
Block a user