fix app layouts: remove style tags from content fragments, use component classes

This commit is contained in:
2026-01-20 17:40:38 +01:00
parent 4b47611902
commit 82bc0c78fe
25 changed files with 5350 additions and 537 deletions

View File

@@ -0,0 +1,398 @@
-- messages.lua - Messages app functionality
-- Handles conversation list and individual chats
local messages_doc = nil
local conversations = {}
local current_conversation = nil
local current_messages = {}
-- Avatar colors
local avatar_colors = {
"#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3",
"#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#FF9800"
}
local function getAvatarColor(name)
local sum = 0
for i = 1, #name do
sum = sum + string.byte(name, i)
end
return avatar_colors[(sum % #avatar_colors) + 1]
end
-- Initialize conversations data
local function initConversationsData()
conversations = {
{
id = "1",
name = "Alice Johnson",
phone = "+1 555-0101",
last_message = "Hey! Are you coming to the party tonight?",
time = "2:34 PM",
unread = 2,
messages = {
{sender = "them", text = "Hey!", time = "2:30 PM"},
{sender = "them", text = "What are you up to?", time = "2:31 PM"},
{sender = "me", text = "Not much, just working", time = "2:32 PM"},
{sender = "them", text = "Cool! There's a party at Mike's tonight", time = "2:33 PM"},
{sender = "them", text = "Hey! Are you coming to the party tonight?", time = "2:34 PM"},
}
},
{
id = "2",
name = "Bob Williams",
phone = "+1 555-0201",
last_message = "Thanks for the help yesterday!",
time = "1:15 PM",
unread = 0,
messages = {
{sender = "them", text = "Hey, can you help me with something?", time = "Yesterday"},
{sender = "me", text = "Sure, what do you need?", time = "Yesterday"},
{sender = "them", text = "I need help moving some furniture", time = "Yesterday"},
{sender = "me", text = "No problem, I'll be there at 2", time = "Yesterday"},
{sender = "them", text = "Thanks for the help yesterday!", time = "1:15 PM"},
}
},
{
id = "3",
name = "Carol Davis",
phone = "+1 555-0301",
last_message = "The meeting has been rescheduled to Friday",
time = "Yesterday",
unread = 0,
messages = {
{sender = "them", text = "Hi, are you free for a meeting tomorrow?", time = "Monday"},
{sender = "me", text = "Let me check my calendar", time = "Monday"},
{sender = "me", text = "Yes, I'm free at 3pm", time = "Monday"},
{sender = "them", text = "The meeting has been rescheduled to Friday", time = "Yesterday"},
}
},
{
id = "4",
name = "David Brown",
phone = "+1 555-0401",
last_message = "Can you send me the files?",
time = "Yesterday",
unread = 1,
messages = {
{sender = "them", text = "Hey, do you have the project files?", time = "Yesterday"},
{sender = "me", text = "Which ones?", time = "Yesterday"},
{sender = "them", text = "Can you send me the files?", time = "Yesterday"},
}
},
{
id = "5",
name = "Emma Wilson",
phone = "+1 555-0501",
last_message = "See you at the coffee shop!",
time = "Mon",
unread = 0,
messages = {
{sender = "me", text = "Want to grab coffee later?", time = "Mon"},
{sender = "them", text = "Sure! What time?", time = "Mon"},
{sender = "me", text = "How about 4pm at the usual place?", time = "Mon"},
{sender = "them", text = "See you at the coffee shop!", time = "Mon"},
}
},
{
id = "6",
name = "Frank Miller",
phone = "+1 555-0601",
last_message = "Great game last night!",
time = "Sun",
unread = 0,
messages = {
{sender = "them", text = "Did you watch the game?", time = "Sun"},
{sender = "me", text = "Yes! It was amazing!", time = "Sun"},
{sender = "them", text = "Great game last night!", time = "Sun"},
}
},
{
id = "7",
name = "Grace Lee",
phone = "+1 555-0701",
last_message = "Happy birthday! :)",
time = "Sat",
unread = 0,
messages = {
{sender = "them", text = "Happy birthday! :)", time = "Sat"},
{sender = "me", text = "Thank you so much! :)", time = "Sat"},
}
},
}
end
-- Initialize messages app
function initMessages(doc)
print("[Messages] Initializing...")
messages_doc = doc
initConversationsData()
renderConversations()
end
-- Render conversation list
function renderConversations()
if not messages_doc then return end
local container = messages_doc:GetElementById("conversations-list")
if not container then return end
local html = ""
for _, conv in ipairs(conversations) do
local color = getAvatarColor(conv.name)
local initial = conv.name:sub(1, 1):upper()
local unread_badge = ""
if conv.unread > 0 then
unread_badge = [[<div class="conversation-unread">]] .. conv.unread .. [[</div>]]
end
html = html .. [[
<div class="conversation-item" onclick="openConversation(']] .. conv.id .. [[')">
<div class="conversation-avatar" style="background-color: ]] .. color .. [[;">]] .. initial .. [[</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">]] .. conv.name .. [[</span>
<span class="conversation-time">]] .. conv.time .. [[</span>
</div>
<div class="conversation-preview">]] .. conv.last_message .. [[</div>
</div>
]] .. unread_badge .. [[
</div>
]]
end
container.inner_rml = html
end
-- Open a conversation
function openConversation(conv_id)
print("[Messages] Opening conversation: " .. conv_id)
-- Find conversation
for _, conv in ipairs(conversations) do
if conv.id == conv_id then
current_conversation = conv
current_messages = conv.messages
conv.unread = 0 -- Mark as read
break
end
end
if current_conversation then
-- Store for chat screen
if mosis and mosis.state then
mosis.state.set("current_chat", {
id = current_conversation.id,
name = current_conversation.name,
phone = current_conversation.phone
})
end
-- Navigate to chat
if navigateTo then
navigateTo("chat")
else
-- Inline chat view
showChatInline()
end
end
end
-- Show chat inline
function showChatInline()
if not messages_doc then return end
local list = messages_doc:GetElementById("conversations-container")
local chat = messages_doc:GetElementById("chat-container")
if list and chat then
list.style.display = "none"
chat.style.display = "flex"
renderChat()
end
end
-- Hide chat and return to list
function hideChat()
if not messages_doc then return end
local list = messages_doc:GetElementById("conversations-container")
local chat = messages_doc:GetElementById("chat-container")
if list and chat then
chat.style.display = "none"
list.style.display = "flex"
renderConversations() -- Refresh to update unread counts
end
current_conversation = nil
end
-- Render chat messages
function renderChat()
if not messages_doc or not current_conversation then return end
-- Update header
local name_el = messages_doc:GetElementById("chat-name")
local avatar_el = messages_doc:GetElementById("chat-avatar")
if name_el then
name_el.inner_rml = current_conversation.name
end
if avatar_el then
local color = getAvatarColor(current_conversation.name)
local initial = current_conversation.name:sub(1, 1):upper()
avatar_el.style["background-color"] = color
avatar_el.inner_rml = initial
end
-- Render messages
local container = messages_doc:GetElementById("chat-messages")
if not container then return end
local html = ""
for _, msg in ipairs(current_messages) do
local class = msg.sender == "me" and "message-sent" or "message-received"
html = html .. [[
<div class="message-bubble ]] .. class .. [[">]] .. msg.text .. [[</div>
]]
end
container.inner_rml = html
-- Scroll to bottom
-- Note: RmlUi may need specific handling for scroll
end
-- Send a message
function sendMessage()
if not messages_doc or not current_conversation then return end
local input = messages_doc:GetElementById("message-input")
if not input then return end
local text = input.value or ""
if text == "" then return end
print("[Messages] Sending: " .. text)
-- Add message to current conversation
table.insert(current_messages, {
sender = "me",
text = text,
time = "Just now"
})
-- Update conversation preview
current_conversation.last_message = text
current_conversation.time = "Just now"
-- Clear input
input.value = ""
-- Re-render chat
renderChat()
-- Simulate reply after delay
if setTimeout then
setTimeout(function()
simulateReply()
end, 2000 + math.random(1000, 3000))
end
end
-- Simulate a reply
function simulateReply()
if not current_conversation then return end
local replies = {
"That's great!",
"I see",
"Sounds good!",
"Let me think about it",
"Sure thing!",
"OK!",
"Thanks!",
"Got it",
"Nice!",
"Interesting..."
}
local reply = replies[math.random(#replies)]
table.insert(current_messages, {
sender = "them",
text = reply,
time = "Just now"
})
current_conversation.last_message = reply
current_conversation.time = "Just now"
renderChat()
end
-- Handle input keypress (for Enter to send)
function onMessageKeypress(event)
if event.key == "Return" or event.key == "Enter" then
sendMessage()
return true
end
return false
end
-- Start new conversation
function newConversation()
print("[Messages] New conversation")
if showToast then
showToast("New message")
end
end
-- Search conversations
function searchConversations(query)
print("[Messages] Searching: " .. query)
-- TODO: Implement search filtering
end
-- Delete conversation
function deleteConversation(conv_id)
print("[Messages] Deleting conversation: " .. conv_id)
for i, conv in ipairs(conversations) do
if conv.id == conv_id then
table.remove(conversations, i)
break
end
end
renderConversations()
if showToast then
showToast("Conversation deleted")
end
end
-- Call contact from chat
function callFromChat()
if not current_conversation then return end
print("[Messages] Calling from chat: " .. current_conversation.name)
if mosis and mosis.state then
mosis.state.set("current_call", {
number = current_conversation.phone,
name = current_conversation.name
})
end
if navigateTo then
navigateTo("calling")
else
if showToast then
showToast("Calling " .. current_conversation.name)
end
end
end

View File

@@ -6,6 +6,7 @@
<link type="text/rcss" href="../../ui/layout.rcss"/>
<script src="../../scripts/navigation.lua"></script>
<script src="../../scripts/layout.lua"></script>
<script src="messages.lua"></script>
<title>Messages</title>
<style>
.conversations-list {
@@ -84,9 +85,131 @@
justify-content: center;
margin-left: 8px;
}
/* Chat View */
#chat-container {
display: none;
flex-direction: column;
flex: 1;
}
.chat-header {
display: flex;
align-items: center;
padding: 8px 16px;
background-color: #1E1E1E;
}
.chat-avatar {
width: 48px;
height: 48px;
border-radius: 24px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #000000;
}
.chat-header-info {
flex: 1;
}
.chat-header-name {
font-size: 18px;
font-weight: 500;
color: #FFFFFF;
}
.chat-header-status {
font-size: 14px;
color: #4CAF50;
}
.chat-messages {
flex: 1;
overflow: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
}
.message-bubble {
max-width: 75%;
padding: 12px 16px;
border-radius: 18px;
font-size: 18px;
line-height: 1.4;
}
.message-sent {
align-self: flex-end;
background-color: #BB86FC;
color: #000000;
border-bottom-right-radius: 4px;
}
.message-received {
align-self: flex-start;
background-color: #2D2D2D;
color: #FFFFFF;
border-bottom-left-radius: 4px;
}
.chat-input-bar {
display: flex;
align-items: center;
padding: 8px 16px;
background-color: #1E1E1E;
gap: 8px;
}
.chat-input {
flex: 1;
padding: 12px 18px;
background-color: #2D2D2D;
border-radius: 24px;
color: #FFFFFF;
font-size: 18px;
}
.chat-input:hover {
background-color: #3D3D3D;
}
.chat-input:focus {
background-color: #353535;
}
.chat-send-btn {
width: 56px;
height: 56px;
border-radius: 28px;
background-color: #BB86FC;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.chat-send-btn:hover {
background-color: #9C64FC;
}
.chat-send-btn:active {
background-color: #7C44DC;
}
.chat-send-btn img {
width: 28px;
height: 28px;
pointer-events: none;
}
</style>
</head>
<body class="app-screen" onload="initLayout(document)" data-model="messages">
<body class="app-screen" onload="initLayout(document); initMessages(document)" data-model="messages">
<!-- System Status Bar -->
<div class="system-status-bar">
<span id="status-time" class="system-status-time">12:30</span>
@@ -97,123 +220,79 @@
</div>
</div>
<!-- App Bar -->
<div class="app-bar">
<div class="app-bar-back" onclick="goBack()">
<img src="../../icons/back.tga"/>
<!-- Conversations List Container -->
<div id="conversations-container" style="display: flex; flex-direction: column; flex: 1;">
<!-- App Bar -->
<div class="app-bar">
<div class="app-bar-back" onclick="goBack()">
<img src="../../icons/back.tga"/>
</div>
<span class="app-bar-title">Messages</span>
<div class="app-bar-actions">
<div class="app-bar-action">
<img src="../../icons/search.tga"/>
</div>
</div>
</div>
<span class="app-bar-title">Messages</span>
<div class="app-bar-actions">
<div class="app-bar-action">
<img src="../../icons/search.tga"/>
<!-- Conversations List -->
<div class="app-content with-nav">
<div class="conversations-list" id="conversations-list">
<!-- Populated by Lua -->
</div>
</div>
<!-- FAB -->
<div class="btn-fab" onclick="newConversation()">
<img src="../../icons/add.tga" style="width: 32px; height: 32px;"/>
</div>
<!-- System Navigation Bar -->
<div class="system-nav-bar">
<div class="system-nav-btn" onclick="onBackPressed()">
<img src="../../icons/back.tga"/>
</div>
<div class="system-nav-home" onclick="onHomePressed()"></div>
<div class="system-nav-btn" onclick="onRecentPressed()">
<img src="../../icons/menu.tga"/>
</div>
</div>
</div>
<!-- Conversations List -->
<div class="app-content with-nav">
<div class="conversations-list">
<!-- Alice -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #E91E63;">A</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">Alice Johnson</span>
<span class="conversation-time">2:34 PM</span>
</div>
<div class="conversation-preview">Hey! Are you coming to the party tonight?</div>
</div>
<div class="conversation-unread">2</div>
<!-- Chat Container -->
<div id="chat-container">
<!-- Chat Header -->
<div class="app-bar">
<div class="app-bar-back" onclick="hideChat()">
<img src="../../icons/back.tga"/>
</div>
<!-- Bob -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #2196F3;">B</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">Bob Williams</span>
<span class="conversation-time">1:15 PM</span>
</div>
<div class="conversation-preview">Thanks for the help yesterday!</div>
</div>
<div class="chat-avatar" id="chat-avatar" style="background-color: #4CAF50;">J</div>
<div class="chat-header-info">
<div class="chat-header-name" id="chat-name">Contact</div>
<div class="chat-header-status">Online</div>
</div>
<!-- Carol -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #4CAF50;">C</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">Carol Davis</span>
<span class="conversation-time">Yesterday</span>
</div>
<div class="conversation-preview">The meeting has been rescheduled to Friday</div>
</div>
<div class="btn-icon" onclick="callFromChat()">
<img src="../../icons/phone.tga" style="width: 32px; height: 32px;"/>
</div>
<!-- David -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #FF9800;">D</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">David Brown</span>
<span class="conversation-time">Yesterday</span>
</div>
<div class="conversation-preview">Can you send me the files?</div>
</div>
<div class="conversation-unread">1</div>
</div>
<!-- Emma -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #9C27B0;">E</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">Emma Wilson</span>
<span class="conversation-time">Mon</span>
</div>
<div class="conversation-preview">See you at the coffee shop!</div>
</div>
</div>
<!-- Frank -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #00BCD4;">F</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">Frank Miller</span>
<span class="conversation-time">Sun</span>
</div>
<div class="conversation-preview">Great game last night!</div>
</div>
</div>
<!-- Grace -->
<div class="conversation-item">
<div class="conversation-avatar" style="background-color: #673AB7;">G</div>
<div class="conversation-content">
<div class="conversation-header">
<span class="conversation-name">Grace Lee</span>
<span class="conversation-time">Sat</span>
</div>
<div class="conversation-preview">Happy birthday! 🎂</div>
</div>
<div class="btn-icon">
<img src="../../icons/more.tga" style="width: 32px; height: 32px;"/>
</div>
</div>
</div>
<!-- FAB -->
<div class="btn-fab">
<img src="../../icons/add.tga" style="width: 32px; height: 32px;"/>
</div>
<!-- System Navigation Bar -->
<div class="system-nav-bar">
<div class="system-nav-btn" onclick="onBackPressed()">
<img src="../../icons/back.tga"/>
<!-- Messages -->
<div class="chat-messages" id="chat-messages">
<!-- Populated by Lua -->
</div>
<div class="system-nav-home" onclick="onHomePressed()"></div>
<div class="system-nav-btn" onclick="onRecentPressed()">
<img src="../../icons/menu.tga"/>
<!-- Input Bar -->
<div class="chat-input-bar">
<div class="btn-icon" style="width: 48px; height: 48px;">
<img src="../../icons/add.tga" style="width: 28px; height: 28px;"/>
</div>
<input class="chat-input" type="text" placeholder="Type a message..." id="message-input"/>
<div class="chat-send-btn" onclick="sendMessage()">
<img src="../../icons/send.tga"/>
</div>
</div>
</div>
</body>