diff --git a/preactalpha.template.html b/preactalpha.template.html index f6a96bd46..b4a9cc143 100644 --- a/preactalpha.template.html +++ b/preactalpha.template.html @@ -54,6 +54,7 @@ + @@ -68,14 +69,13 @@ - - - - - - - - + + + + + + + diff --git a/src/battle-text-parser.ts b/src/battle-text-parser.ts index ccc0303b0..41f7430be 100644 --- a/src/battle-text-parser.ts +++ b/src/battle-text-parser.ts @@ -1,3 +1,13 @@ +/** + * Text parser + * + * No dependencies + * Optional dependency: BattleText + * + * @author Guangcong Luo + * @license MIT + */ + declare const BattleText: {[id: string]: {[templateName: string]: string}}; type Args = [string, ...string[]]; @@ -15,12 +25,14 @@ class BattleTextParser { this.perspective = perspective; } - static parseLine(line: string): {args: Args, kwArgs: KWArgs} { + static parseLine(line: string, noDefault: true): Args | null; + static parseLine(line: string): Args; + static parseLine(line: string, noDefault?: boolean): Args | null { if (!line.startsWith('|')) { - return {args: ['', line], kwArgs: {}}; + return ['', line]; } if (line === '|') { - return {args: ['done'], kwArgs: {}}; + return ['done']; } const index = line.indexOf('|', 1); const cmd = line.slice(1, index); @@ -28,23 +40,28 @@ class BattleTextParser { case 'chatmsg': case 'chatmsg-raw': case 'raw': case 'error': case 'html': case 'inactive': case 'inactiveoff': case 'warning': case 'fieldhtml': case 'controlshtml': case 'bigerror': - case 'debug': case 'tier': - return {args: [cmd, line.slice(index + 1)], kwArgs: {}}; + case 'debug': case 'tier': case 'challstr': case 'popup': case '': + return [cmd, line.slice(index + 1)]; case 'c': case 'chat': case 'uhtml': case 'uhtmlchange': // three parts const index2a = line.indexOf('|', index + 1); - return {args: [cmd, line.slice(index + 1, index2a), line.slice(index2a + 1)], kwArgs: {}}; - case 'c:': + return [cmd, line.slice(index + 1, index2a), line.slice(index2a + 1)]; + case 'c:': case 'pm': // four parts const index2b = line.indexOf('|', index + 1); const index3b = line.indexOf('|', index2b + 1); - return { - args: [cmd, line.slice(index + 1, index2b), line.slice(index2b + 1, index3b), line.slice(index3b + 1)], - kwArgs: {}, - }; + return [cmd, line.slice(index + 1, index2b), line.slice(index2b + 1, index3b), line.slice(index3b + 1)]; } - let args: Args = line.slice(1).split('|') as any; - let kwArgs: KWArgs = {}; + if (noDefault) return null; + return line.slice(1).split('|') as [string, ...string[]]; + } + + static parseBattleLine(line: string): {args: Args, kwArgs: KWArgs} { + let args = this.parseLine(line, true); + if (args) return {args, kwArgs: {}}; + + args = line.slice(1).split('|') as [string, ...string[]]; + const kwArgs: KWArgs = {}; while (args.length > 1) { const lastArg = args[args.length - 1]; if (lastArg.charAt(0) !== '[') break; @@ -159,7 +176,7 @@ class BattleTextParser { extractMessage(buf: string) { let out = ''; for (const line of buf.split('\n')) { - const {args, kwArgs} = BattleTextParser.parseLine(line); + const {args, kwArgs} = BattleTextParser.parseBattleLine(line); out += this.parseArgs(args, kwArgs) || ''; } return out; diff --git a/src/battle.ts b/src/battle.ts index 133bd96ce..9e6e4bf10 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -3395,7 +3395,7 @@ class Battle { return; } if (!str) return; - const {args, kwArgs} = BattleTextParser.parseLine(str); + const {args, kwArgs} = BattleTextParser.parseBattleLine(str); if (this.scene.maybeCloseMessagebar(args, kwArgs)) { this.activityStep--; @@ -3408,7 +3408,7 @@ class Battle { let nextKwargs: KWArgs = {}; const nextLine = this.activityQueue[this.activityStep + 1] || ''; if (nextLine.slice(0, 2) === '|-') { - ({args: nextArgs, kwArgs: nextKwargs} = BattleTextParser.parseLine(nextLine)); + ({args: nextArgs, kwArgs: nextKwargs} = BattleTextParser.parseBattleLine(nextLine)); } if (this.debug) { diff --git a/src/client-core.ts b/src/client-core.ts index 5d6b9bc33..d71585fe7 100644 --- a/src/client-core.ts +++ b/src/client-core.ts @@ -82,11 +82,10 @@ if (!window.console) { *********************************************************************/ // PS's model classes are defined here -const PSURL = (() => { - let prefix = ''; - if (document.location!.protocol !== 'http:') prefix = 'https:'; - return prefix + '//play.pokemonshowdown.com/'; -})(); +const PSURL = ( + (document.location!.protocol !== 'http:' ? 'https:' : '') + + '//play.pokemonshowdown.com/' +); class PSSubscription { observable: PSModel | PSStreamModel; diff --git a/src/client-main.ts b/src/client-main.ts index fe9140f43..f9897f9b4 100644 --- a/src/client-main.ts +++ b/src/client-main.ts @@ -224,13 +224,16 @@ class PSTeams extends PSStreamModel<'team' | 'format'> { class PSUser extends PSModel { name = "Guest"; + group = ''; userid = "guest" as ID; named = false; registered = false; avatar = "1"; - setName(name: string, named: boolean, avatar: string) { + setName(fullName: string, named: boolean, avatar: string) { const loggingIn = (!this.named && named); + const {name, group} = BattleTextParser.parseNameParts(fullName); this.name = name; + this.group = group; this.userid = toID(name); this.named = named; this.avatar = avatar; @@ -336,7 +339,7 @@ class PSServer { * Rooms *********************************************************************/ -type PSRoomLocation = 'left' | 'right' | 'popup' | 'modal-popup' | 'semimodal-popup'; +type PSRoomLocation = 'left' | 'right' | 'popup' | 'mini-window' | 'modal-popup' | 'semimodal-popup'; interface RoomOptions { id: RoomID; @@ -427,7 +430,7 @@ class PlaceholderRoom extends PSRoom { * PS *********************************************************************/ -type RoomType = {Model: typeof PSRoom, Component: any, title?: string}; +type RoomType = {Model?: typeof PSRoom, Component: any, title?: string}; /** * This model updates: @@ -464,6 +467,8 @@ const PS = new class extends PSModel { leftRoomList: RoomID[] = []; /** List of rooms on the right side of the top tabbar */ rightRoomList: RoomID[] = []; + /** List of mini-rooms in the Main Menu */ + miniRoomList: RoomID[] = []; /** Currently active popups, in stack order (bottom to top) */ popups: RoomID[] = []; @@ -527,54 +532,32 @@ const PS = new class extends PSModel { /** Tracks whether or not to display the "Use arrow keys" hint */ arrowKeysUsed = false; + newsHTML = document.querySelector('.news-embed .pm-log')?.innerHTML || ''; + constructor() { super(); this.addRoom({ id: '' as RoomID, title: "Home", - type: 'mainmenu', }); this.addRoom({ id: 'rooms' as RoomID, title: "Rooms", - type: 'rooms', }); + if (this.newsHTML) { + this.addRoom({ + id: 'news' as RoomID, + title: "News", + }); + } + this.updateLayout(); window.addEventListener('resize', () => this.updateLayout()); } - lineParse(str: string): [string, ...string[]] { - if (!str.startsWith('|')) { - return ['', str]; - } - const index = str.indexOf('|', 1); - const cmd = str.slice(1, index); - switch (cmd) { - case 'html': - case 'raw': - case 'challstr': - case 'popup': - case '': - return [cmd, str.slice(index + 1)]; - case 'c': - case 'uhtml': - case 'uhtmlchange': - // three parts - const index2a = str.indexOf('|', index + 1); - return [cmd, str.slice(index + 1, index2a), str.slice(index2a + 1)]; - case 'c:': - case 'pm': - // four parts - const index2b = str.indexOf('|', index + 1); - const index3b = str.indexOf('|', index2b + 1); - return [cmd, str.slice(index + 1, index2b), str.slice(index2b + 1, index3b), str.slice(index3b + 1)]; - } - return str.slice(1).split('|') as [string, ...string[]]; - } - // Panel layout /////////////// /** @@ -750,6 +733,7 @@ const PS = new class extends PSModel { switch (hyphenIndex < 0 ? options.id : options.id.slice(0, hyphenIndex + 1)) { case 'teambuilder': case 'ladder': case 'battles': case 'rooms': case 'options': case 'volume': case 'teamdropdown': case 'formatdropdown': + case 'news': options.type = options.id; break; case 'battle-': case 'user-': case 'team-': @@ -758,6 +742,9 @@ const PS = new class extends PSModel { case 'view-': options.type = 'html'; break; + case '': + options.type = 'mainmenu'; + break; default: options.type = 'chat'; break; @@ -779,12 +766,16 @@ const PS = new class extends PSModel { case 'formatdropdown': options.location = 'semimodal-popup'; break; + case 'news': + options.location = 'mini-window'; + break; } + if (options.id.startsWith('pm-')) options.location = 'mini-window'; } const roomType = this.roomTypes[options.type]; if (roomType?.title) options.title = roomType.title; - const Model = roomType ? roomType.Model : PlaceholderRoom; + const Model = roomType ? (roomType.Model || PSRoom) : PlaceholderRoom; return new Model(options); } updateRoomTypes() { @@ -797,7 +788,8 @@ const PS = new class extends PSModel { const options: RoomOptions = room; if (roomType.title) options.title = roomType.title; - const newRoom = new roomType.Model(options); + const Model = roomType.Model || PSRoom; + const newRoom = new Model(options); this.rooms[roomid] = newRoom; if (this.leftRoom === room) this.leftRoom = newRoom; if (this.rightRoom === room) this.rightRoom = newRoom; @@ -937,6 +929,9 @@ const PS = new class extends PSModel { } if (!noFocus || !this.rightRoom) this.rightRoom = room; break; + case 'mini-window': + this.miniRoomList.push(room.id); + break; case 'popup': case 'semimodal-popup': case 'modal-popup': @@ -979,6 +974,13 @@ const PS = new class extends PSModel { if (PS.room === room) PS.room = PS.activePanel; } + if (room.location === 'mini-window') { + const miniRoomIndex = PS.miniRoomList.indexOf(room.id); + if (miniRoomIndex >= 0) { + PS.miniRoomList.splice(miniRoomIndex, 1); + } + } + if (this.popups.length && room.id === this.popups[this.popups.length - 1]) { this.popups.pop(); PS.room = this.popups.length ? PS.rooms[this.popups[this.popups.length - 1]]! : PS.activePanel; diff --git a/src/panel-chat.tsx b/src/panel-chat.tsx index 18fef5521..98bec8b19 100644 --- a/src/panel-chat.tsx +++ b/src/panel-chat.tsx @@ -360,6 +360,7 @@ class ChatPanel extends PSRoomPanel { }; render() { const room = this.props.room; + const isPM = !!room.pmTarget; const challengeTo = room.challengingFormat ? @@ -381,11 +382,11 @@ class ChatPanel extends PSRoomPanel { return - + {challengeTo || challengeFrom && [challengeTo, challengeFrom]} - - + + ; } } @@ -455,7 +456,7 @@ class ChatLog extends preact.Component<{ } this.subscription = this.props.room.subscribe(msg => { if (!msg) return; - const tokens = PS.lineParse(msg); + const tokens = BattleTextParser.parseLine(msg); switch (tokens[0]) { case 'title': this.props.room.title = tokens[1]; diff --git a/src/panel-example.tsx b/src/panel-example.tsx index 77bd439dd..c30c2f7f5 100644 --- a/src/panel-example.tsx +++ b/src/panel-example.tsx @@ -7,6 +7,8 @@ * @license AGPLv3 */ +// Example room with panel + class ExampleRoom extends PSRoom { readonly classType: string = 'example'; constructor(options: RoomOptions) { @@ -27,3 +29,18 @@ PS.roomTypes['example'] = { Model: ExampleRoom, Component: ExamplePanel, }; + +// Example panel with no room + +class ExampleViewPanel extends PSRoomPanel { + render() { + const room = this.props.room; + return + Loading... + ; + } +} + +PS.roomTypes['exampleview'] = { + Component: ExampleViewPanel, +}; diff --git a/src/panel-mainmenu.tsx b/src/panel-mainmenu.tsx index db19ea846..58ea8a93b 100644 --- a/src/panel-mainmenu.tsx +++ b/src/panel-mainmenu.tsx @@ -23,7 +23,7 @@ class MainMenuRoom extends PSRoom { pspl?: RoomInfo[], } = {}; receive(line: string) { - const tokens = PS.lineParse(line); + const tokens = BattleTextParser.parseLine(line); switch (tokens[0]) { case 'challstr': PSLoginServer.query({ @@ -242,13 +242,41 @@ class MainMenuRoom extends PSRoom { } } -class MainMenuPanel extends PSRoomPanel { +class NewsPanel extends PSRoomPanel { + render() { + return + + ; + } +} + +class MainMenuPanel extends PSRoomPanel { focus() { (this.base!.querySelector('button.big') as HTMLButtonElement).focus(); } submit = (e: Event) => { alert('todo: implement'); }; + renderMiniRoom(room: PSRoom) { + const roomType = PS.roomTypes[room.type]; + const Panel = roomType ? roomType.Component : PSRoomPanel; + return ; + } + renderMiniRooms() { + return PS.miniRoomList.map(roomid => { + const room = PS.rooms[roomid]!; + return + + + + + {room.title} + + {this.renderMiniRoom(room)} + + ; + }); + } render() { const onlineButton = ' button' + (PS.isOffline ? ' disabled' : ''); const searchButton = (PS.down ? @@ -274,22 +302,7 @@ class MainMenuPanel extends PSRoomPanel { - - - - - - News - - - - Test client - Welcome to the test client! You can test client changes here! - —Zarel on Sep 25, 2015 - - - - + {this.renderMiniRooms()} {searchButton} @@ -437,6 +450,10 @@ class TeamForm extends preact.Component<{ } } +PS.roomTypes['news'] = { + Component: NewsPanel, +}; + PS.roomTypes['mainmenu'] = { Model: MainMenuRoom, Component: MainMenuPanel, diff --git a/src/panel-teambuilder-team.tsx b/src/panel-teambuilder-team.tsx index 5febef74d..64dd6000f 100644 --- a/src/panel-teambuilder-team.tsx +++ b/src/panel-teambuilder-team.tsx @@ -190,7 +190,6 @@ class TeamPanel extends PSRoomPanel { } PS.roomTypes['team'] = { - Model: PSRoom, Component: TeamPanel, title: "Team", }; diff --git a/src/panel-teambuilder.tsx b/src/panel-teambuilder.tsx index a24968f76..24605c346 100644 --- a/src/panel-teambuilder.tsx +++ b/src/panel-teambuilder.tsx @@ -221,7 +221,6 @@ class TeambuilderPanel extends PSRoomPanel { } PS.roomTypes['teambuilder'] = { - Model: PSRoom, Component: TeambuilderPanel, title: "Teambuilder", }; diff --git a/src/panel-teamdropdown.tsx b/src/panel-teamdropdown.tsx index 50258329b..d1850242e 100644 --- a/src/panel-teamdropdown.tsx +++ b/src/panel-teamdropdown.tsx @@ -624,11 +624,9 @@ class FormatDropdownPanel extends PSRoomPanel { } PS.roomTypes['teamdropdown'] = { - Model: PSRoom, Component: TeamDropdownPanel, }; PS.roomTypes['formatdropdown'] = { - Model: PSRoom, Component: FormatDropdownPanel, }; diff --git a/src/panels.tsx b/src/panels.tsx index 77f3c23fc..524189e80 100644 --- a/src/panels.tsx +++ b/src/panels.tsx @@ -142,6 +142,12 @@ function PSPanelWrapper(props: { room: PSRoom, children: preact.ComponentChildren, scrollable?: boolean, width?: number, }) { const room = props.room; + if (room.location === 'mini-window') { + if (room.id === 'news') { + return {props.children}; + } + return {props.children}; + } if (room.location !== 'left' && room.location !== 'right') { const style = PSMain.getPopupStyle(room, props.width); return diff --git a/style/client2.css b/style/client2.css index 6ee8408ba..c79d28c83 100644 --- a/style/client2.css +++ b/style/client2.css @@ -531,10 +531,10 @@ select { color: #BB2222; } .minimizebutton:hover, -.pm-window h3:hover .minimizebutton { +.mini-window h3:hover .minimizebutton { color: #333333; } -.pm-window h3 .closebutton:hover + .minimizebutton { +.mini-window h3 .closebutton:hover + .minimizebutton { color: #999999 !important; } .closebutton:active, @@ -624,11 +624,11 @@ select { -webkit-overflow-scrolling: touch; overflow-scrolling: touch; } -.ps-room.ps-room-light { +.ps-room.ps-room-light, .ps-room-light { background: rgba(242,247,250,.85); color: black; } -.ps-room.ps-room-opaque { +.ps-room.ps-room-opaque, .ps-room-opaque { background: #EEF2F5; color: black; } @@ -824,13 +824,13 @@ p.or:after { margin: 0 auto; min-height: 2px; } -.pm-window { +.mini-window { margin: 0 -12px 12px -10px; } -.tiny-layout .pm-window { +.tiny-layout .mini-window { margin: 0 0 12px 0; } -.pm-window h3 { +.mini-window h3 { background: rgba(248,248,248,.8); color: #777777; margin: 0; @@ -841,33 +841,41 @@ p.or:after { border-top-right-radius: 5px; cursor: pointer; } -.pm-window h3:hover { +.mini-window h3:hover { color: #333333; } -.pm-window h3.pm-minimized { +.mini-window h3.pm-minimized { border-radius: 5px; } -.pm-window h3.pm-notifying { +.mini-window h3.pm-notifying { border-color: #AA8866; background: #E3C3A3; } -.pm-window h3.pm-notifying:hover { +.mini-window h3.pm-notifying:hover { border-color: #604020; background: #CFAF8F; } -.pm-window h3 .closebutton, -.pm-window h3 .minimizebutton { +.mini-window h3 .closebutton, +.mini-window h3 .minimizebutton { float: right; margin: -3px -3px; width: 22px; height: 22px; } -.pm-window h3 small { +.mini-window h3 small { color: #888888; font-weight: normal; vertical-align: 1px; } -.pm-log { +.mini-window .mini-window-contents { + position: relative; + height: 300px; + border: 1px solid #AAAAAA; + border-top: 0; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} +.mini-window-body, .pm-log { min-height: 100px; max-height: 300px; background: rgba(242,247,250,.925); @@ -875,12 +883,18 @@ p.or:after { word-wrap: break-word; border: 1px solid #AAAAAA; border-top: 0; - border-bottom: 0; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; } +.mini-window-body { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} +.pm-log { + border-bottom: 0; +} .pm-log-add { color: black; border: 1px solid #AAAAAA; @@ -888,17 +902,17 @@ p.or:after { border-bottom-right-radius: 5px; background: rgba(242,247,250,.85); } -.pm-window.collapsed .pm-log, -.pm-window.collapsed .pm-log-add { +.mini-window.collapsed .pm-log, +.mini-window.collapsed .pm-log-add { display: none; } -.pm-window.focused h3, -.pm-window.focused .pm-log, -.pm-window.focused .pm-log-add { +.mini-window.focused h3, +.mini-window.focused .pm-log, +.mini-window.focused .pm-log-add { border-color: #555555; } -.pm-window.focused h3, -.pm-window.focused h3:hover { +.mini-window.focused h3, +.mini-window.focused h3:hover { background: #f8f8f8; color: #222222; } @@ -916,6 +930,7 @@ p.or:after { font-size: 9pt; padding: 1px 10px; border-bottom: 1px solid #AAAAAA; + margin-bottom: -1px; } .unread { background: rgba(255,240,130,0.33); @@ -1220,7 +1235,7 @@ p.or:after { } .chat-log .inner, .pm-log .inner { - padding: 4px 8px 6px 8px; + padding: 24px 8px 6px 8px; } a.ilink { text-decoration: none; @@ -3286,9 +3301,9 @@ a.ilink.yours { .dark .menugroup { background: rgba(0,0,0,.2); } -.dark .pm-window.focused h3, -.dark .pm-window.focused .pm-log, -.dark .pm-window.focused .pm-log-add { +.dark .mini-window.focused h3, +.dark .mini-window.focused .pm-log, +.dark .mini-window.focused .pm-log-add { border-color: #CCC; } diff --git a/testclient-beta.html b/testclient-beta.html index 320bb5435..b338be5a4 100644 --- a/testclient-beta.html +++ b/testclient-beta.html @@ -55,6 +55,7 @@ + @@ -72,7 +73,6 @@ -
Loading...
Welcome to the test client! You can test client changes here!
—Zarel on Sep 25, 2015