2026-07-21 16:13:16 +08:00

26 lines
4.2 KiB
TypeScript

import { h, render } from 'vue'
// 信息应用
import { el, uid, relTime } from '../../utils'
import { store as Store } from '../../composables/useStore'
import { ui as UI } from '../../composables/useUI'
import { Apps, stdMenus } from '../../composables/useApps'
import MessagesComponent from './Messages.vue'
import { Notify } from '../../composables/useNotify'
export const MessagesApp = {
id: 'messages', name: '信息', icon: '/assets/icons/messages.png',
w: 760, h: 540, minW: 560, minH: 380,
menus(win:any){return stdMenus(this,{file:[{label:'新信息',key:'⌘N',action:()=>win?.appState?.newChat()}]})},
store:{get(){return Store.get('messages',{convs:[{id:'c1',name:'王小明',msgs:[{from:'them',text:'在吗?周末的球局你还来吗',ts:Date.now()-7200000},{from:'me',text:'来!老时间老地方?',ts:Date.now()-7000000},{from:'them',text:'对,下午三点,别迟到',ts:Date.now()-6900000}],unread:0},{id:'c2',name:'李思颖',msgs:[{from:'them',text:'设计稿我看完了,整体很棒',ts:Date.now()-4000000},{from:'them',text:'Dock 的放大曲线再顺滑一点就更好了',ts:Date.now()-3900000}],unread:2},{id:'c3',name:'妈妈',msgs:[{from:'them',text:'吃饭了吗?最近降温,多穿点',ts:Date.now()-90000000}],unread:1}]})},set(v:any){Store.set('messages',v)}},
replies:['哈哈好的','收到!','稍等,我马上看','真的吗?太好了','没问题 👌','那就这么定了','哈哈哈哈','嗯嗯,知道了'],
render(win:any){
const st=win.appState={data:this.store.get(),cur:null as any};win.body.classList.add('msg-body');const listEl=el('div',{class:'notes-list'}),chatEl=el('div',{class:'msg-chat'})
win.body.append(el('div',{class:'notes-side msg-side'},el('div',{class:'notes-side-bar'},el('span',{class:'fb-side-title',text:'对话'})),listEl),chatEl);const save=()=>this.store.set(st.data)
st.newChat=async()=>{const name=await UI.prompt('新对话','输入联系人姓名:','');if(!name)return;const conv={id:uid(),name,msgs:[],unread:0};st.data.convs.push(conv);st.cur=conv;save();renderAll()}
const renderList=()=>{listEl.innerHTML='';st.data.convs.sort((a:any,b:any)=>(b.msgs[b.msgs.length-1]?.ts||0)-(a.msgs[a.msgs.length-1]?.ts||0)).forEach((c:any)=>{const last=c.msgs[c.msgs.length-1];const row=el('div',{class:'note-row contact-row'+(c===st.cur?' sel':'')},el('span',{class:'contact-avatar',text:c.name.slice(0,1)}),el('div',{style:{flex:'1',minWidth:'0'}},el('div',{class:'note-title',text:c.name}),el('div',{class:'note-sub',text:last?last.text:'开始聊天吧'})),c.unread?el('span',{class:'badge',text:String(c.unread)}):null);row.addEventListener('click',()=>{st.cur=c;c.unread=0;save();renderAll()});listEl.append(row)})}
const renderChat=()=>{chatEl.innerHTML='';const c=st.cur;if(!c){chatEl.append(el('div',{class:'empty-state'},el('div',{class:'es-icon',text:'💬'}),el('div',{text:'选择对话'})));return};const scroll=el('div',{class:'msg-scroll'});let lastTs=0;c.msgs.forEach((m:any)=>{if(m.ts-lastTs>300000)scroll.append(el('div',{class:'msg-time',text:relTime(m.ts)}));lastTs=m.ts;scroll.append(el('div',{class:'msg-bubble-row '+m.from},el('div',{class:'msg-bubble',text:m.text})))});const input=el('input',{class:'text-input msg-input',type:'text',placeholder:'iMessage 信息'});const send=()=>{const v=(input as HTMLInputElement).value.trim();if(!v)return;c.msgs.push({from:'me',text:v,ts:Date.now()});(input as HTMLInputElement).value='';save();renderChat();renderList();setTimeout(()=>{if(!st.data.convs.includes(c))return;c.msgs.push({from:'them',text:this.replies[Math.floor(Math.random()*this.replies.length)],ts:Date.now()});save();if(st.cur===c&&document.body.contains(chatEl))renderChat();else{c.unread++;Notify.send({appId:'messages',title:c.name,body:c.msgs[c.msgs.length-1].text})};renderList()},1200+Math.random()*2200)};input.addEventListener('keydown',(e:KeyboardEvent)=>{e.stopPropagation();if(e.key==='Enter')send()});chatEl.append(el('div',{class:'msg-header'},el('span',{class:'contact-avatar',text:c.name.slice(0,1)}),el('b',{text:c.name})),scroll,el('div',{class:'msg-input-row'},input));setTimeout(()=>{scroll.scrollTop=scroll.scrollHeight},30)}
const renderAll=()=>{renderList();renderChat()};st.cur=st.data.convs[0]||null;renderAll()
}
}
Apps.register(MessagesApp)