1- import React , { useState } from 'react' ;
2- import { DeviceMode , MobileApp } from '../../../../models/mobileDevice' ;
1+ import React , { useState , useMemo } from 'react' ;
2+ import { DeviceMode , MobileApp , DeviceGameContext } from '../../../../models/mobileDevice' ;
33import { getDeviceConfig , getAppName } from '../../../../models/eraDevice' ;
44
55interface AppProps {
66 eraId : string ;
77 mode : DeviceMode ;
88 appId : MobileApp ;
99 onBack : ( ) => void ;
10+ gameContext ?: DeviceGameContext ;
1011}
1112
1213interface ForumPost {
@@ -21,56 +22,80 @@ interface ForumPost {
2122 replyList : { author : string ; content : string ; time : string } [ ] ;
2223}
2324
24- const ForumApp : React . FC < AppProps > = ( { eraId, mode, appId, onBack } ) => {
25+ const ForumApp : React . FC < AppProps > = ( { eraId, mode, appId, onBack, gameContext } ) => {
2526 const config = getDeviceConfig ( eraId ) ;
2627 const appName = config ? getAppName ( config , appId , mode ) : '论坛' ;
2728 const [ selectedPost , setSelectedPost ] = useState < ForumPost | null > ( null ) ;
2829 const [ activeCategory , setActiveCategory ] = useState ( '全部' ) ;
2930
30- const categories = [ '全部' , '武林杂谈' , '求师问道' , '江湖情报' , '江湖趣事' ] ;
31+ const posts : ForumPost [ ] = useMemo ( ( ) => {
32+ const result : ForumPost [ ] = [ ] ;
33+ const ctx = gameContext ;
34+ if ( ! ctx ?. 世界 ) return result ;
3135
32- const placeholderPosts : ForumPost [ ] = [
33- {
34- id : '1' , author : '华山剑客' , title : '今日论剑,紫霞神功果然了得' ,
35- content : '今日在华山之巅与几位同道切磋,紫霞神功内力绵长,后劲十足,实为正派内功中的上乘功夫。唯觉修炼门槛颇高,非十余年苦功不能窥其门径。' ,
36- category : '武林杂谈' , time : '申时' , replies : 12 , views : 342 ,
37- replyList : [
38- { author : '嵩山弟子' , content : '紫霞神功确是好功夫,但我嵩山派大阳神掌也不遑多让!' , time : '申时一刻' } ,
39- { author : '无名散人' , content : '内功之道,贵在专精。各家各派皆有绝学,不必厚此薄彼。' , time : '申时二刻' } ,
40- ] ,
41- } ,
42- {
43- id : '2' , author : '丐帮长老' , title : '求问降龙十八掌传人 whereabouts' ,
44- content : '帮中古籍记载,降龙十八掌乃丐帮镇帮绝学。近年来却鲜有传人消息,哪位英雄知晓现任传人的下落?' ,
45- category : '求师问道' , time : '午时' , replies : 8 , views : 215 ,
46- replyList : [
47- { author : '江湖百晓生' , content : '传闻在襄阳一带出现过,郭大侠的后人应当还在坚守此地。' , time : '午时一刻' } ,
48- ] ,
49- } ,
50- {
51- id : '3' , author : '暗桩密探' , title : '【密报】明教总坛近日异动频繁' ,
52- content : '据可靠消息,明教总坛近日有大批高手聚集,似在筹备某件大事。请各位同道提前做好准备,以免措手不及。' ,
53- category : '江湖情报' , time : '辰时' , replies : 23 , views : 567 ,
54- replyList : [
55- { author : '六扇门捕头' , content : '多谢提醒,已派人暗中查探。' , time : '辰时一刻' } ,
56- { author : '峨眉女侠' , content : '师太已经注意到了,命我等加强戒备。' , time : '辰时二刻' } ,
57- { author : '武当道士' , content : '师父说此事自有定数,不必惊慌。' , time : '辰时三刻' } ,
58- ] ,
59- } ,
60- {
61- id : '4' , author : '茶楼说书人' , title : '笑谈:某大侠夜奔客栈,误入女客房' ,
62- content : '话说前日有位大侠月夜赶路,误入一家客栈,摸黑进了客房。次日天明才发现走错了,隔壁住的是一位峨眉女侠。那女侠拔剑就追,大侠连跑三条街……' ,
63- category : '江湖趣事' , time : '昨日' , replies : 45 , views : 1024 ,
64- replyList : [
65- { author : '路人甲' , content : '哈哈哈,这位大侠怕不是令狐冲?' , time : '昨日酉时' } ,
66- { author : '知情者' , content : '此事确有其事,我便是那客栈的店小二!' , time : '昨日戌时' } ,
67- ] ,
68- } ,
69- ] ;
36+ // 进行中事件 → 论坛热帖
37+ if ( ctx . 世界 . 进行中事件 ) {
38+ ctx . 世界 . 进行中事件 . forEach ( ( event , idx ) => {
39+ result . push ( {
40+ id : `ongoing-${ idx } ` ,
41+ author : event . 关联人物 ?. [ 0 ] || '江湖传闻' ,
42+ title : event . 事件名 ,
43+ content : event . 事件说明 ,
44+ category : '江湖情报' ,
45+ time : event . 开始时间 || '近日' ,
46+ replies : event . 关联人物 ?. length || 0 ,
47+ views : Math . floor ( Math . random ( ) * 500 ) + 50 ,
48+ replyList : event . 关联人物 . slice ( 1 ) . map ( ( person ) => ( {
49+ author : person ,
50+ content : `涉及此事,${ person } 亦有参与。` ,
51+ time : event . 开始时间 || '近日' ,
52+ } ) ) ,
53+ } ) ;
54+ } ) ;
55+ }
7056
57+ // 已结算事件 → 历史帖
58+ if ( ctx . 世界 . 已结算事件 ) {
59+ ctx . 世界 . 已结算事件 . slice ( - 5 ) . forEach ( ( event , idx ) => {
60+ const 结果 = event . 事件结果 ?. join ( '; ' ) || '已了结' ;
61+ result . push ( {
62+ id : `settled-${ idx } ` ,
63+ author : event . 关联人物 ?. [ 0 ] || '史官' ,
64+ title : `[已结案] ${ event . 事件名 } ` ,
65+ content : `${ event . 事件说明 } \n结果: ${ 结果 } ` ,
66+ category : '武林旧事' ,
67+ time : event . 结算时间 || '往昔' ,
68+ replies : event . 关联人物 ?. length || 0 ,
69+ views : Math . floor ( Math . random ( ) * 300 ) + 30 ,
70+ replyList : [ ] ,
71+ } ) ;
72+ } ) ;
73+ }
74+
75+ // 世界镜头规划 → 讨论帖
76+ if ( ctx . 世界 . 世界镜头规划 ) {
77+ ctx . 世界 . 世界镜头规划 . slice ( - 3 ) . forEach ( ( lens , idx ) => {
78+ result . push ( {
79+ id : `lens-${ idx } ` ,
80+ author : lens . 关联人物 ?. [ 0 ] || '说书人' ,
81+ title : lens . 镜头标题 ,
82+ content : lens . 镜头内容 || '' ,
83+ category : '武林杂谈' ,
84+ time : lens . 触发时间 || '近日' ,
85+ replies : lens . 关联人物 ?. length || 0 ,
86+ views : Math . floor ( Math . random ( ) * 200 ) + 20 ,
87+ replyList : [ ] ,
88+ } ) ;
89+ } ) ;
90+ }
91+
92+ return result ;
93+ } , [ gameContext ?. 世界 ] ) ;
94+
95+ const categories = [ '全部' , ...Array . from ( new Set ( posts . map ( ( p ) => p . category ) ) ) ] ;
7196 const filteredPosts = activeCategory === '全部'
72- ? placeholderPosts
73- : placeholderPosts . filter ( ( p ) => p . category === activeCategory ) ;
97+ ? posts
98+ : posts . filter ( ( p ) => p . category === activeCategory ) ;
7499
75100 if ( selectedPost ) {
76101 return (
@@ -88,22 +113,24 @@ const ForumApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack }) => {
88113 < span className = "text-[10px] text-wuxia-gold/60 bg-wuxia-gold/10 px-1.5 py-0.5 rounded" > { selectedPost . category } </ span >
89114 < span className = "text-[10px] text-gray-500" > { selectedPost . views } 浏览</ span >
90115 </ div >
91- < p className = "text-sm text-gray-200 leading-relaxed" > { selectedPost . content } </ p >
116+ < p className = "text-sm text-gray-200 leading-relaxed whitespace-pre-wrap " > { selectedPost . content } </ p >
92117 </ div >
93- < div >
94- < h4 className = "text-xs font-semibold text-gray-400 mb-2" > { selectedPost . replies } 条回复</ h4 >
95- < div className = "space-y-3" >
96- { selectedPost . replyList . map ( ( reply , idx ) => (
97- < div key = { idx } className = "rounded-lg bg-gray-900/40 border border-gray-800/30 p-3" >
98- < div className = "flex items-center justify-between mb-1" >
99- < span className = "text-xs text-wuxia-gold/70" > { reply . author } </ span >
100- < span className = "text-[10px] text-gray-500" > { reply . time } </ span >
118+ { selectedPost . replyList . length > 0 && (
119+ < div >
120+ < h4 className = "text-xs font-semibold text-gray-400 mb-2" > { selectedPost . replyList . length } 条回复</ h4 >
121+ < div className = "space-y-3" >
122+ { selectedPost . replyList . map ( ( reply , idx ) => (
123+ < div key = { idx } className = "rounded-lg bg-gray-900/40 border border-gray-800/30 p-3" >
124+ < div className = "flex items-center justify-between mb-1" >
125+ < span className = "text-xs text-wuxia-gold/70" > { reply . author } </ span >
126+ < span className = "text-[10px] text-gray-500" > { reply . time } </ span >
127+ </ div >
128+ < p className = "text-sm text-gray-300" > { reply . content } </ p >
101129 </ div >
102- < p className = "text-sm text-gray-300" > { reply . content } </ p >
103- </ div >
104- ) ) }
130+ ) ) }
131+ </ div >
105132 </ div >
106- </ div >
133+ ) }
107134 </ div >
108135 </ div >
109136 ) ;
@@ -115,39 +142,48 @@ const ForumApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack }) => {
115142 < button onClick = { onBack } className = "text-gray-400 hover:text-white transition-colors" > ←</ button >
116143 < h3 className = "font-semibold text-white" > { appName } </ h3 >
117144 </ div >
118- < div className = "flex gap-1 px-4 py-2 overflow-x-auto border-b border-gray-800/30" >
119- { categories . map ( ( cat ) => (
120- < button
121- key = { cat }
122- onClick = { ( ) => setActiveCategory ( cat ) }
123- className = { `px-3 py-1 rounded-full text-xs whitespace-nowrap transition-colors ${ activeCategory === cat ? 'bg-wuxia-gold/20 text-wuxia-gold' : 'text-gray-400 hover:text-white' } ` }
124- >
125- { cat }
126- </ button >
127- ) ) }
128- </ div >
129- < div className = "flex-1 overflow-y-auto" >
130- < ul className = "divide-y divide-gray-800/50" >
131- { filteredPosts . map ( ( post ) => (
132- < li key = { post . id } >
133- < button
134- onClick = { ( ) => setSelectedPost ( post ) }
135- className = "w-full px-4 py-3 text-left hover:bg-gray-800/30 transition-colors"
136- >
137- < div className = "flex items-center gap-2 mb-1" >
138- < span className = "text-[10px] text-wuxia-gold/60 bg-wuxia-gold/10 px-1.5 py-0.5 rounded" > { post . category } </ span >
139- < span className = "text-sm text-white font-medium truncate" > { post . title } </ span >
140- </ div >
141- < div className = "flex items-center gap-3 text-[10px] text-gray-500" >
142- < span > { post . author } </ span >
143- < span > { post . time } </ span >
144- < span > 回复 { post . replies } </ span >
145- < span > 浏览 { post . views } </ span >
146- </ div >
147- </ button >
148- </ li >
145+ { categories . length > 1 && (
146+ < div className = "flex gap-1 px-4 py-2 overflow-x-auto border-b border-gray-800/30" >
147+ { categories . map ( ( cat ) => (
148+ < button
149+ key = { cat }
150+ onClick = { ( ) => setActiveCategory ( cat ) }
151+ className = { `px-3 py-1 rounded-full text-xs whitespace-nowrap transition-colors ${ activeCategory === cat ? 'bg-wuxia-gold/20 text-wuxia-gold' : 'text-gray-400 hover:text-white' } ` }
152+ >
153+ { cat }
154+ </ button >
149155 ) ) }
150- </ ul >
156+ </ div >
157+ ) }
158+ < div className = "flex-1 overflow-y-auto" >
159+ { filteredPosts . length > 0 ? (
160+ < ul className = "divide-y divide-gray-800/50" >
161+ { filteredPosts . map ( ( post ) => (
162+ < li key = { post . id } >
163+ < button
164+ onClick = { ( ) => setSelectedPost ( post ) }
165+ className = "w-full px-4 py-3 text-left hover:bg-gray-800/30 transition-colors"
166+ >
167+ < div className = "flex items-center gap-2 mb-1" >
168+ < span className = "text-[10px] text-wuxia-gold/60 bg-wuxia-gold/10 px-1.5 py-0.5 rounded" > { post . category } </ span >
169+ < span className = "text-sm text-white font-medium truncate" > { post . title } </ span >
170+ </ div >
171+ < div className = "flex items-center gap-3 text-[10px] text-gray-500" >
172+ < span > { post . author } </ span >
173+ < span > { post . time } </ span >
174+ < span > 回复 { post . replies } </ span >
175+ < span > 浏览 { post . views } </ span >
176+ </ div >
177+ </ button >
178+ </ li >
179+ ) ) }
180+ </ ul >
181+ ) : (
182+ < div className = "flex flex-col items-center justify-center h-full text-center p-8" >
183+ < span className = "text-4xl text-gray-600 mb-3" > 📋</ span >
184+ < p className = "text-sm text-gray-400" > 暂无帖子</ p >
185+ </ div >
186+ ) }
151187 </ div >
152188 </ div >
153189 ) ;
0 commit comments