Rename replay → initChannelState
check / check (push) Successful in 2m20s

Rename the query parameter, function, and all related comments
from 'replay' to 'initChannelState' to better reflect the
semantics: the server initializes channel state for the
reconnecting client rather than replaying past events.
This commit is contained in:
clawbot authored and user committed 2026-03-09 17:00:56 -07:00
1 parent 096fb2b207
commit 78d657111b
5 files changed
+23 -21

No files matched your search

+3 -3
View File
@@ -1036,7 +1036,7 @@ Return the current user's session state.
| Parameter | Type | Default | Description |
|-----------|--------|---------|-------------|
| `replay` | string | (none) | When set to `1`, enqueues synthetic JOIN + TOPIC + NAMES messages for every channel the session belongs to into the calling client's queue. Used by the SPA on reconnect to restore channel tabs without re-sending JOIN commands. |
| `initChannelState` | string | (none) | When set to `1`, enqueues synthetic JOIN + TOPIC + NAMES messages for every channel the session belongs to into the calling client's queue. Used by the SPA on reconnect to restore channel tabs without re-sending JOIN commands. |
**Response:** `200 OK`
```json
@@ -1070,9 +1070,9 @@ curl -s http://localhost:8080/api/v1/state \
-H "Authorization: Bearer $TOKEN" | jq .
```
**Reconnect with channel replay:**
**Reconnect with channel state initialization:**
```bash
curl -s "http://localhost:8080/api/v1/state?replay=1" \
curl -s "http://localhost:8080/api/v1/state?initChannelState=1" \
-H "Authorization: Bearer $TOKEN" | jq .
```
+10 -9
View File
@@ -444,10 +444,11 @@ func (hdlr *Handlers) enqueueNumeric(
}
// HandleState returns the current session's info and
// channels. When called with ?replay=1, it also enqueues
// synthetic JOIN + TOPIC + NAMES messages for every channel
// the session belongs to so that a reconnecting client can
// rebuild its channel tabs from the message stream.
// channels. When called with ?initChannelState=1, it also
// enqueues synthetic JOIN + TOPIC + NAMES messages for
// every channel the session belongs to so that a
// reconnecting client can rebuild its channel tabs from
// the message stream.
func (hdlr *Handlers) HandleState() http.HandlerFunc {
return func(
writer http.ResponseWriter,
@@ -475,8 +476,8 @@ func (hdlr *Handlers) HandleState() http.HandlerFunc {
return
}
if request.URL.Query().Get("replay") == "1" {
hdlr.replayChannelState(
if request.URL.Query().Get("initChannelState") == "1" {
hdlr.initChannelState(
request, clientID, sessionID, nick,
)
}
@@ -489,12 +490,12 @@ func (hdlr *Handlers) HandleState() http.HandlerFunc {
}
}
// replayChannelState enqueues synthetic JOIN messages and
// initChannelState enqueues synthetic JOIN messages and
// join-numerics (TOPIC, NAMES) for every channel the
// session belongs to. Messages are enqueued only to the
// specified client so other clients/sessions are not
// affected.
func (hdlr *Handlers) replayChannelState(
func (hdlr *Handlers) initChannelState(
request *http.Request,
clientID, sessionID int64,
nick string,
@@ -516,7 +517,7 @@ func (hdlr *Handlers) replayChannelState(
)
if insErr != nil {
hdlr.log.Error(
"replay: insert JOIN",
"initChannelState: insert JOIN",
"error", insErr,
)
+3 -3
View File
@@ -182,9 +182,9 @@ func (hdlr *Handlers) handleLogin(
request, clientID, sessionID, payload.Nick,
)
// Replay channel state so the new client knows which
// channels the session already belongs to.
hdlr.replayChannelState(
// Initialize channel state so the new client knows
// which channels the session already belongs to.
hdlr.initChannelState(
request, clientID, sessionID, payload.Nick,
)
+1 -1
View File
@@ -1,2 +1,2 @@
var ie,T,De,pt,G,$e,He,Re,Le,ve,me,he,mt,ae={},se=[],ht=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,_e=Array.isArray;function J(e,t){for(var n in t)e[n]=t[n];return e}function be(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function m(e,t,n){var a,s,o,_={};for(o in t)o=="key"?a=t[o]:o=="ref"?s=t[o]:_[o]=t[o];if(arguments.length>2&&(_.children=arguments.length>3?ie.call(arguments,2):n),typeof e=="function"&&e.defaultProps!=null)for(o in e.defaultProps)_[o]===void 0&&(_[o]=e.defaultProps[o]);return re(e,_,a,s,null)}function re(e,t,n,a,s){var o={type:e,props:t,key:n,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:s??++De,__i:-1,__u:0};return s==null&&T.vnode!=null&&T.vnode(o),o}function le(e){return e.children}function oe(e,t){this.props=e,this.context=t}function Q(e,t){if(t==null)return e.__?Q(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null)return n.__e;return typeof e.type=="function"?Q(e):null}function yt(e){if(e.__P&&e.__d){var t=e.__v,n=t.__e,a=[],s=[],o=J({},t);o.__v=t.__v+1,T.vnode&&T.vnode(o),ke(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[n]:null,a,n??Q(t),!!(32&t.__u),s),o.__v=t.__v,o.__.__k[o.__i]=o,je(a,o,s),t.__e=t.__=null,o.__e!=n&&We(o)}}function We(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),We(e)}function Me(e){(!e.__d&&(e.__d=!0)&&G.push(e)&&!ce.__r++||$e!=T.debounceRendering)&&(($e=T.debounceRendering)||He)(ce)}function ce(){for(var e,t=1;G.length;)G.length>t&&G.sort(Re),e=G.shift(),t=G.length,yt(e);ce.__r=0}function Fe(e,t,n,a,s,o,_,u,y,f,k){var c,h,v,C,$,A,g,w=a&&a.__k||se,D=t.length;for(y=vt(n,t,w,y,D),c=0;c<D;c++)(v=n.__k[c])!=null&&(h=v.__i!=-1&&w[v.__i]||ae,v.__i=c,A=ke(e,v,h,s,o,_,u,y,f,k),C=v.__e,v.ref&&h.ref!=v.ref&&(h.ref&&ge(h.ref,null,v),k.push(v.ref,v.__c||C,v)),$==null&&C!=null&&($=C),(g=!!(4&v.__u))||h.__k===v.__k?y=Je(v,y,e,g):typeof v.type=="function"&&A!==void 0?y=A:C&&(y=C.nextSibling),v.__u&=-7);return n.__e=$,y}function vt(e,t,n,a,s){var o,_,u,y,f,k=n.length,c=k,h=0;for(e.__k=new Array(s),o=0;o<s;o++)(_=t[o])!=null&&typeof _!="boolean"&&typeof _!="function"?(typeof _=="string"||typeof _=="number"||typeof _=="bigint"||_.constructor==String?_=e.__k[o]=re(null,_,null,null,null):_e(_)?_=e.__k[o]=re(le,{children:_},null,null,null):_.constructor===void 0&&_.__b>0?_=e.__k[o]=re(_.type,_.props,_.key,_.ref?_.ref:null,_.__v):e.__k[o]=_,y=o+h,_.__=e,_.__b=e.__b+1,u=null,(f=_.__i=bt(_,n,y,c))!=-1&&(c--,(u=n[f])&&(u.__u|=2)),u==null||u.__v==null?(f==-1&&(s>k?h--:s<k&&h++),typeof _.type!="function"&&(_.__u|=4)):f!=y&&(f==y-1?h--:f==y+1?h++:(f>y?h--:h++,_.__u|=4))):e.__k[o]=null;if(c)for(o=0;o<k;o++)(u=n[o])!=null&&(2&u.__u)==0&&(u.__e==a&&(a=Q(u)),qe(u,u));return a}function Je(e,t,n,a){var s,o;if(typeof e.type=="function"){for(s=e.__k,o=0;s&&o<s.length;o++)s[o]&&(s[o].__=e,t=Je(s[o],t,n,a));return t}e.__e!=t&&(a&&(t&&e.type&&!t.parentNode&&(t=Q(e)),n.insertBefore(e.__e,t||null)),t=e.__e);do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function bt(e,t,n,a){var s,o,_,u=e.key,y=e.type,f=t[n],k=f!=null&&(2&f.__u)==0;if(f===null&&u==null||k&&u==f.key&&y==f.type)return n;if(a>(k?1:0)){for(s=n-1,o=n+1;s>=0||o<t.length;)if((f=t[_=s>=0?s--:o++])!=null&&(2&f.__u)==0&&u==f.key&&y==f.type)return _}return-1}function Ee(e,t,n){t[0]=="-"?e.setProperty(t,n??""):e[t]=n==null?"":typeof n!="number"||ht.test(t)?n:n+"px"}function ne(e,t,n,a,s){var o,_;e:if(t=="style")if(typeof n=="string")e.style.cssText=n;else{if(typeof a=="string"&&(e.style.cssText=a=""),a)for(t in a)n&&t in n||Ee(e.style,t,"");if(n)for(t in n)a&&n[t]==a[t]||Ee(e.style,t,n[t])}else if(t[0]=="o"&&t[1]=="n")o=t!=(t=t.replace(Le,"$1")),_=t.toLowerCase(),t=_ in e||t=="onFocusOut"||t=="onFocusIn"?_.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+o]=n,n?a?n.u=a.u:(n.u=ve,e.addEventListener(t,o?he:me,o)):e.removeEventListener(t,o?he:me,o);else{if(s=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&n==1?"":n))}}function Ue(e){return function(t){if(this.l){var n=this.l[t.type+e];if(t.t==null)t.t=ve++;else if(t.t<n.u)return;return n(T.event?T.event(t):t)}}}function ke(e,t,n,a,s,o,_,u,y,f){var k,c,h,v,C,$,A,g,w,D,M,V,q,L,B,E=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(y=!!(32&n.__u),o=[u=t.__e=n.__e]),(k=T.__b)&&k(t);e:if(typeof E=="function")try{if(g=t.props,w="prototype"in E&&E.prototype.render,D=(k=E.contextType)&&a[k.__c],M=k?D?D.props.value:k.__:a,n.__c?A=(c=t.__c=n.__c).__=c.__E:(w?t.__c=c=new E(g,M):(t.__c=c=new oe(g,M),c.constructor=E,c.render=gt),D&&D.sub(c),c.state||(c.state={}),c.__n=a,h=c.__d=!0,c.__h=[],c._sb=[]),w&&c.__s=Line truncated
var ie,T,De,pt,G,$e,He,Re,Le,ve,me,he,mt,ae={},se=[],ht=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,_e=Array.isArray;function J(e,t){for(var n in t)e[n]=t[n];return e}function be(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function m(e,t,n){var a,s,o,_={};for(o in t)o=="key"?a=t[o]:o=="ref"?s=t[o]:_[o]=t[o];if(arguments.length>2&&(_.children=arguments.length>3?ie.call(arguments,2):n),typeof e=="function"&&e.defaultProps!=null)for(o in e.defaultProps)_[o]===void 0&&(_[o]=e.defaultProps[o]);return re(e,_,a,s,null)}function re(e,t,n,a,s){var o={type:e,props:t,key:n,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:s??++De,__i:-1,__u:0};return s==null&&T.vnode!=null&&T.vnode(o),o}function le(e){return e.children}function oe(e,t){this.props=e,this.context=t}function Q(e,t){if(t==null)return e.__?Q(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null)return n.__e;return typeof e.type=="function"?Q(e):null}function yt(e){if(e.__P&&e.__d){var t=e.__v,n=t.__e,a=[],s=[],o=J({},t);o.__v=t.__v+1,T.vnode&&T.vnode(o),ke(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[n]:null,a,n??Q(t),!!(32&t.__u),s),o.__v=t.__v,o.__.__k[o.__i]=o,je(a,o,s),t.__e=t.__=null,o.__e!=n&&We(o)}}function We(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),We(e)}function Me(e){(!e.__d&&(e.__d=!0)&&G.push(e)&&!ce.__r++||$e!=T.debounceRendering)&&(($e=T.debounceRendering)||He)(ce)}function ce(){for(var e,t=1;G.length;)G.length>t&&G.sort(Re),e=G.shift(),t=G.length,yt(e);ce.__r=0}function Fe(e,t,n,a,s,o,_,u,y,f,k){var c,h,v,C,$,A,g,w=a&&a.__k||se,D=t.length;for(y=vt(n,t,w,y,D),c=0;c<D;c++)(v=n.__k[c])!=null&&(h=v.__i!=-1&&w[v.__i]||ae,v.__i=c,A=ke(e,v,h,s,o,_,u,y,f,k),C=v.__e,v.ref&&h.ref!=v.ref&&(h.ref&&ge(h.ref,null,v),k.push(v.ref,v.__c||C,v)),$==null&&C!=null&&($=C),(g=!!(4&v.__u))||h.__k===v.__k?y=Je(v,y,e,g):typeof v.type=="function"&&A!==void 0?y=A:C&&(y=C.nextSibling),v.__u&=-7);return n.__e=$,y}function vt(e,t,n,a,s){var o,_,u,y,f,k=n.length,c=k,h=0;for(e.__k=new Array(s),o=0;o<s;o++)(_=t[o])!=null&&typeof _!="boolean"&&typeof _!="function"?(typeof _=="string"||typeof _=="number"||typeof _=="bigint"||_.constructor==String?_=e.__k[o]=re(null,_,null,null,null):_e(_)?_=e.__k[o]=re(le,{children:_},null,null,null):_.constructor===void 0&&_.__b>0?_=e.__k[o]=re(_.type,_.props,_.key,_.ref?_.ref:null,_.__v):e.__k[o]=_,y=o+h,_.__=e,_.__b=e.__b+1,u=null,(f=_.__i=bt(_,n,y,c))!=-1&&(c--,(u=n[f])&&(u.__u|=2)),u==null||u.__v==null?(f==-1&&(s>k?h--:s<k&&h++),typeof _.type!="function"&&(_.__u|=4)):f!=y&&(f==y-1?h--:f==y+1?h++:(f>y?h--:h++,_.__u|=4))):e.__k[o]=null;if(c)for(o=0;o<k;o++)(u=n[o])!=null&&(2&u.__u)==0&&(u.__e==a&&(a=Q(u)),qe(u,u));return a}function Je(e,t,n,a){var s,o;if(typeof e.type=="function"){for(s=e.__k,o=0;s&&o<s.length;o++)s[o]&&(s[o].__=e,t=Je(s[o],t,n,a));return t}e.__e!=t&&(a&&(t&&e.type&&!t.parentNode&&(t=Q(e)),n.insertBefore(e.__e,t||null)),t=e.__e);do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function bt(e,t,n,a){var s,o,_,u=e.key,y=e.type,f=t[n],k=f!=null&&(2&f.__u)==0;if(f===null&&u==null||k&&u==f.key&&y==f.type)return n;if(a>(k?1:0)){for(s=n-1,o=n+1;s>=0||o<t.length;)if((f=t[_=s>=0?s--:o++])!=null&&(2&f.__u)==0&&u==f.key&&y==f.type)return _}return-1}function Ee(e,t,n){t[0]=="-"?e.setProperty(t,n??""):e[t]=n==null?"":typeof n!="number"||ht.test(t)?n:n+"px"}function ne(e,t,n,a,s){var o,_;e:if(t=="style")if(typeof n=="string")e.style.cssText=n;else{if(typeof a=="string"&&(e.style.cssText=a=""),a)for(t in a)n&&t in n||Ee(e.style,t,"");if(n)for(t in n)a&&n[t]==a[t]||Ee(e.style,t,n[t])}else if(t[0]=="o"&&t[1]=="n")o=t!=(t=t.replace(Le,"$1")),_=t.toLowerCase(),t=_ in e||t=="onFocusOut"||t=="onFocusIn"?_.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+o]=n,n?a?n.u=a.u:(n.u=ve,e.addEventListener(t,o?he:me,o)):e.removeEventListener(t,o?he:me,o);else{if(s=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&n==1?"":n))}}function Ue(e){return function(t){if(this.l){var n=this.l[t.type+e];if(t.t==null)t.t=ve++;else if(t.t<n.u)return;return n(T.event?T.event(t):t)}}}function ke(e,t,n,a,s,o,_,u,y,f){var k,c,h,v,C,$,A,g,w,D,M,V,q,L,B,E=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(y=!!(32&n.__u),o=[u=t.__e=n.__e]),(k=T.__b)&&k(t);e:if(typeof E=="function")try{if(g=t.props,w="prototype"in E&&E.prototype.render,D=(k=E.contextType)&&a[k.__c],M=k?D?D.props.value:k.__:a,n.__c?A=(c=t.__c=n.__c).__=c.__E:(w?t.__c=c=new E(g,M):(t.__c=c=new oe(g,M),c.constructor=E,c.render=gt),D&&D.sub(c),c.state||(c.state={}),c.__n=a,h=c.__d=!0,c.__h=[],c._sb=[]),w&&c.__s=Line truncated
`):"",p={id:r.id,ts:r.ts,from:r.from,to:r.to,command:r.command};switch(r.command){case"PRIVMSG":case"NOTICE":{let l=i,d=!1;Nt(l)&&(l=Pt(l),d=!0);let b={...p,text:l,system:!1,isAction:d},F=r.to;if(F&&F.startsWith("#"))N(F,b);else{let H=r.from===X.current?r.to:r.from;o(pe=>pe.find(Pe=>Pe.type==="dm"&&Pe.name===H)?pe:[...pe,{type:"dm",name:H}]),N(H,b)}break}case"JOIN":{let l=`${r.from} has joined ${r.to}`;r.to&&N(r.to,{...p,text:l,system:!0}),r.to&&r.to.startsWith("#")&&(r.from===X.current&&o(d=>d.find(b=>b.type==="channel"&&b.name===r.to)?d:[...d,{type:"channel",name:r.to}]),K(r.to));break}case"PART":{let l=i?" ("+i+")":"",d=`${r.from} has parted ${r.to}${l}`;r.to&&N(r.to,{...p,text:d,system:!0}),r.to&&r.to.startsWith("#")&&K(r.to);break}case"QUIT":{let l=i?" ("+i+")":"",d=`${r.from} has quit${l}`;z.current.forEach(b=>{b.type==="channel"&&N(b.name,{...p,text:d,system:!0})});break}case"NICK":{let l=Array.isArray(r.body)?r.body[0]:i,d=`${r.from} is now known as ${l}`;z.current.forEach(b=>{b.type==="channel"&&N(b.name,{...p,text:d,system:!0})}),r.from===X.current&&l&&a(l),z.current.forEach(b=>{b.type==="channel"&&K(b.name)});break}case"TOPIC":{let l=`${r.from} has changed the topic to: ${i}`;r.to&&(N(r.to,{...p,text:l,system:!0}),v(d=>({...d,[r.to]:i})));break}case"353":{if(Array.isArray(r.params)&&r.params.length>=2&&r.body){let l=r.params[1],F=(Array.isArray(r.body)?r.body[0]:String(r.body)).split(/\s+/).filter(Boolean).map(H=>H.startsWith("@")?{nick:H.slice(1),mode:"o"}:H.startsWith("+")?{nick:H.slice(1),mode:"v"}:{nick:H,mode:""});c(H=>({...H,[l]:F}))}break}case"332":{if(Array.isArray(r.params)&&r.params.length>=1){let l=r.params[0],d=Array.isArray(r.body)?r.body[0]:i;d&&v(b=>({...b,[l]:d}))}break}case"322":{if(Array.isArray(r.params)&&r.params.length>=2){let l=r.params[0],d=r.params[1];N("Server",{...p,text:`${l} (${d} users): ${(i||"").trim()}`,system:!0})}break}case"323":N("Server",{...p,text:i||"End of channel list",system:!0});break;case"352":{if(Array.isArray(r.params)&&r.params.length>=5){let l=r.params[0],d=r.params[4],b=r.params.length>5?r.params[5]:"";N("Server",{...p,text:`${l} ${d} ${b}`,system:!0})}break}case"315":N("Server",{...p,text:i||"End of /WHO list",system:!0});break;case"311":{if(Array.isArray(r.params)&&r.params.length>=1){let l=r.params[0];N("Server",{...p,text:`${l} (${i})`,system:!0})}break}case"312":{if(Array.isArray(r.params)&&r.params.length>=2){let l=r.params[0],d=r.params[1];N("Server",{...p,text:`${l} on ${d}`,system:!0})}break}case"319":{if(Array.isArray(r.params)&&r.params.length>=1){let l=r.params[0];N("Server",{...p,text:`${l} is on: ${i}`,system:!0})}break}case"318":N("Server",{...p,text:i||"End of /WHOIS list",system:!0});break;case"375":case"372":case"376":N("Server",{...p,text:i,system:!0});break;default:i&&N("Server",{...p,text:i,system:!0})}},[N,K]);U(()=>{if(!e)return;let r=!0;return(async()=>{for(;r;)try{let p=new AbortController;W.current=p;let l=await x(`/messages?after=${B.current}&timeout=${Ct}`,{signal:p.signal});if(!r)break;if(D(!0),l.messages)for(let d of l.messages)fe(d);l.last_id>B.current&&(B.current=l.last_id)}catch(p){if(!r)break;if(p.name==="AbortError")continue;D(!1),await new Promise(l=>setTimeout(l,It))}})(),()=>{r=!1,W.current?.abort()}},[e,fe]),U(()=>{if(!e)return;let r=s[_];if(!r||r.type!=="channel")return;K(r.name);let i=setInterval(()=>K(r.name),At);return()=>clearInterval(i)},[e,_,s,K]),U(()=>{Oe.current?.scrollIntoView({behavior:"smooth"})},[y,_]),U(()=>{Z.current?.focus()},[_]),U(()=>{let r=i=>{i.key==="/"&&document.activeElement!==Z.current&&!i.ctrlKey&&!i.altKey&&!i.metaKey&&(i.preventDefault(),Z.current?.focus())};return document.addEventListener("keydown",r),Z.current?.focus(),()=>document.removeEventListener("keydown",r)},[]),U(()=>{if(!e)return;let r=s[_];!r||r.type!=="channel"||x("/channels").then(i=>{let p=i.find(l=>l.name===r.name);p&&p.topic&&v(l=>({...l,[r.name]:p.topic}))}).catch(()=>{})},[e,_,s]);let at=Y(async(r,i)=>{if(a(r),t(!0),S("Server",`Connected as ${r}`),i){try{await x("/messages",{method:"POST",body:JSON.stringify({command:"MOTD"})})}catch{}return}let p=JSON.parse(localStorage.getItem("neoirc_channels")||"[]");for(let l of p)try{await x("/messages",{method:"POST",body:JSON.stringify({command:"JOIN",to:l})}),o(d=>d.find(b=>b.type==="channel"&&b.name===l)?d:[...d,{type:"channel",name:l}])}catch{}},[S]),st=async r=>{if(r){r=r.trim(),r.startsWith("#")||(r="#"+r);try{await x("/messages",{method:"POST",body:JSON.stringify({command:"JOIN",to:r})}),o(p=>p.find(l=>l.type==="channel"&&l.name===r)?p:[...p,{type:"channel",name:r}]);let i=z.current.length;u(i);try{let p=await x(`/history?target=${encodeURIComponent(r)}&limit=50`);if(Array.isArray(p))for(let l of p)fe(l)}catch{}}catch(i){S("Server",`Failed to join ${r}: ${i.data?.error||"error"}`)}}},Ne=async(r,i)=>{try{await x("/messages",{method:"POST",body:JSON.stringify(i?{command:"PART",to:r,body:[i]}:{command:"PART",to:r})})}catch{}o(p=>p.filter(l=>!(l.type==="channel"&&l.nam Line truncated
+6 -5
View File
@@ -70,7 +70,7 @@ function LoginScreen({ onLogin }) {
.catch(() => {});
const saved = localStorage.getItem("neoirc_token");
if (saved) {
api("/state?replay=1")
api("/state?initChannelState=1")
.then((u) => onLogin(u.nick, true))
.catch(() => localStorage.removeItem("neoirc_token"));
}
@@ -335,7 +335,7 @@ function App() {
if (msg.to) addMessage(msg.to, { ...base, text, system: true });
if (msg.to && msg.to.startsWith("#")) {
// Create a tab when the current user joins a channel
// (including replayed JOINs on reconnect).
// (including JOINs from initChannelState on reconnect).
if (msg.from === nickRef.current) {
setTabs((prev) => {
if (
@@ -656,9 +656,10 @@ function App() {
if (isResumed) {
// Request MOTD on resumed sessions (new sessions
// get it automatically from the server during
// creation). Channel state is replayed by the
// server via the message queue (?replay=1), so we
// do not need to re-JOIN channels here.
// creation). Channel state is initialized by the
// server via the message queue
// (?initChannelState=1), so we do not need to
// re-JOIN channels here.
try {
await api("/messages", {
method: "POST",