feat: integrate login flow, lobby UI and bitmap font resources
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import type { RemoteConfig } from '../config/remote-config.ts';
|
||||
import type { PlatformState } from '../platform/stores/platform-types.ts';
|
||||
import { selectSelfPlayer } from '../platform/stores/selectors.ts';
|
||||
// Presentation checks required view inputs without depending on the protocol layer.
|
||||
function requireNumber(value:unknown,path:string):number {if(typeof value!=='number'||!Number.isFinite(value))throw Error(`${path}: expected number`);return value;}
|
||||
function requireInteger(value:unknown,path:string):number {const result=requireNumber(value,path);if(!Number.isInteger(result))throw Error(`${path}: expected integer`);return result;}
|
||||
function requireString(value:unknown,path:string):string {if(typeof value!=='string')throw Error(`${path}: expected string`);return value;}
|
||||
function requireRecord(value:unknown,path:string):Record<string,unknown> {if(!value||typeof value!=='object'||Array.isArray(value))throw Error(`${path}: expected object`);return value as Record<string,unknown>;}
|
||||
|
||||
export function selectLobbyModel(state:PlatformState) {
|
||||
const self=selectSelfPlayer(state);
|
||||
if(!self)throw Error('lobby requires authenticated player');
|
||||
return Object.freeze({...self,
|
||||
roomcard:requireNumber(self.roomcard,'players.self.roomcard'),
|
||||
taskstate:requireInteger(self.taskstate,'players.self.taskstate'),
|
||||
score:requireNumber(self.score,'players.self.score'),
|
||||
sign:self.sign===null?'这个家伙很懒~~':requireString(self.sign,'players.self.sign') || '这个家伙很懒~~',
|
||||
canAct:state.app.phase==='logged-in'&&state.room.kind==='outside',
|
||||
});
|
||||
}
|
||||
export type LobbyModel=ReturnType<typeof selectLobbyModel>;
|
||||
export interface Notice {type:0|1; text:string; image:string; url:string; height:number;}
|
||||
export interface LobbyPolicy {noticeHours:number; notices:readonly Notice[]; scroll:string; hideTask:boolean; hideShare:boolean;
|
||||
starName:string; contacts:readonly {label:string;value:string}[];}
|
||||
|
||||
/** Source defaults: 04_Data.sysConfig and advertInfo_0/1/2; 12_Logic.get_paravalue. */
|
||||
export function resolveLobbyPolicy(remote:RemoteConfig):LobbyPolicy {
|
||||
const interval=remote.getValue('gameNoticeShow');
|
||||
const noticeHours=interval==null?0:Number(interval);
|
||||
if(!Number.isFinite(noticeHours)||noticeHours< -1)throw Error('Invalid gameNoticeShow');
|
||||
const menuValue=remote.getValue('menunotice');
|
||||
const menu=menuValue==null?{}:requireRecord(menuValue,'menunotice');
|
||||
const notices:Notice[]=[];
|
||||
for(let index=0;index<3;index++){
|
||||
const value=menu[`advert${index}`];
|
||||
if(value==null){
|
||||
const message=index===0?remote.getValue('noticemsg'):null;
|
||||
notices.push({type:0,text:message==null?'':requireString(message,'noticemsg').replace(/#/g,'\n'),image:'',url:'',height:1});
|
||||
continue;
|
||||
}
|
||||
const data=requireRecord(value,`menunotice.advert${index}`);
|
||||
const type=requireInteger(data.type,`menunotice.advert${index}.type`);
|
||||
if(type!==0&&type!==1)throw Error(`menunotice.advert${index}.type must be 0 or 1`);
|
||||
// Empty text and URL explicitly mean no content/action in the source.
|
||||
const field=(key:string)=>data[key]==null?'':requireString(data[key],`menunotice.advert${index}.${key}`);
|
||||
notices.push({type,text:type===0?field('msg').replace(/#/g,'\n'):'',image:type===1?field('imgUrl'):'',
|
||||
url:field(type===0?'url':'imgAdUrl'),height:type===1?requireNumber(data.height,`menunotice.advert${index}.height`):1});
|
||||
}
|
||||
const scroll=remote.getValue('scrollmsg');
|
||||
const star=remote.getValue('starName');
|
||||
const contacts:Array<{label:string;value:string}>=[];
|
||||
for(const [key,title] of [['service_wechat','客服微信:'],['service_qq','投诉建议:'],['service_tel','客服手机:']]){
|
||||
const value=remote.getValue(key);
|
||||
if(value!=null&&value!=='')contacts.push({label:title,value:requireString(value,key)});
|
||||
}
|
||||
return Object.freeze({noticeHours,notices:Object.freeze(notices),scroll:scroll==null?'':requireString(scroll,'scrollmsg'),
|
||||
starName:star==null?'星星':requireString(star,'starName'),contacts:Object.freeze(contacts),
|
||||
hideTask:Boolean(remote.getValue('hideTaskBtn')),hideShare:Boolean(remote.getValue('hideWxShare'))});
|
||||
}
|
||||
/** Exact JudgeShow expiry: equality is not expired; reconnect never auto-opens. */
|
||||
export function shouldShowNotice(hours:number,previous:number|null,now:number,reconnect:boolean):boolean {
|
||||
if(reconnect||hours===-1)return false;
|
||||
return hours===0||!previous||previous+hours*3600000<now;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "98247194-1211-4ac7-b8cf-e097983e8a2d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -32,11 +32,11 @@ export function resolveLoginPolicy(remote:RemoteConfig):LoginPolicy {
|
||||
showVisitor:flag(hallField('showVisitor'),'showVisitor'),qqLogin:flag(hallField('qqLogin'),'qqLogin'),
|
||||
hiddenVisitor:LOGIN_DEFAULTS.hiddenVisitor,avatarUrl,avatarRange:Object.freeze([...range])});
|
||||
}
|
||||
/** Assignment order is significant: JumpWxAuth applies showVisitor/QQ after cache hiding. */
|
||||
/** Visitor visibility takes precedence over every other login button. */
|
||||
export function loginButtons(policy:LoginPolicy, versionState:0|1, cached:boolean) {
|
||||
const account=versionState===0&&cached;
|
||||
let wechat=!policy.deviceLogin&&!account;
|
||||
let visitor=!account;
|
||||
if(versionState===0){visitor=false;if(policy.showVisitor){wechat=false;visitor=true;}}
|
||||
return {wechat,visitor,phone:policy.deviceLogin&&!account,quick:false,qq:policy.qqLogin,account};
|
||||
return {wechat:wechat&&!visitor,visitor,phone:policy.deviceLogin&&!account&&!visitor,quick:false,qq:policy.qqLogin&&!visitor,account};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user