fchat-rising/site/character_page/sidebar.vue

248 lines
11 KiB
Vue

<template>
<div id="character-page-sidebar" class="card bg-light">
<div class="card-header">
<span class="character-name">{{ character.character.name }}</span>
<div v-if="character.character.title" class="character-title">{{ character.character.title }}</div>
<character-action-menu :character="character" @rename="showRename()" @delete="showDelete()"
@block="showBlock()"></character-action-menu>
</div>
<div class="card-body">
<img :src="avatarUrl(character.character.name)" class="character-avatar" style="margin-right:10px">
<div v-if="authenticated" class="d-flex justify-content-between flex-wrap character-links-block">
<template v-if="character.is_self">
<a :href="editUrl" class="edit-link"><i class="fa fa-fw fa-pencil-alt"></i>Edit</a>
<a @click="showDelete" class="delete-link"><i class="fa fa-fw fa-trash"></i>Delete</a>
<a @click="showDuplicate()" class="duplicate-link"><i class="fa fa-fw fa-copy"></i>Duplicate</a>
</template>
<template v-else>
<span v-if="character.self_staff || character.settings.block_bookmarks !== true">
<a @click.prevent="toggleBookmark()" href="#" class="btn"
:class="{bookmarked: character.bookmarked, unbookmarked: !character.bookmarked}">
<i class="fa fa-fw" :class="{'fa-minus': character.bookmarked, 'fa-plus': !character.bookmarked}"></i>Bookmark
</a>
<span v-if="character.settings.block_bookmarks" class="prevents-bookmarks">!</span>
</span>
<a href="#" @click.prevent="showFriends()" class="friend-link btn"><i class="fa fa-fw fa-user"></i>Friend</a>
<a href="#" v-if="!oldApi" @click.prevent="showReport()" class="report-link btn">
<i class="fa fa-fw fa-exclamation-triangle"></i>Report</a>
</template>
<a href="#" @click.prevent="showMemo()" class="memo-link btn"><i class="far fa-sticky-note fa-fw"></i>Memo</a>
</div>
<div v-if="character.badges && character.badges.length > 0" class="badges-block">
<div v-for="badge in character.badges" class="character-badge px-2 py-1" :class="badgeClass(badge)">
<i class="fa-fw" :class="badgeIconClass(badge)"></i> {{ badgeTitle(badge) }}
</div>
</div>
<a v-if="authenticated && !character.is_self" :href="noteUrl" class="character-page-note-link btn" style="padding:0 4px">
<i class="far fa-envelope fa-fw"></i>Send Note</a>
<div v-if="character.character.online_chat" @click="showInChat()" class="character-page-online-chat">Online In Chat</div>
<div class="contact-block">
<contact-method v-for="method in contactMethods" :infotag="method" :key="method.id"
:data="character.character.infotags[method.id]"></contact-method>
</div>
<div class="quick-info-block">
<infotag-item v-for="id in quickInfoIds" v-if="character.character.infotags[id]" :infotag="getInfotag(id)"
:data="character.character.infotags[id]" :key="id"></infotag-item>
<div class="quick-info">
<span class="quick-info-label">Created: </span>
<span class="quick-info-value"><date :time="character.character.created_at"></date></span>
</div>
<div class="quick-info">
<span class="quick-info-label">Last updated: </span>
<span class="quick-info-value"><date :time="character.character.updated_at"></date></span>
</div>
<div class="quick-info" v-if="character.character.last_online_at">
<span class="quick-info-label">Last online:</span>
<span class="quick-info-value"><date :time="character.character.last_online_at"></date></span>
</div>
<div class="quick-info">
<span class="quick-info-label">Views: </span>
<span class="quick-info-value">{{character.character.views}}</span>
</div>
<div class="quick-info" v-if="character.character.timezone != null">
<span class="quick-info-label">Timezone:</span>
<span class="quick-info-value">
UTC{{character.character.timezone > 0 ? '+' : ''}}{{character.character.timezone != 0 ? character.character.timezone : ''}}
</span>
</div>
</div>
<div class="character-list-block" v-if="character.character_list">
<div v-for="listCharacter in character.character_list">
<img :src="avatarUrl(listCharacter.name)" class="character-avatar icon" style="margin-right:5px">
<character-link :character="listCharacter.name"></character-link>
</div>
</div>
</div>
<template>
<memo-dialog :character="character.character" :memo="character.memo" ref="memo-dialog" @memo="memo"></memo-dialog>
<delete-dialog :character="character" ref="delete-dialog"></delete-dialog>
<rename-dialog :character="character" ref="rename-dialog"></rename-dialog>
<duplicate-dialog :character="character" ref="duplicate-dialog"></duplicate-dialog>
<report-dialog v-if="!oldApi && authenticated && !character.is_self" :character="character" ref="report-dialog"></report-dialog>
<friend-dialog :character="character" ref="friend-dialog"></friend-dialog>
<block-dialog :character="character" ref="block-dialog"></block-dialog>
</template>
</div>
</template>
<script lang="ts">
import {Component, Prop} from '@f-list/vue-ts';
import Vue, {Component as VueComponent, ComponentOptions, CreateElement, VNode} from 'vue';
import DateDisplay from '../../components/date_display.vue';
import {Infotag} from '../../interfaces';
import * as Utils from '../utils';
import ContactMethodView from './contact_method.vue';
import {methods, registeredComponents, Store} from './data_store';
import DeleteDialog from './delete_dialog.vue';
import DuplicateDialog from './duplicate_dialog.vue';
import FriendDialog from './friend_dialog.vue';
import InfotagView from './infotag.vue';
import {Character, CONTACT_GROUP_ID} from './interfaces';
import MemoDialog from './memo_dialog.vue';
import ReportDialog from './report_dialog.vue';
interface ShowableVueDialog extends Vue {
show(): void
}
function resolveComponent(name: string): () => Promise<VueComponent | ComponentOptions<Vue>> {
return async(): Promise<VueComponent | ComponentOptions<Vue>> => {
if(typeof registeredComponents[name] === 'undefined')
return {
render(createElement: CreateElement): VNode {
return createElement('span');
},
name
};
return registeredComponents[name]!;
};
}
Vue.component('block-dialog', resolveComponent('block-dialog'));
Vue.component('rename-dialog', resolveComponent('rename-dialog'));
Vue.component('character-action-menu', resolveComponent('character-action-menu'));
@Component({
components: {
'contact-method': ContactMethodView,
date: DateDisplay,
'delete-dialog': DeleteDialog,
'duplicate-dialog': DuplicateDialog,
'friend-dialog': FriendDialog,
'infotag-item': InfotagView,
'memo-dialog': MemoDialog,
'report-dialog': ReportDialog
}
})
export default class Sidebar extends Vue {
@Prop({required: true})
readonly character!: Character;
@Prop
readonly oldApi?: true;
readonly quickInfoIds: ReadonlyArray<number> = [1, 3, 2, 49, 9, 29, 15, 41, 25]; // Do not sort these.
readonly avatarUrl = Utils.avatarURL;
badgeClass(badgeName: string): string {
return `character-badge-${badgeName.replace('.', '-')}`;
}
badgeIconClass(badgeName: string): string {
const classMap: {[key: string]: string} = {
admin: 'fa fa-gem',
global: 'far fa-gem',
chatop: 'far fa-gem',
chanop: 'fa fa-star',
helpdesk: 'fa fa-user',
developer: 'fa fa-terminal',
'subscription.lifetime': 'fa fa-certificate'
};
return badgeName in classMap ? classMap[badgeName] : '';
}
badgeTitle(badgeName: string): string {
const badgeMap: {[key: string]: string} = {
admin: 'Administrator',
global: 'Global Moderator',
chatop: 'Chat Moderator',
chanop: 'Channel Moderator',
helpdesk: 'Helpdesk',
developer: 'Developer',
'subscription.lifetime': 'Lifetime Subscriber',
'subscription.other': 'Subscriber'
};
return badgeName in badgeMap ? badgeMap[badgeName] : badgeName;
}
showBlock(): void {
(<ShowableVueDialog>this.$refs['block-dialog']).show();
}
showRename(): void {
(<ShowableVueDialog>this.$refs['rename-dialog']).show();
}
showDelete(): void {
(<ShowableVueDialog>this.$refs['delete-dialog']).show();
}
showDuplicate(): void {
(<ShowableVueDialog>this.$refs['duplicate-dialog']).show();
}
showMemo(): void {
(<ShowableVueDialog>this.$refs['memo-dialog']).show();
}
showReport(): void {
(<ShowableVueDialog>this.$refs['report-dialog']).show();
}
showFriends(): void {
(<ShowableVueDialog>this.$refs['friend-dialog']).show();
}
showInChat(): void {
//TODO implement this
}
async toggleBookmark(): Promise<void> {
try {
await methods.bookmarkUpdate(this.character.character.id, !this.character.bookmarked);
this.character.bookmarked = !this.character.bookmarked;
} catch(e) {
Utils.ajaxError(e, 'Unable to change bookmark state.');
}
}
get editUrl(): string {
return `${Utils.siteDomain}character/${this.character.character.id}/edit`;
}
get noteUrl(): string {
return methods.sendNoteUrl(this.character.character);
}
get contactMethods(): {id: number, value?: string}[] {
return Object.keys(Store.shared.infotags).map((x) => Store.shared.infotags[x])
.filter((x) => x.infotag_group === CONTACT_GROUP_ID && this.character.character.infotags[x.id] !== undefined)
.sort((a, b) => a.name < b.name ? -1 : 1);
}
getInfotag(id: number): Infotag {
return Store.shared.infotags[id];
}
get authenticated(): boolean {
return Store.authenticated;
}
memo(memo: object): void {
this.$emit('memo', memo);
}
}
</script>