27 Oct 2026

How to create web sms

How to create web sms

  • Raymond Chauke

    Senior Engineer

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>WEB SMS "Chat" WEB APP</title>
<style>
:root{--bg:#f6f7f9;--surface:#fff;--text:#1b1f24;--muted:#6b7280;--line:#e5e7eb;--accent:#2563eb;--accent-text:#fff;--mine:#dbeafe;--mine-text:#0b2a66;
box-sizing:border-box;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--bg:#0f1318;--surface:#171c23;--text:#e8eaed;--muted:#98a2b3;--line:#2a313b;--accent:#4c8dff;--accent-text:#06132e;--mine:#1e3a6b;--mine-text:#e6efff;color-scheme:dark}}
:root[data-theme="dark"]{--bg:#0f1318;--surface:#171c23;--text:#e8eaed;--muted:#98a2b3;--line:#2a313b;--accent:#4c8dff;--accent-text:#06132e;--mine:#1e3a6b;--mine-text:#e6efff;color-scheme:dark}
*{box-sizing:border-box}
html,body{height:100%;margin:0}
body{background:var(--bg);color:var(--text);font:16px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;display:flex;flex-direction:column}
header{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--surface);border-bottom:1px solid var(--line)}
h1{font-size:17px;margin:0;flex:1}
nav{display:flex;gap:6px;overflow-x:auto;padding:8px 16px;background:var(--surface);border-bottom:1px solid var(--line)}
nav button{border:1px solid var(--line);background:var(--bg);color:var(--text);padding:6px 14px;border-radius:999px;cursor:pointer;white-space:nowrap;font:inherit;font-size:14px}
nav button[aria-pressed="true"]{background:var(--accent);color:var(--accent-text);border-color:var(--accent)}
#log{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px}
.msg{max-width:min(80%,520px);background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:8px 12px;align-self:flex-start;word-wrap:break-word;overflow-wrap:anywhere}
.msg.me{align-self:flex-end;background:var(--mine);color:var(--mine-text);border-color:transparent}
.who{font-size:12px;color:var(--muted);margin-bottom:2px}
.msg.me .who{color:inherit;opacity:.75}
.txt{white-space:pre-wrap}
.note{margin:auto;text-align:center;color:var(--muted);max-width:340px}
form,.bar{display:flex;gap:8px;padding:10px 16px;background:var(--surface);border-top:1px solid var(--line)}
#in{flex:1;font:inherit;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:var(--text);resize:none;max-height:120px}
#send{font:inherit;border:0;background:var(--accent);color:var(--accent-text);padding:0 18px;border-radius:10px;cursor:pointer;font-weight:600}
#send:disabled,#in:disabled{opacity:.5;cursor:not-allowed}
#err{color:#b42318;font-size:13px;padding:0 16px 6px;min-height:0}
</style>
</head>
<body>
<header><h1 id="title"># general</h1><span id="status" style="font-size:13px;color:var(--muted)">Connecting…</span></header>
<nav id="rooms" aria-label="Channels"></nav>
<div id="log" aria-live="polite"><div class="note">Loading messages…</div></div>
<div id="err"></div>
<div class="bar"><textarea id="in" rows="1" maxlength="1000" placeholder="Write a message…" disabled></textarea><button id="send" disabled>Send</button></div>
<script>
(function(){
var ROOMS=["general","announcements","support","random"];
var room=ROOMS[0],db=null,user=null,myId=null,unsub=null,names={};
var log=document.getElementById("log"),input=document.getElementById("in"),send=document.getElementById("send"),
    err=document.getElementById("err"),statusEl=document.getElementById("status"),title=document.getElementById("title");

function note(t){log.innerHTML="";var d=document.createElement("div");d.className="note";d.textContent=t;log.appendChild(d)}

function drawRooms(){
  var nav=document.getElementById("rooms");nav.innerHTML="";
  ROOMS.forEach(function(r){
    var b=document.createElement("button");b.textContent="# "+r;b.setAttribute("aria-pressed",r===room);
    b.onclick=function(){if(r!==room){room=r;drawRooms();subscribe()}};nav.appendChild(b);
  });
  title.textContent="# "+room;
}

async function resolveNames(ids){
  var need=ids.filter(function(i){return !(i in names)});
  if(!need.length||!user)return;
  try{var ps=await user.profiles(need);need.forEach(function(i){names[i]=(ps[i]&&ps[i].name)||""})}catch(e){}
}

async function render(docs){
  var msgs=docs.map(function(d){return Object.assign({id:d.id},d.data())}).reverse();
  var ids=[];msgs.forEach(function(m){if(m.uid&&ids.indexOf(m.uid)<0)ids.push(m.uid)});
  await resolveNames(ids);
  var atBottom=log.scrollHeight-log.scrollTop-log.clientHeight<80;
  log.innerHTML="";
  if(!msgs.length){note("No messages yet. Say hello!");return}
  msgs.forEach(function(m){
    var d=document.createElement("div");d.className="msg"+(m.uid===myId?" me":"");
    var w=document.createElement("div");w.className="who";
    var t=m.ts?new Date(m.ts).toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"}):"";
    w.textContent=(m.uid===myId?"You":(names[m.uid]||"Someone"))+(t?" · "+t:"");
    var x=document.createElement("div");x.className="txt";x.textContent=String(m.text||"");
    d.appendChild(w);d.appendChild(x);log.appendChild(d);
  });
  if(atBottom||log.dataset.first!==room){log.scrollTop=log.scrollHeight;log.dataset.first=room}
}

function subscribe(){
  if(unsub){unsub();unsub=null}
  if(!db)return;
  note("Loading messages…");
  unsub=db.collection("rooms/"+room+"/messages").orderBy("ts","desc").limit(100).onSnapshot(
    function(s){render(s.docs);statusEl.textContent="Live"},
    function(e){statusEl.textContent="Offline";note("Couldn’t load messages ("+e.code+").")}
  );
}

async function post(){
  var text=input.value.trim();if(!text||!db)return;
  send.disabled=true;err.textContent="";
  try{
    await db.collection("rooms/"+room+"/messages").add({uid:myId,text:text.slice(0,1000),ts:Date.now()});
    input.value="";
  }catch(e){
    err.textContent=e.code==="resource_exhausted"?"You’re sending too fast. Please wait a moment.":"Message not sent ("+(e.code||"error")+").";
  }
  send.disabled=false;input.focus();
}
send.onclick=post;
input.addEventListener("keydown",function(e){if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();post()}});

drawRooms();
(async function(){
  try{
    db=await chauke.use("db");user=await chauke.use("user");
    if(!db){statusEl.textContent="Offline";note("Please sign in to chauke.org to join the chat.");return}
    myId=user?await user.id():null;
    var canWrite=user&&user.can?user.can("data.write"):null;
    if(canWrite!==false&&myId){input.disabled=false;send.disabled=false}
    else{input.placeholder="You have read-only access";}
    subscribe();
  }catch(e){statusEl.textContent="Offline";note("Chat is unavailable right now.")}
})();
})();
</script>
</body>
</html>