From 79cfb2b93884a9261a4152e5936e45dd31170064 Mon Sep 17 00:00:00 2001 From: "Mr. Stallion" <mrstallion@nobody.nowhere.fauxdomain.ext> Date: Sat, 21 Mar 2020 13:35:22 -0500 Subject: [PATCH] Status message history --- chat/CharacterSearch.vue | 2 +- chat/ConversationView.vue | 2 +- chat/StatusPicker.vue | 111 ++++++++++++++++++++++++++++++++++++ chat/StatusSwitcher.vue | 36 +++++++++++- chat/interfaces.ts | 5 +- components/custom_dialog.ts | 2 +- electron/index.html | 2 +- readme.md | 17 +++--- 8 files changed, 162 insertions(+), 15 deletions(-) create mode 100644 chat/StatusPicker.vue diff --git a/chat/CharacterSearch.vue b/chat/CharacterSearch.vue index 745266a..74cc13a 100644 --- a/chat/CharacterSearch.vue +++ b/chat/CharacterSearch.vue @@ -286,7 +286,7 @@ .search-spinner { float: right; - animation: search-spin 4s linear infinite; + animation: search-spin 0.75s linear infinite; } } diff --git a/chat/ConversationView.vue b/chat/ConversationView.vue index 933d700..dde1e32 100644 --- a/chat/ConversationView.vue +++ b/chat/ConversationView.vue @@ -56,7 +56,7 @@ class="nav-link" href="#" @click.prevent="setMode(mode)">{{l('channel.mode.' + mode)}}</a> </li> <li> - <a @click.prevent="toggleNonMatchingAds()" :class="{active: showNonMatchingAds, disabled: !showNonMatchingAds}" v-show="(conversation.mode == 'both' || conversation.mode == 'ads')" + <a @click.prevent="toggleNonMatchingAds()" :class="{active: showNonMatchingAds}" v-show="(conversation.mode == 'both' || conversation.mode == 'ads')" class="nav-link" href="#">Non-Matching</a> </li> </ul> diff --git a/chat/StatusPicker.vue b/chat/StatusPicker.vue new file mode 100644 index 0000000..36bc7ed --- /dev/null +++ b/chat/StatusPicker.vue @@ -0,0 +1,111 @@ +<template> + <modal action="Status message history" buttonText="Select" @open="onMounted()" @submit="selectStatus" dialogClass="w-100 modal-lg"> + <form class="status-picker" v-if="history.length > 0"> + <div class="form-row" v-for="(historicStatus, index) in history" :class="{ 'selected-row': (index === selectedStatus)}"> + <div class="form-col radio-col"> + <input type="radio" :id="'history_status_' + index" :name="'history_status_' + index" v-model="selectedStatus" v-bind:value="index" /> + </div> + <div class="form-col content-col"> + <label class="custom-control-label" :for="'history_status_' + index"> + <bbcode :text="historicStatus"></bbcode> + </label> + </div> + </div> + </form> + <div v-else> + <i>This character has no status message history.</i> + </div> + </modal> +</template> + +<script lang="ts"> + import { Component, Hook, Prop } from '@f-list/vue-ts'; + import Modal from '../components/Modal.vue'; + import Dropdown from '../components/Dropdown.vue'; + import CustomDialog from '../components/custom_dialog'; + import core from './core'; + import { BBCodeView } from '../bbcode/view'; + import * as _ from 'lodash'; + + @Component({ + components: {modal: Modal, dropdown: Dropdown, bbcode: BBCodeView(core.bbCodeParser)} + }) + export default class StatusPicker extends CustomDialog { + @Prop({required: true}) + readonly callback!: (statusMessage: string) => void; + + @Prop({required: true}) + readonly curStatus!: string | undefined; + + history: string[] = []; + + selectedStatus: number | null = null; + + @Hook('mounted') + async onMounted(): Promise<void> { + this.history = (await core.settingsStore.get('statusHistory')) || []; + this.selectedStatus = null; + + if ((this.curStatus) && (this.curStatus.trim() !== '')) { + const cleanedStatus = this.curStatus.toLowerCase().trim(); + + const index = _.findIndex( + this.history, + (c: string) => (c.toString().toLowerCase().trim() === cleanedStatus) + ); + + if (index >= 0) { + this.selectedStatus = index; + } + } + } + + + selectStatus(): void { + if (this.selectedStatus !== null) { + this.callback(this.history[this.selectedStatus]); + } + } + } +</script> + +<style lang="scss"> + .status-picker { + .radio-col { + display: none; + } + + label::before { + display:none !important; + } + + .content-col { + min-width: 100%; + + label { + min-width: 100%; + } + } + + .form-row { + margin-bottom: 10px; + padding: 3px; + + border: 1px solid rgba(0,0,0,0); + border-radius: 2px; + } + + .form-row:hover { + background-color: #20203e; + border: 1px solid #2d2d6b; + border-radius: 2px; + } + + .selected-row, + .form-row.selected-row:hover { + background-color: #343461; + border: 1px solid #6565b2; + border-radius: 2px; + } + } +</style> diff --git a/chat/StatusSwitcher.vue b/chat/StatusSwitcher.vue index 19add28..4afabb1 100644 --- a/chat/StatusSwitcher.vue +++ b/chat/StatusSwitcher.vue @@ -17,6 +17,11 @@ </div> </editor> </div> + <div class="form-group"> + <button type="button" @click="showStatusPicker" class="btn btn-outline-secondary">History</button> + </div> + + <status-picker ref="statusPicker" :callback="insertStatusMessage" :curStatus="enteredText"></status-picker> </modal> </template> @@ -31,9 +36,11 @@ import {Character, userStatuses} from './interfaces'; import l from './localize'; import {getStatusIcon} from './UserView.vue'; + import StatusPicker from './StatusPicker.vue'; + import * as _ from 'lodash'; @Component({ - components: {modal: Modal, editor: Editor, dropdown: Dropdown} + components: {modal: Modal, editor: Editor, dropdown: Dropdown, 'status-picker': StatusPicker} }) export default class StatusSwitcher extends CustomDialog { selectedStatus: Character.Status | undefined; @@ -65,11 +72,36 @@ setStatus(): void { core.connection.send('STA', {status: this.status, statusmsg: this.text}); + + // tslint:disable-next-line + this.updateHistory(this.text); } reset(): void { this.selectedStatus = undefined; this.enteredText = undefined; } + + insertStatusMessage(statusMessage: string): void { + this.text = statusMessage; + } + + + async updateHistory(statusMessage: string): Promise<void> { + if ((!statusMessage) || (statusMessage.trim() === '')) { + return; + } + + const curHistory: string[] = (await core.settingsStore.get('statusHistory')) || []; + const statusMessageClean = statusMessage.toString().trim().toLowerCase(); + const filteredHistory: string[] = _.reject(curHistory, (c: string) => (c.toString().trim().toLowerCase() === statusMessageClean)); + const newHistory: string[] = _.take(_.concat([statusMessage], filteredHistory), 10); + + await core.settingsStore.set('statusHistory', newHistory); + } + + showStatusPicker(): void { + (<StatusPicker>this.$refs['statusPicker']).show(); + } } -</script> \ No newline at end of file +</script> diff --git a/chat/interfaces.ts b/chat/interfaces.ts index 046e5dd..22f170f 100644 --- a/chat/interfaces.ts +++ b/chat/interfaces.ts @@ -145,13 +145,14 @@ export interface Logs { export namespace Settings { export type Keys = { - settings: Settings, - pinned: {channels: string[], private: string[]}, + settings: Settings + pinned: {channels: string[], private: string[]} conversationSettings: {[key: string]: Conversation.Settings | undefined} modes: {[key: string]: Channel.Mode | undefined} recent: Conversation.RecentPrivateConversation[] recentChannels: Conversation.RecentChannelConversation[] hiddenUsers: string[] + statusHistory: string[] }; export interface Store { diff --git a/components/custom_dialog.ts b/components/custom_dialog.ts index 971be27..a2b0248 100644 --- a/components/custom_dialog.ts +++ b/components/custom_dialog.ts @@ -17,4 +17,4 @@ export default class CustomDialog extends Vue { hide(): void { this.dialog.hide(); } -} \ No newline at end of file +} diff --git a/electron/index.html b/electron/index.html index 559538b..b5d7a50 100644 --- a/electron/index.html +++ b/electron/index.html @@ -12,4 +12,4 @@ <script type="text/javascript" src="common.js"></script> <script type="text/javascript" src="chat.js"></script> </body> -</html> \ No newline at end of file +</html> diff --git a/readme.md b/readme.md index 3c4d41f..6554894 100644 --- a/readme.md +++ b/readme.md @@ -7,10 +7,11 @@ This repository contains a heavily customized version of the mainline F-Chat 3.0 1. **Profile matching** automatically compares your profile with others to determine with whom you are compatible. 1. **Automatic ad posting** repeatedly posts and rotates ads on selected channels. -1. **Link previews** pop up shows a preview of an image when you hover your mouse over a link. +1. **Link previews** popup shows a preview of an image / video when you hover your mouse over a link. +1. **Caching** speeds up profile loads and other actions. -### More Details +### More Detailed Differences * Ads view * Highlight ads from characters most interesting to you @@ -42,10 +43,13 @@ This repository contains a heavily customized version of the mainline F-Chat 3.0 * Display match score in search results * Current search filters are listed in the search dialog * Search filters can be reset +* Character status + * Last 10 status messages are stored and can be accessed from the 'Set status' dialog * General - * Character profiles, guestbooks, friend lists, and image lists are cached for faster access. + * Character profiles, guestbooks, friend lists, and image lists are cached for faster access * Conversation dialog can be opened by typing in a character name - * Message search also checks character names + * Message search matches character names + * PM list shows characters' online status as a colored icon ## How to Set Up Ads @@ -76,10 +80,9 @@ This repository contains a heavily customized version of the mainline F-Chat 3.0 * Save character's status messages * Conversation bot API * 'Filter unmatching ads' is not channel specific -- it's either on everywhere or nowhere -* AD UI Cleanup / hide to popovers +* AD UI Cleanup / hide to popovers? * image loading animation -* Ad cache is broken -* Show online / offline status on PM list +* Usually submissive vs usually submissive shows up as 'maybe'? # F-List Exported