diff --git a/src/ts/common/color.ts b/src/ts/common/color.ts index f6b7040..a0b5944 100644 --- a/src/ts/common/color.ts +++ b/src/ts/common/color.ts @@ -157,6 +157,12 @@ export interface HSVA { a: number; } +export interface HSL { + h: number; + s: number; + l: number; +} + export interface RGB { r: number; g: number; @@ -493,3 +499,37 @@ export function h2rgb(h: number): RGB { b: Math.round(b * 255) }; } + +export function rgb2hsl(rgb: RGB): HSL { + const r = rgb.r / 255; + const g = rgb.g / 255; + const b = rgb.b / 255; + let max = Math.max(r, g, b); + let min = Math.min(r, g, b); + let delta = max - min; + let h = 0; + let s; + let l; + + if (max === min) h = 0; + else if (r === max) h = (g - b) / delta; + else if (g === max) h = 2 + (b - r) / delta; + else if (b === max) h = 4 + (r - g) / delta; + + h = Math.min(h * 60, 360); + if (h < 0) h += 360; + l = (min + max) / 2; + + if (max === min) s = 0; + else if (l <= 0.5) s = delta / (max + min); + else s = delta / (2 - max - min); + h = Math.floor(h); + s = Math.floor(s * 100); + l = Math.floor(l * 100); + + return { h, s, l }; +} + +export function hsl2CSS(hsl: HSL) { + return `hsl(${hsl.h},${hsl.s}%,${hsl.l}%)`; +} diff --git a/src/ts/common/interfaces.ts b/src/ts/common/interfaces.ts index 4fa653d..e9ab8b8 100644 --- a/src/ts/common/interfaces.ts +++ b/src/ts/common/interfaces.ts @@ -683,6 +683,7 @@ export interface BrowserSettings { scale?: number; walkByDefault?: boolean; brightNight?: boolean; + timestamp?: '24' | '12'; } export interface EntityTypeName { diff --git a/src/ts/components/shared/chat-box/chat-box.pug b/src/ts/components/shared/chat-box/chat-box.pug index d14a4f4..9ea927d 100644 --- a/src/ts/components/shared/chat-box/chat-box.pug +++ b/src/ts/components/shared/chat-box/chat-box.pug @@ -5,6 +5,14 @@ aria-label="Chat message") .chat-box-type(#typeBox (click)="toggleChatType()") | #[span(#typePrefix)][#[span.chat-box-type-name(#typeName)]]: + .emotes.emoji-box([style.display]="emojiBoxState") + ul + li(*ngFor="let e of emotes" (click)="addEmoji(e.symbol)" + ) + span.emote-sample.mr-2 + emote-box([emote]="e.names[0]") + + emote-box.game-button.chat-emoji-button([emote]="btnEmoji" (click)="toggleEmojiBox()" (mouseenter)="onMouseEnterEmojiButton($event)") button.game-button.chat-send-button((click)="send($event)" title="Send message" aria-label="Send message") fa-icon([icon]="sendIcon") diff --git a/src/ts/components/shared/chat-box/chat-box.scss b/src/ts/components/shared/chat-box/chat-box.scss index 611fb1e..1ac7665 100644 --- a/src/ts/components/shared/chat-box/chat-box.scss +++ b/src/ts/components/shared/chat-box/chat-box.scss @@ -34,7 +34,7 @@ z-index: 1; padding: 8px; font-size: 14px; - padding-right: 35px; + padding-right: 65px; padding-left: 90px; border-radius: $border-radius-base; outline: none; @@ -86,3 +86,71 @@ white-space: nowrap; vertical-align: bottom; } + +.chat-emoji-button { + user-select: none; + position: absolute; + right: 30px; + bottom: 3px; + padding: 0; + margin: 0; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); + + &:hover { + bottom: 4px; + -webkit-filter: grayscale(0); + filter: grayscale(0%); + } +} + +.emoji-box { + display: flex; + padding: 10px; + position: absolute; + background-color: #000000; + opacity: 0.95; + bottom: 40px; + left: 0; + right: 0; + width: 100%; + height: 190px; + overflow-y: auto; + + &::-webkit-scrollbar { + width: 7px; + } + + &::-webkit-scrollbar-thumb { + background: rgb(77, 77, 77); + border-radius: 14px; + } +} + +.emoji-box ul { + margin-bottom: 0; +} + +.emoji-box li { + display: inline; + list-style: none; + column-width: 180px; +} + +.emote-sample { + user-select: none; + display: inline-block; + width: 29px; + height: 29px; + text-align: center; + margin-bottom: 3px; + cursor: pointer; + &:hover { + background: rgba(0, 255, 255, 0.2); + border-radius: 2px; + } +} + +.emote-sample emote-box { + line-height: 2; +} diff --git a/src/ts/components/shared/chat-box/chat-box.ts b/src/ts/components/shared/chat-box/chat-box.ts index 909afc7..32748a9 100644 --- a/src/ts/components/shared/chat-box/chat-box.ts +++ b/src/ts/components/shared/chat-box/chat-box.ts @@ -9,10 +9,11 @@ import { faComment, faAngleDoubleRight } from '../../../client/icons'; import { isInParty } from '../../../client/partyUtils'; import { handleActionCommand } from '../../../client/playerActions'; import { hasHeadAnimation } from '../../../common/pony'; -import { AutocompleteState, autocompleteMesssage, replaceEmojis } from '../../../client/emoji'; +import { AutocompleteState, autocompleteMesssage, replaceEmojis, emojis } from '../../../client/emoji'; import { replaceNodes } from '../../../client/htmlUtils'; import { invalidEnumReturn } from '../../../common/utils'; import { findMatchingEntityNames, findEntityOrMockByAnyMeans, findBestEntityByName } from '../../../client/handlers'; +import { sample } from 'lodash'; const chatTypeNames: string[] = []; const chatTypeClasses: string[] = []; @@ -45,6 +46,8 @@ export class ChatBox implements AfterViewInit, OnDestroy { readonly maxSayLength = SAY_MAX_LENGTH; readonly commentIcon = faComment; readonly sendIcon = faAngleDoubleRight; + readonly emotes = emojis; + emojiBoxState = 'none'; @ViewChild('inputElement', { static: true }) inputElement!: ElementRef; @ViewChild('typeBox', { static: true }) typeBox!: ElementRef; @ViewChild('typePrefix', { static: true }) typePrefix!: ElementRef; @@ -54,6 +57,7 @@ export class ChatBox implements AfterViewInit, OnDestroy { isOpen = false; message: string | undefined = ''; chatType = ChatType.Say; + btnEmoji = emojis[0].names[0]; private pasted = false; private lastMessages: string[] = []; private state: AutocompleteState = {}; @@ -92,6 +96,21 @@ export class ChatBox implements AfterViewInit, OnDestroy { ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()); } + addEmoji(emoji: string) { + this.toggleEmojiBox(); + if (!this.message) { + this.message = emoji; + } else if (this.input.maxLength > this.message.length) { + this.message += emoji; + } + } + toggleEmojiBox() { + if (this.emojiBoxState === 'none') { + this.emojiBoxState = 'inline-block'; + } else { + this.emojiBoxState = 'none'; + } + } send(_event: Event | undefined) { let chatType = this.chatType; let message = replaceEmojis(cleanMessage(this.message || '')).substr(0, SAY_MAX_LENGTH); @@ -259,6 +278,7 @@ export class ChatBox implements AfterViewInit, OnDestroy { } private close() { if (this.isOpen) { + this.emojiBoxState = 'none'; this.input.blur(); this.isOpen = false; this.chatBox.nativeElement.hidden = true; @@ -273,6 +293,11 @@ export class ChatBox implements AfterViewInit, OnDestroy { this.open(); } } + onMouseEnterEmojiButton(event: MouseEvent) { + if (!event.target) return; + const emoji = sample(emojis)!; + this.btnEmoji = emoji.names[0]; + } toggleChatType() { const chatTypes = getChatTypes(this.game); this.chatType = chatTypes[(chatTypes.indexOf(this.chatType) + 1) % chatTypes.length]; diff --git a/src/ts/components/shared/chat-log/chat-log.pug b/src/ts/components/shared/chat-log/chat-log.pug index 3bc0af1..a69607d 100644 --- a/src/ts/components/shared/chat-log/chat-log.pug +++ b/src/ts/components/shared/chat-log/chat-log.pug @@ -13,6 +13,9 @@ | Party a.link-plain.chat-log-tab(#whisperTab tabindex (click)="switchTab('whisper')") | Whisper + a.link-plain.chat-log-tab.chat-log-filter-tab([style.background-color]="filterColor" (click)="focus()") + fa-icon.search-icon([icon]="searchIcon") + input.chat-log-filter(#filterInput type="text" (keyup.enter)="(unFocus())" (keyup.esc)="(unFocus())" [placeholder]="'Filter'" (keyup)="filterChat()") .chat-log-scroll-outer() .chat-log-scroll-inner(#scroll) .chat-log-scroll-inner-inner(#lines) diff --git a/src/ts/components/shared/chat-log/chat-log.scss b/src/ts/components/shared/chat-log/chat-log.scss index 62796ae..084592e 100644 --- a/src/ts/components/shared/chat-log/chat-log.scss +++ b/src/ts/components/shared/chat-log/chat-log.scss @@ -56,7 +56,7 @@ $resizer-offset: $resizer-size / 2; } .whisper-icon { - position: absolute; + position: absolute; top: 0; left: 0; font-size: 14px; @@ -84,6 +84,7 @@ $resizer-offset: $resizer-size / 2; left: 30px; display: flex; user-select: none; + text-shadow: 0px 0px 4px #000000; } .chat-log-tab { @@ -157,3 +158,64 @@ $resizer-offset: $resizer-size / 2; transform: rotate(45deg); color: rgba(255, 255, 255, 0.2); } + +.search-icon { + position: relative; + float: left; + width: 0; + bottom: -1px; + left: 0; + z-index: 2; +} + +.chat-log-filter-tab { + width: 100px; +} + +.chat-log-filter { + padding-bottom: 5px; + border: none; + background-color: rgba(0, 0, 0, 0); + border: none; + outline: none; + color: white; + padding-left: 20px; + width: 100%; +} + +.chat-log-filter::-webkit-input-placeholder { + opacity: 1; + color: white; + text-shadow: 0px 0px 4px #000000; +} + +.chat-log-filter:-moz-placeholder { + /* Firefox 18- */ + opacity: 1; + color: white; + text-shadow: 0px 0px 4px #000000; +} + +.chat-log-filter::-moz-placeholder { + /* Firefox 19+ */ + opacity: 1; + color: white; + text-shadow: 0px 0px 4px #000000; +} + +.chat-log-filter:-ms-input-placeholder { + opacity: 1; + color: white; + text-shadow: 0px 0px 4px #000000; +} + +@media only screen and (max-width: 360px) { + .chat-log-filter-tab { + position: absolute; + left: 0; + bottom: 35px; + width: 100%; + width: calc(100% - 10px); + border-radius: 2px; + } +} diff --git a/src/ts/components/shared/chat-log/chat-log.ts b/src/ts/components/shared/chat-log/chat-log.ts index 1ac6223..40d8ef4 100644 --- a/src/ts/components/shared/chat-log/chat-log.ts +++ b/src/ts/components/shared/chat-log/chat-log.ts @@ -8,9 +8,12 @@ import { MessageType, isPartyMessage, ChatMessage, Pony, FakeEntity, isWhisper, import { SettingsService } from '../../services/settingsService'; import { AgDragEvent } from '../directives/agDrag'; import { element, textNode, removeAllNodes, replaceNodes } from '../../../client/htmlUtils'; -import { DEFAULT_CHATLOG_OPACITY, PONY_TYPE } from '../../../common/constants'; -import { faCaretUp, faArrowDown } from '../../../client/icons'; +import { DEFAULT_CHATLOG_OPACITY, PONY_TYPE, SECOND } from '../../../common/constants'; +import { faCaretUp, faArrowDown, faSearch } from '../../../client/icons'; import { sampleMessages } from '../../../common/debugData'; +import { findEntityById } from '../../../common/worldMap'; +import { colorToRGBA, rgb2hsl, HSL, hsl2CSS } from '../../../common/color'; +import * as moment from 'moment'; interface IndexEntryUser { id: number; @@ -26,6 +29,8 @@ interface ChatLogLineDOM { entry: ChatLogMessage; root: HTMLElement; label: HTMLElement; + time: HTMLElement; + timeContent: HTMLElement; labelText: Text; name: HTMLElement; nameContent: HTMLElement; @@ -98,6 +103,11 @@ export function createChatLogLineDOM(clickLabel: ClickHandler, clickName: ClickH line.root = element('div', 'chat-line', [ element('span', 'chat-line-lead'), + line.time = element('span', 'chat-line-name', [ + line.timeContent = element( + 'span', 'chat-line-time-content', [textNode('')], undefined), + line.index = element('span', 'chat-line-time-index', [line.indexText = textNode('')]), + ]), line.label = element( 'span', 'chat-line-label mr-1', [line.labelText = textNode('')], undefined, { click: () => clickLabel(line.entry) }), line.prefixText = textNode(''), @@ -115,7 +125,7 @@ export function createChatLogLineDOM(clickLabel: ClickHandler, clickName: ClickH return line; } -export function updateChatLogLine(line: ChatLogLineDOM, entry: ChatLogMessage) { +export function updateChatLogLine(line: ChatLogLineDOM, entry: ChatLogMessage, hourMode?: '12' | '24') { const { classes, label, message, prefix, suffix } = entry; const hasSpace = message.indexOf(' ') !== -1; @@ -125,12 +135,28 @@ export function updateChatLogLine(line: ChatLogLineDOM, entry: ChatLogMessage) { line.labelText.nodeValue = label ? `[${label}]` : ''; updateChatLogName(line, entry); + updateTime(line, hourMode); line.prefixText.nodeValue = prefix || ''; line.suffixText.nodeValue = suffix ? ` ${suffix}: ` : ': '; replaceNodes(line.message, message); } +function updateTime(line: ChatLogLineDOM, hourMode?: '12' | '24') { + line.time.style.display = 'inline'; + if (!hourMode) return; + if (hourMode === '24') + replaceNodes(line.timeContent, `[${moment().format('HH:mm:ss')}] `); + if (hourMode === '12') + replaceNodes(line.timeContent, `[${moment().format('LTS')}] `); +} + +function setNameColors(line: ChatLogLineDOM | undefined, colors?: string[]) { + if (!colors || !line) return; + if (colors[1]) line.name.style.color = colors[1]; + if (colors[0]) line.nameContent.style.color = colors[0]; +} + function updateChatLogName(line: ChatLogLineDOM, { name, index }: ChatLogMessage) { if (name) { line.name.style.display = 'inline'; @@ -183,12 +209,14 @@ function findUserIndex(users: IndexEntryUser[], id: number, crc: number | undefi export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { readonly toBottomIcon = faArrowDown; readonly resizeIcon = faCaretUp; + readonly searchIcon = faSearch; @ViewChild('chatLog', { static: true }) chatLog!: ElementRef; @ViewChild('scroll', { static: true }) scroll!: ElementRef; @ViewChild('lines', { static: true }) lines!: ElementRef; @ViewChild('localTab', { static: true }) localTab!: ElementRef; @ViewChild('partyTab', { static: true }) partyTab!: ElementRef; @ViewChild('whisperTab', { static: true }) whisperTab!: ElementRef; + @ViewChild('filterInput', { static: true }) filterInput!: ElementRef; @ViewChild('toggleButton', { static: true }) toggleButton!: ElementRef; @ViewChild('count', { static: true }) countElement!: ElementRef; @ViewChild('content', { static: true }) contentElement!: ElementRef; @@ -199,6 +227,7 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { local: ChatLogMessage[] = []; party: ChatLogMessage[] = []; whisper: ChatLogMessage[] = []; + filterColor = this.inactiveBg; unread = 0; private subscriptions: Subscription[] = []; private startX = 0; @@ -210,6 +239,8 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { private indexes = new Map(); private messageCounter = 0; private lastOpacity = 0; + private autoClear?: NodeJS.Timeout; + private autoUnfocus?: NodeJS.Timeout; constructor( private game: PonyTownGame, private settingsService: SettingsService, @@ -277,6 +308,8 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { } } ngAfterViewInit() { + const canvas = document.getElementById('canvas') as HTMLCanvasElement; + canvas.addEventListener('click', () => this.unFocus()); this.game.findEntityFromChatLog = this.findEntityFromMessages; this.game.findEntityFromChatLogByName = this.findEntityFromMessagesByName; @@ -414,6 +447,130 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { return index; } + filterChat() { + this.clearTimeOutAutoClear(); + this.clearTimeOutAutoUnfocus(); + let value = this.filterInput.nativeElement.value; + if (!value) { + this.clearTimeOutAutoClear(); + this.unFocus(); + this.filterChatLogLines('', false); + return; + } else { + this.filterColor = this.bg; + } + + this.autoClear = setTimeout(() => { + this.filterInput.nativeElement.value = ''; + this.filterChat(); + this.unFocus(); + }, SECOND * 30); + + this.autoUnfocus = setTimeout(() => { + this.unFocus(); + }, SECOND * 3); + + let toLowerCase = false; + + this.filterInput.nativeElement.style.color = ''; + if (value.startsWith('#')) { + value = value.slice(1); + } else if (value.startsWith('/')) { + value = value.slice(1); + try { + const regExp = new RegExp(value); + this.filterChatLogLines(regExp, toLowerCase); + } catch (err) {// If the user inputs invalid regExp we just notify him by changing text color to red + if (DEVELOPMENT) console.error(err); + this.filterInput.nativeElement.style.color = '#ff6666'; + } + return; + } else { + value = value.toLowerCase(); + toLowerCase = true; + } + this.filterChatLogLines(value, toLowerCase); + } + filterChatLogLines(content: string | RegExp, caseSensitive: boolean) { + const lines = this.linesElement.getElementsByTagName('div'); + + for (let i = 0; i < lines.length; i++) { + let textContent = lines[i].textContent; + + if (textContent) { + textContent = textContent.slice(10); + + if (typeof content === 'string') { + if (caseSensitive) { + lines[i].hidden = !textContent.toLowerCase().includes(content); + } else { + lines[i].hidden = !textContent.includes(content); + } + } else { + lines[i].hidden = !textContent.match(content); + } + } + } + } + unhideAllLines() { + const lines = this.linesElement.getElementsByTagName('div'); + for (let i = 0; i < lines.length; i++) { + lines[i].hidden = false; + } + } + brightenDarkColors(hsl: HSL) { + if (hsl.l < 40) { + if (hsl.l > 20 && hsl.l < 40) { + hsl.l += 20; + if (hsl.s > 11) hsl.s -= 11; + } else { + hsl.l = 40; + if (hsl.s > 11) hsl.s = 0; + } + } + return hsl; + } + private getCharacterColors(id: number | undefined) { + if (!id) return; + const entity = findEntityById(this.game.map, id) as Pony; + if (!entity || !entity.palettePonyInfo) return; + let colors = []; + let { body, mane } = entity.palettePonyInfo; + if (body && body.palette && body.palette.colors[1]) { + const rgb = colorToRGBA(body.palette.colors[1]); + const hsl = rgb2hsl(rgb); + this.brightenDarkColors(hsl); + colors.push(hsl2CSS(hsl)); + } + if (mane && mane.palette && mane.palette.colors[1]) { + const rgb = colorToRGBA(mane.palette.colors[1]); + const hsl = rgb2hsl(rgb); + this.brightenDarkColors(hsl); + colors.push(hsl2CSS(hsl)); + } else if (colors && colors[0]) { + colors.push((colors[0])); + } + return colors; + } + clearTimeOutAutoClear() { + if (this.autoClear) clearTimeout(this.autoClear); + this.autoClear = undefined; + this.filterColor = this.inactiveBg; + } + clearTimeOutAutoUnfocus() { + if (this.autoUnfocus) clearTimeout(this.autoUnfocus); + this.autoUnfocus = undefined; + } + focus() { + this.clearTimeOutAutoUnfocus(); + if (this.filterInput.nativeElement) + this.filterInput.nativeElement.focus(); + } + unFocus() { + this.clearTimeOutAutoUnfocus(); + if (this.filterInput.nativeElement) + this.filterInput.nativeElement.blur(); + } addMessage(message: ChatMessage) { if (message.name && message.message) { const entry = this.createEntry(message); @@ -422,14 +579,17 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { const open = this.open; const scrolledToEnd = open ? this.scrolledToEnd : false; const tab = this.activeTab; + const colors = this.getCharacterColors(message.id); this.addEntryToList(this.local, GENERAL_CHAT_LIMIT, open && tab === 'local', entry); + setNameColors(entry.dom, colors); if (party || whisper) { const partyEntry = { ...entry }; partyEntry.dom = undefined; partyEntry.label = whisper ? partyEntry.label : undefined; this.addEntryToList(this.party, PARTY_CHAT_LIMIT, open && tab === 'party', partyEntry); + setNameColors(partyEntry.dom, colors); } if (whisper) { @@ -437,6 +597,7 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { whisperEntry.dom = undefined; whisperEntry.label = undefined; this.addEntryToList(this.whisper, WHISPER_CHAT_LIMIT, open && tab === 'whisper', whisperEntry); + setNameColors(whisperEntry.dom, colors); } if (message.type === MessageType.Whisper && !this.open) { @@ -447,6 +608,15 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { this.scrollToEnd(); } + if (tab !== 'whisper' && isWhisper(message.type)) { + this.whisperTab.nativeElement.classList.add('unread'); + this.whisperTab.nativeElement.style.backgroundColor = `rgba(225, 161, 223, ${this.opacity / 100})`; + } + if (tab !== 'party' && party) { + this.partyTab.nativeElement.classList.add('unread'); + this.partyTab.nativeElement.style.backgroundColor = `rgba(184, 227, 255, ${this.opacity / 100})`; + } + this.filterChat(); this.messageCounter++; } } @@ -470,7 +640,7 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { if (isOpen) { entry.dom = removedDom || createChatLogLineDOM(this.clickLabel, this.clickNameHandler); - updateChatLogLine(entry.dom, entry); + updateChatLogLine(entry.dom, entry, this.settings.timestamp); this.linesElement.appendChild(entry.dom.root); } } @@ -486,6 +656,7 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { this.regenerateList(); this.scrollToEnd(); this.updateTabs(); + this.filterChat(); } } private updateTabs() { @@ -496,10 +667,12 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { private setActiveTab(tab: HTMLElement, active: boolean) { if (active) { tab.classList.add('active'); + tab.classList.remove('unread'); tab.style.backgroundColor = this.bg; } else { tab.classList.remove('active'); - tab.style.backgroundColor = this.inactiveBg; + if (!tab.classList.contains('unread')) + tab.style.backgroundColor = this.inactiveBg; } } scrollToEnd() { @@ -531,7 +704,8 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { this.messages.forEach(entry => { if (!entry.dom) { entry.dom = createChatLogLineDOM(this.clickLabel, this.clickNameHandler); - updateChatLogLine(entry.dom, entry); + setNameColors(entry.dom, this.getCharacterColors(entry.entityId)); + updateChatLogLine(entry.dom, entry, this.settings.timestamp); } lines.appendChild(entry.dom.root); @@ -548,7 +722,8 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck { } if (resizeX) { - this.settings.chatlogWidth = clamp(x - this.startX, 200, 2000); + const filterBoxWidth = 80; + this.settings.chatlogWidth = clamp(x - this.startX, 200 + filterBoxWidth, 2000); } if (resizeY) { diff --git a/src/ts/components/shared/settings-modal/settings-modal.pug b/src/ts/components/shared/settings-modal/settings-modal.pug index db4c1b1..64c0d60 100644 --- a/src/ts/components/shared/settings-modal/settings-modal.pug +++ b/src/ts/components/shared/settings-modal/settings-modal.pug @@ -71,6 +71,16 @@ [(checked)]="account.ignoreNonFriendWhispers" help="Don't show any whispers from players who are not on your friends list") | Only allow whispers from friends + .form-group + .form-group + label#chatlog-opacity-label Timestamp + div + .btn-group.mb-2(btnCheckbox) + button.btn.btn-outline-secondary.d-sm-block([class.active]='!browser.timestamp' (click)='switchTimestamp()') None + button.btn.btn-outline-secondary.d-sm-block([class.active]='browser.timestamp === "12"' (click)='switchTimestamp("12")') 12-hour format + button.btn.btn-outline-secondary.d-sm-block([class.active]='browser.timestamp === "24"' (click)='switchTimestamp("24")') 24-hour format + small.form-text.text-muted.mt-0 Displays a timestamp in front of chatlog messages + .form-group label#chatlog-opacity-label Chatlog background opacity div diff --git a/src/ts/components/shared/settings-modal/settings-modal.ts b/src/ts/components/shared/settings-modal/settings-modal.ts index fe5a292..57cadd4 100644 --- a/src/ts/components/shared/settings-modal/settings-modal.ts +++ b/src/ts/components/shared/settings-modal/settings-modal.ts @@ -100,6 +100,11 @@ export class SettingsModal implements OnInit, OnDestroy { this.settingsService.saveBrowserSettings(this.browser); this.close.emit(); } + switchTimestamp(state?: string) { + if (!state) this.browser.timestamp = undefined; + else if (state === '12') this.browser.timestamp = '12'; + else if (state === '24') this.browser.timestamp = '24'; + } updateChatlogRange(range: number | undefined) { document.body.classList.add('translucent-modals'); updateRangeIndicator(range, this.game);