mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-04-12 07:25:50 +00:00
优化了接龙的消息样式
This commit is contained in:
@@ -4670,6 +4670,8 @@ class ChatService {
|
||||
case '57':
|
||||
// 引用消息,title 就是回复的内容
|
||||
return title
|
||||
case '53':
|
||||
return `[接龙] ${title.split(/\r?\n/).map(line => line.trim()).find(Boolean) || title}`
|
||||
case '2000':
|
||||
return `[转账] ${title}`
|
||||
case '2001':
|
||||
@@ -4699,6 +4701,8 @@ class ChatService {
|
||||
return '[链接]'
|
||||
case '87':
|
||||
return '[群公告]'
|
||||
case '53':
|
||||
return '[接龙]'
|
||||
default:
|
||||
return '[消息]'
|
||||
}
|
||||
@@ -5298,6 +5302,8 @@ class ChatService {
|
||||
const quoteInfo = this.parseQuoteMessage(content)
|
||||
result.quotedContent = quoteInfo.content
|
||||
result.quotedSender = quoteInfo.sender
|
||||
} else if (xmlType === '53') {
|
||||
result.appMsgKind = 'solitaire'
|
||||
} else if ((xmlType === '5' || xmlType === '49') && (sourceUsername?.startsWith('gh_') || appName?.includes('公众号') || sourceName)) {
|
||||
result.appMsgKind = 'official-link'
|
||||
} else if (url) {
|
||||
|
||||
@@ -2119,6 +2119,7 @@ class ExportService {
|
||||
}
|
||||
return title || '[引用消息]'
|
||||
}
|
||||
if (xmlType === '53') return title ? `[接龙] ${title.split(/\r?\n/).map(line => line.trim()).find(Boolean) || title}` : '[接龙]'
|
||||
if (xmlType === '5' || xmlType === '49') return title ? `[链接] ${title}` : '[链接]'
|
||||
|
||||
// 有 title 就返回 title
|
||||
@@ -3220,6 +3221,8 @@ class ExportService {
|
||||
appMsgKind = 'announcement'
|
||||
} else if (xmlType === '57' || hasReferMsg || localType === 244813135921) {
|
||||
appMsgKind = 'quote'
|
||||
} else if (xmlType === '53') {
|
||||
appMsgKind = 'solitaire'
|
||||
} else if (xmlType === '5' || xmlType === '49') {
|
||||
appMsgKind = 'link'
|
||||
} else if (looksLikeAppMsg) {
|
||||
|
||||
@@ -2064,6 +2064,7 @@
|
||||
.message-bubble .bubble-content:has(> .link-message),
|
||||
.message-bubble .bubble-content:has(> .card-message),
|
||||
.message-bubble .bubble-content:has(> .chat-record-message),
|
||||
.message-bubble .bubble-content:has(> .solitaire-message),
|
||||
.message-bubble .bubble-content:has(> .official-message),
|
||||
.message-bubble .bubble-content:has(> .channel-video-card),
|
||||
.message-bubble .bubble-content:has(> .location-message) {
|
||||
@@ -3604,6 +3605,140 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 接龙消息
|
||||
.solitaire-message {
|
||||
width: min(360px, 72vw);
|
||||
max-width: 360px;
|
||||
background: var(--card-inner-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.solitaire-header {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 12px 14px 10px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.solitaire-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--primary) 12%, transparent);
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.solitaire-heading {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.solitaire-title {
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.45;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.solitaire-meta {
|
||||
margin-top: 2px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.solitaire-intro,
|
||||
.solitaire-entry-list {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.solitaire-intro {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.solitaire-intro-line {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.solitaire-entry-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.solitaire-entry {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.solitaire-entry-index {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-tertiary);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.solitaire-entry-text {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.solitaire-muted-line {
|
||||
color: var(--text-tertiary);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.solitaire-footer {
|
||||
padding: 8px 14px 10px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.solitaire-chevron {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
&.expanded .solitaire-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
// 通话消息
|
||||
.call-message {
|
||||
display: flex;
|
||||
|
||||
@@ -181,6 +181,51 @@ function buildChatRecordPreviewItems(recordList: ChatRecordItem[], maxVisible =
|
||||
]
|
||||
}
|
||||
|
||||
interface SolitaireEntry {
|
||||
index: string
|
||||
text: string
|
||||
}
|
||||
|
||||
interface SolitaireContent {
|
||||
title: string
|
||||
introLines: string[]
|
||||
entries: SolitaireEntry[]
|
||||
}
|
||||
|
||||
function parseSolitaireContent(rawTitle: string): SolitaireContent {
|
||||
const lines = String(rawTitle || '')
|
||||
.replace(/\r\n/g, '\n')
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
const title = lines[0] || '接龙'
|
||||
const introLines: string[] = []
|
||||
const entries: SolitaireEntry[] = []
|
||||
let hasStartedEntries = false
|
||||
|
||||
for (const line of lines.slice(1)) {
|
||||
const entryMatch = /^(\d+)[..、]\s*(.+)$/.exec(line)
|
||||
if (entryMatch) {
|
||||
hasStartedEntries = true
|
||||
entries.push({
|
||||
index: entryMatch[1],
|
||||
text: entryMatch[2].trim()
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if (hasStartedEntries && entries.length > 0) {
|
||||
const previous = entries[entries.length - 1]
|
||||
previous.text = `${previous.text} ${line}`.trim()
|
||||
} else {
|
||||
introLines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
return { title, introLines, entries }
|
||||
}
|
||||
|
||||
function composeGlobalMsgSearchResults(
|
||||
seedMap: Map<string, GlobalMsgSearchResult[]>,
|
||||
authoritativeMap: Map<string, GlobalMsgSearchResult[]>
|
||||
@@ -7825,6 +7870,7 @@ function MessageBubble({
|
||||
const [senderName, setSenderName] = useState<string | undefined>(undefined)
|
||||
const [quotedSenderName, setQuotedSenderName] = useState<string | undefined>(undefined)
|
||||
const [quoteLayout, setQuoteLayout] = useState<QuoteLayout>('quote-top')
|
||||
const [solitaireExpanded, setSolitaireExpanded] = useState(false)
|
||||
const senderProfileRequestSeqRef = useRef(0)
|
||||
const [emojiError, setEmojiError] = useState(false)
|
||||
const [emojiLoading, setEmojiLoading] = useState(false)
|
||||
@@ -9433,6 +9479,71 @@ function MessageBubble({
|
||||
)
|
||||
}
|
||||
|
||||
if (xmlType === '53' || message.appMsgKind === 'solitaire') {
|
||||
const solitaireText = message.linkTitle || q('appmsg > title') || q('title') || cleanedParsedContent || '接龙'
|
||||
const solitaire = parseSolitaireContent(solitaireText)
|
||||
const previewEntries = solitaireExpanded ? solitaire.entries : solitaire.entries.slice(0, 3)
|
||||
const hiddenEntryCount = Math.max(0, solitaire.entries.length - previewEntries.length)
|
||||
const introLines = solitaireExpanded ? solitaire.introLines : solitaire.introLines.slice(0, 4)
|
||||
const hasMoreIntro = !solitaireExpanded && solitaire.introLines.length > introLines.length
|
||||
const countText = solitaire.entries.length > 0 ? `${solitaire.entries.length} 人参与` : '接龙消息'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`solitaire-message${solitaireExpanded ? ' expanded' : ''}`}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-expanded={solitaireExpanded}
|
||||
onClick={isSelectionMode ? undefined : (e) => {
|
||||
e.stopPropagation()
|
||||
setSolitaireExpanded(value => !value)
|
||||
}}
|
||||
onKeyDown={isSelectionMode ? undefined : (e) => {
|
||||
if (e.key !== 'Enter' && e.key !== ' ') return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setSolitaireExpanded(value => !value)
|
||||
}}
|
||||
title={solitaireExpanded ? '点击收起接龙' : '点击展开接龙'}
|
||||
>
|
||||
<div className="solitaire-header">
|
||||
<div className="solitaire-icon" aria-hidden="true">
|
||||
<Hash size={18} />
|
||||
</div>
|
||||
<div className="solitaire-heading">
|
||||
<div className="solitaire-title">{solitaire.title}</div>
|
||||
<div className="solitaire-meta">{countText}</div>
|
||||
</div>
|
||||
</div>
|
||||
{introLines.length > 0 && (
|
||||
<div className="solitaire-intro">
|
||||
{introLines.map((line, index) => (
|
||||
<div key={`${line}-${index}`} className="solitaire-intro-line">{line}</div>
|
||||
))}
|
||||
{hasMoreIntro && <div className="solitaire-muted-line">...</div>}
|
||||
</div>
|
||||
)}
|
||||
{previewEntries.length > 0 ? (
|
||||
<div className="solitaire-entry-list">
|
||||
{previewEntries.map(entry => (
|
||||
<div key={`${entry.index}-${entry.text}`} className="solitaire-entry">
|
||||
<span className="solitaire-entry-index">{entry.index}</span>
|
||||
<span className="solitaire-entry-text">{entry.text}</span>
|
||||
</div>
|
||||
))}
|
||||
{hiddenEntryCount > 0 && (
|
||||
<div className="solitaire-muted-line">还有 {hiddenEntryCount} 条...</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="solitaire-footer">
|
||||
<span>{solitaireExpanded ? '收起接龙' : '展开接龙'}</span>
|
||||
<ChevronDown size={14} className="solitaire-chevron" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const title = message.linkTitle || q('title') || cleanedParsedContent || 'Card'
|
||||
const desc = message.appMsgDesc || q('des')
|
||||
const url = message.linkUrl || q('url')
|
||||
|
||||
Reference in New Issue
Block a user