diff --git a/app.py b/app.py index c811926..e6e758a 100644 --- a/app.py +++ b/app.py @@ -203,7 +203,7 @@ def login(): login_user(user) return redirect(url_for('dashboard')) - return render_template('login.html', error='Invalid credentials') + return render_template('login.html', error=_('Invalid credentials')) return render_template('login.html') @@ -239,17 +239,17 @@ def refresh_token(): def add_memory(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return jsonify({'success': False, 'error': 'Connection not found'}), 404 + return jsonify({'success': False, 'error': _('Connection not found')}), 404 data = request.get_json(silent=True) or {} memory_text = (data.get('memory_text') or '').strip() memory_date = (data.get('memory_date') or '').strip() if not memory_text or len(memory_text) > 200: - return jsonify({'success': False, 'error': 'Invalid memory text'}), 400 + return jsonify({'success': False, 'error': _('Invalid memory text')}), 400 if not validate_memory_date(memory_date): - return jsonify({'success': False, 'error': 'Invalid memory date'}), 400 + return jsonify({'success': False, 'error': _('Invalid memory date')}), 400 memory_id = ConnectionMemory.create(connection_id, current_user.id, memory_text, memory_date) return jsonify({'success': True, 'memory_id': memory_id}) @@ -260,10 +260,10 @@ def add_memory(connection_id): def approve_memory(connection_id, memory_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return jsonify({'success': False, 'error': 'Connection not found'}), 404 + return jsonify({'success': False, 'error': _('Connection not found')}), 404 if not ConnectionMemory.approve(connection_id, memory_id, current_user.id): - return jsonify({'success': False, 'error': 'Unable to approve memory'}), 400 + return jsonify({'success': False, 'error': _('Unable to approve memory')}), 400 return jsonify({'success': True}) @@ -293,7 +293,7 @@ def connection_page(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) other_user = connection.get_other_user(current_user.id) return render_template('connection.html', connection=connection, other_user=other_user) @@ -304,7 +304,7 @@ def clipboard(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) clipboard_data = SharedClipboard.get_by_connection(connection_id) other_user = connection.get_other_user(current_user.id) @@ -319,7 +319,7 @@ def clipboard(connection_id): def memories(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) other_user = connection.get_other_user(current_user.id) memories = ConnectionMemory.get_for_connection(connection_id) @@ -334,7 +334,7 @@ def drawing_game(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) game = DrawingGame.get_by_connection(connection_id) other_user = connection.get_other_user(current_user.id) @@ -350,7 +350,7 @@ def chat(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) other_user = connection.get_other_user(current_user.id) messages = ChatMessage.get_messages(connection_id) @@ -376,7 +376,7 @@ def admin_login(): if secrets.compare_digest(password, admin_password): session['is_admin'] = True return redirect(url_for('admin_panel')) - return render_template('admin_login.html', error='Invalid password') + return render_template('admin_login.html', error=_('Invalid password')) return render_template('admin_login.html') @@ -615,23 +615,23 @@ def handle_drawing_start(data): # Validate access if not validate_connection_access(connection_id, drawer_id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} # Validate answer (limit to 50 characters) answer = answer.strip()[:50] if not answer: - return {'success': False, 'error': 'Answer required'} + return {'success': False, 'error': _('Answer required')} # Get or create session session = DrawingSession.get_active_session(connection_id) if not session: - return {'success': False, 'error': 'No active session'} + return {'success': False, 'error': _('No active session')} if session.waiting_for_partner: - return {'success': False, 'error': 'Waiting for partner to join'} + return {'success': False, 'error': _('Waiting for partner to join')} if session.is_session_complete(): - return {'success': False, 'error': 'Session complete'} + return {'success': False, 'error': _('Session complete')} # Start the round session.start_next_round(drawer_id, answer) @@ -721,15 +721,15 @@ def handle_end_game_early(data): session_id = data.get('session_id') if not validate_connection_access(connection_id, current_user.id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} session = DrawingSession.get_by_id(session_id) if not session or session.connection_id != connection_id: - return {'success': False, 'error': 'Invalid session'} + return {'success': False, 'error': _('Invalid session')} # Ensure the session is still active if not session.is_active: - return {'success': False, 'error': 'Session is not active'} + return {'success': False, 'error': _('Session is not active')} # End the session session.end_session() @@ -749,7 +749,7 @@ def handle_create_new_game_session(data): connection_id = data.get('connection_id') if not validate_connection_access(connection_id, current_user.id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} # Create new session (this will deactivate old ones) session_id = DrawingSession.create(connection_id, current_user.id, DEFAULT_ROUNDS_PER_SESSION) @@ -773,11 +773,11 @@ def handle_send_message(data): # Validate access if not validate_connection_access(connection_id, current_user.id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} # Validate message length (max 1000 chars) if not message or len(message) > 1000: - return {'success': False, 'error': 'Invalid message'} + return {'success': False, 'error': _('Invalid message')} # Save message message_id = ChatMessage.create(connection_id, current_user.id, message) diff --git a/static/css/style.css b/static/css/style.css index 87c8cc8..aa6b431 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -449,11 +449,13 @@ body { border-radius: var(--border-radius); background: #ffffff; padding: 0.5rem; + flex: 1; + min-height: 400px; } .clipboard-drawing-wrap canvas { width: 100%; - height: auto; + height: 100%; border-radius: var(--border-radius); background: #ffffff; display: block; @@ -469,15 +471,15 @@ body { } .clipboard-display.drawing-active .clipboard-drawing-wrap { - display: block; + display: flex; } .clipboard-display.drawing-active { - min-height: 360px; + min-height: 400px; } .clipboard-pane.other-pane .clipboard-display.drawing-active { - min-height: 320px; + min-height: 400px; } .clipboard-container.handwriting-mode { @@ -493,7 +495,7 @@ body { } .clipboard-container.handwriting-mode .clipboard-pane.other-pane .clipboard-display.drawing-active { - min-height: 180px; + min-height: 300px; } .clipboard-textarea { @@ -600,11 +602,16 @@ body { .memory-page { margin-top: 1rem; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; } .memory-pane.standalone-memory { box-shadow: 0 2px 10px rgba(30, 64, 175, 0.12); border-left-color: #4f46e5; + padding: 1rem; + border-radius: var(--border-radius); } .toolbar-btn.link-btn { @@ -660,8 +667,20 @@ body { display: flex; flex-direction: column; gap: 0.5rem; - border-top: 1px solid #e2e8f0; - padding-top: 0.75rem; + margin-top: 0.75rem; +} + +.memory-pane h3 { + font-size: 1.1rem; + color: #1f2937; + margin-bottom: 0.75rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.memory-pane h3 i { + color: #4f46e5; } .memory-form input, @@ -1069,6 +1088,22 @@ body { font-size: 0.9rem; } + .clipboard-drawing-wrap { + min-height: 150px; + } + + .clipboard-display.drawing-active { + min-height: 150px; + } + + .clipboard-pane.other-pane .clipboard-display.drawing-active { + min-height: 150px; + } + + .clipboard-container.handwriting-mode .clipboard-pane.other-pane .clipboard-display.drawing-active { + min-height: 120px; + } + .clipboard-toolbar { flex-direction: column; align-items: flex-start; @@ -1093,7 +1128,8 @@ body { } .memory-pane { - display: none; + display: flex; + flex-direction: column; } .memory-pane.is-open { @@ -1106,6 +1142,8 @@ body { .memory-page { margin-top: 0.5rem; + grid-template-columns: 1fr; + gap: 1rem; } .memory-list { diff --git a/templates/clipboard.html b/templates/clipboard.html index 40656e2..f82e055 100644 --- a/templates/clipboard.html +++ b/templates/clipboard.html @@ -417,19 +417,18 @@

{{ _('Your Area (%(username)s)', username=curren lastY = e.clientY - rect.top; myDrawingCtx.beginPath(); myDrawingCtx.moveTo(lastX, lastY); - emitDrawing({ type: 'stroke_start', x: lastX, y: lastY }); } function drawStroke(x, y) { myDrawingCtx.lineWidth = 4; + myDrawingCtx.lineCap = 'round'; + myDrawingCtx.lineJoin = 'round'; myDrawingCtx.strokeStyle = '#1f2937'; myDrawingCtx.lineTo(x, y); myDrawingCtx.stroke(); myDrawingCtx.beginPath(); myDrawingCtx.moveTo(x, y); localDrawingHasContent = true; - lastX = x; - lastY = y; emitDrawing({ type: 'draw', x, y }); } @@ -438,8 +437,6 @@

{{ _('Your Area (%(username)s)', username=curren const rect = myDrawingCanvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; - myDrawingCtx.beginPath(); - myDrawingCtx.moveTo(lastX, lastY); drawStroke(x, y); lastX = x; lastY = y; @@ -487,7 +484,6 @@

{{ _('Your Area (%(username)s)', username=curren lastY = touch.clientY - rect.top; myDrawingCtx.beginPath(); myDrawingCtx.moveTo(lastX, lastY); - emitDrawing({ type: 'stroke_start', x: lastX, y: lastY }); }, { passive: false }); myDrawingCanvas.addEventListener('touchmove', (event) => { @@ -497,9 +493,9 @@

{{ _('Your Area (%(username)s)', username=curren const rect = myDrawingCanvas.getBoundingClientRect(); const x = touch.clientX - rect.left; const y = touch.clientY - rect.top; - myDrawingCtx.beginPath(); - myDrawingCtx.moveTo(lastX, lastY); drawStroke(x, y); + lastX = x; + lastY = y; }, { passive: false }); myDrawingCanvas.addEventListener('touchend', (event) => { @@ -521,7 +517,7 @@

{{ _('Your Area (%(username)s)', username=curren drawingPoints.push(drawing); } otherLastStroke = Date.now(); - if (drawing.type === 'stroke_start' || drawing.type === 'draw') { + if (drawing.type === 'draw') { setRemoteDrawingActive(true); } lastRemoteTimestamp = drawing.ts || lastRemoteTimestamp; @@ -586,21 +582,22 @@

{{ _('Your Area (%(username)s)', username=curren } return; } - if (drawing.type === 'stroke_end' || drawing.type === 'stroke_start') { + if (drawing.type === 'stroke_end') { ctx.beginPath(); - if (drawing.type === 'stroke_start') { - ctx.moveTo(drawing.x, drawing.y); - } return; } - ctx.lineWidth = 4; - ctx.strokeStyle = '#1f2937'; - ctx.lineTo(drawing.x, drawing.y); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(drawing.x, drawing.y); - if (ctx === otherDrawingCtx) { - otherDrawingHasContent = true; + if (drawing.type === 'draw') { + ctx.lineWidth = 4; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.strokeStyle = '#1f2937'; + ctx.lineTo(drawing.x, drawing.y); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(drawing.x, drawing.y); + if (ctx === otherDrawingCtx) { + otherDrawingHasContent = true; + } } } diff --git a/templates/memories.html b/templates/memories.html index 17fff1d..db53f9c 100644 --- a/templates/memories.html +++ b/templates/memories.html @@ -13,6 +13,7 @@

{{ _('Memories with %(username)s', username
+

{{ _('Memory List') }}

{% if memories %} {% for memory in memories %} @@ -45,6 +46,9 @@

{{ _('Memories with %(username)s', username

{{ _('No memories yet. Add one below!') }}

{% endif %}

+
+
+

{{ _('Add New Memory') }}

diff --git a/translations/zh/LC_MESSAGES/messages.mo b/translations/zh/LC_MESSAGES/messages.mo index cd71548..2b76241 100644 Binary files a/translations/zh/LC_MESSAGES/messages.mo and b/translations/zh/LC_MESSAGES/messages.mo differ diff --git a/translations/zh/LC_MESSAGES/messages.po b/translations/zh/LC_MESSAGES/messages.po index 054fc63..7bb2dc8 100644 --- a/translations/zh/LC_MESSAGES/messages.po +++ b/translations/zh/LC_MESSAGES/messages.po @@ -336,6 +336,14 @@ msgstr "还没有回忆,下面添加一个吧!" msgid "Date" msgstr "日期" +#: templates/memories.html:16 +msgid "Memory List" +msgstr "回忆列表" + +#: templates/memories.html:42 +msgid "Add New Memory" +msgstr "添加新回忆" + #: templates/memories.html:51 msgid "Memory" msgstr "回忆" @@ -367,6 +375,63 @@ msgstr "无法确认回忆。" #: templates/memories.html:117 msgid "Memory approved!" msgstr "回忆已确认!" + +#: app.py:206 +msgid "Invalid credentials" +msgstr "无效的凭证" + +#: app.py:249 +msgid "Invalid memory text" +msgstr "无效的回忆内容" + +#: app.py:252 +msgid "Invalid memory date" +msgstr "无效的回忆日期" + +#: app.py:242 app.py:263 app.py:296 app.py:307 app.py:322 app.py:337 app.py:353 +msgid "Connection not found" +msgstr "未找到连接" + +#: app.py:266 +msgid "Unable to approve memory" +msgstr "无法确认回忆" + +#: app.py:379 +msgid "Invalid password" +msgstr "无效的密码" + +#: app.py:618 app.py:724 app.py:752 app.py:776 +msgid "Access denied" +msgstr "拒绝访问" + +#: app.py:623 +msgid "Answer required" +msgstr "需要提供答案" + +#: app.py:628 +msgid "No active session" +msgstr "没有活动会话" + +#: app.py:631 +msgid "Waiting for partner to join" +msgstr "等待对方加入" + +#: app.py:634 +msgid "Session complete" +msgstr "会话已完成" + +#: app.py:728 +msgid "Invalid session" +msgstr "无效的会话" + +#: app.py:732 +msgid "Session is not active" +msgstr "会话未激活" + +#: app.py:780 +msgid "Invalid message" +msgstr "无效的消息" + #: templates/dashboard.html:58 msgid "No connections yet. Share your invite token to connect with others!" msgstr "还没有连接。分享您的邀请令牌以与他人连接!"