This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Zaimki/routes/profile.vue

324 lines
12 KiB
Vue
Raw Normal View History

<template>
2020-11-16 11:43:44 -08:00
<div v-if="profile">
<ClientOnly>
<div slot="placeholder" class="my-5 text-center">
<Spinner size="5rem"/>
</div>
2020-10-24 12:50:08 -07:00
<div class="mb-3 d-flex justify-content-between flex-column flex-md-row">
2020-10-23 11:24:43 -07:00
<h2 class="text-nowrap">
<Avatar :user="profile"/>
@{{username}}
</h2>
2020-10-24 12:50:08 -07:00
<div>
2021-03-01 13:35:03 -08:00
<div class="text-end">
2020-12-08 14:14:40 -08:00
<nuxt-link v-if="$user() && $user().username === username" to="/editor"
class="btn btn-outline-primary btn-sm mb-2"
>
<Icon v="edit"/>
<T>profile.edit</T>
</nuxt-link>
</div>
2021-01-21 09:56:08 -08:00
<div v-if="Object.keys(profiles).length > 1" class="locale-list">
2020-12-06 08:15:08 -08:00
<a :href="`https://pronouns.page/@${username}`" v-if="$user() && $user().username === username"
2021-01-22 14:54:24 -08:00
class="btn btn-outline-secondary btn-sm mb-1 me-1"
2020-12-06 08:15:08 -08:00
>
<Icon v="external-link"/>
pronouns.page/@{{username}}
</a>
2021-01-21 09:56:08 -08:00
<br/>
2020-10-24 12:50:08 -07:00
<LocaleLink v-for="(options, locale) in locales" :key="locale" v-if="profiles[locale] !== undefined"
:locale="locale" :link="`/@${username}`"
2021-01-22 14:54:24 -08:00
:class="['btn', locale === config.locale ? 'btn-primary disabled' : 'btn-outline-primary', 'btn-sm', 'mb-1 me-1']">
2020-10-24 12:50:08 -07:00
{{options.name}}
</LocaleLink>
</div>
</div>
2020-10-23 11:24:43 -07:00
</div>
<section v-if="$isGranted('users') && profile.bannedReason">
<div class="alert alert-warning">
<p class="h4">
<Icon v="ban"/>
{{$t('ban.banned')}}
</p>
<p class="mb-0">{{profile.bannedReason}}</p>
</div>
</section>
2021-06-16 07:08:38 -07:00
<section v-if="profile.age ||profile.description.trim().length">
2020-10-23 11:24:43 -07:00
<p v-for="line in profile.description.split('\n')" class="mb-1">
2021-04-12 04:57:18 -07:00
<Spelling escape :text="line"/>
2020-10-23 11:24:43 -07:00
</p>
<p v-if="profile.age">
<Icon v="birthday-cake"/>
{{ profile.age }}
</p>
2020-10-23 11:24:43 -07:00
</section>
<section v-if="profile.flags.length || Object.keys(profile.customFlags).length">
2020-10-23 11:24:43 -07:00
<ul class="list-inline">
<li v-for="flag in profile.flags" v-if="allFlags[flag]" class="list-inline-item pr-2">
2021-04-05 08:03:13 -07:00
<Flag :name="flag.startsWith('-') ? allFlags[flag] : $translateForPronoun(allFlags[flag], mainPronoun)"
:alt="allFlags[flag]"
:img="`/flags/${flag}.png`"
:terms="terms"/>
</li>
<li v-for="(desc, flag) in profile.customFlags" class="list-inline-item pr-2">
<Flag :name="desc"
:alt="desc"
:img="buildImageUrl(flag, 'flag')"
:terms="terms"
custom/>
2020-10-23 11:24:43 -07:00
</li>
</ul>
</section>
2020-10-26 16:47:56 -07:00
<section v-if="profile.links.length">
2020-10-23 11:24:43 -07:00
<ul class="list-inline">
2020-10-26 16:47:56 -07:00
<li v-for="link in profile.links" class="list-inline-item pr-2">
<ProfileLink :link="link"/>
2020-10-23 11:24:43 -07:00
</li>
</ul>
</section>
<section class="d-flex">
<div class="w-50" v-if="Object.keys(profile.names).length">
<h3>
<Icon v="signature"/>
<T>profile.names</T>
</h3>
2020-10-23 11:24:43 -07:00
<ul class="list-unstyled">
<li v-for="(opinion, name) in profile.names"><Opinion :word="name" :opinion="opinion"/></li>
</ul>
2020-10-23 11:24:43 -07:00
</div>
<div class="w-50" v-if="Object.keys(profile.pronouns).length">
<h3>
<Icon v="tags"/>
<T>profile.pronouns</T>
</h3>
2020-10-23 11:24:43 -07:00
<ul class="list-unstyled">
<li v-for="{link, pronoun, opinion} in pronounOpinions">
<Opinion :word="typeof pronoun === 'string' ? pronoun : (pronoun.name(glue) + (pronoun.smallForm ? '/' + pronoun.morphemes[pronoun.smallForm] : ''))" :opinion="opinion" :link="`/${link}`"/>
2020-10-24 12:50:08 -07:00
</li>
2020-10-23 11:24:43 -07:00
</ul>
</div>
</section>
2020-11-27 10:57:14 -08:00
<section class="clearfix">
2020-10-23 11:24:43 -07:00
<h3>
<Icon v="scroll-old"/>
<T>profile.words</T>
</h3>
2020-10-25 04:00:45 -07:00
<div>
2021-01-22 14:54:24 -08:00
<div v-for="group in profile.words" v-if="Object.keys(profile.words).length" class="float-start w-50 w-md-25">
<ul class="list-unstyled">
2020-10-23 11:24:43 -07:00
<li v-for="(opinion, word) in group"><Opinion :word="word" :opinion="opinion"/></li>
</ul>
</div>
2020-10-23 11:24:43 -07:00
</div>
</section>
2020-11-23 09:14:46 -08:00
<section>
<OpinionLegend/>
</section>
2021-04-04 13:45:42 -07:00
<client-only>
<section v-if="$isGranted('users')">
<div class="alert alert-warning">
<textarea v-model="profile.bannedReason" class="form-control" rows="3" :placeholder="$t('ban.reason')" :disabled="saving"></textarea>
<button class="btn btn-danger d-block w-100 mt-2" :disabled="saving" @click="ban">
<Icon v="ban"/>
{{$t('ban.action')}}
</button>
</div>
</section>
</client-only>
2021-06-16 07:08:38 -07:00
2021-04-04 13:45:42 -07:00
<Separator icon="heart"/>
2021-04-06 06:15:59 -07:00
<Support/>
<section>
<Share/>
</section>
</ClientOnly>
</div>
2020-11-16 11:43:44 -08:00
<div v-else-if="Object.keys(profiles).length">
2020-10-28 08:22:29 -07:00
<h2 class="text-nowrap mb-3">
<Avatar :user="profiles[Object.keys(profiles)[0]]"/>
@{{username}}
</h2>
<div class="list-group">
<LocaleLink v-for="(options, locale) in locales" :key="locale" v-if="profiles[locale] !== undefined"
:locale="locale" :link="`/@${username}`"
class="list-group-item list-group-item-action list-group-item-hoverable">
<div class="h3">
{{options.name}}
</div>
</LocaleLink>
</div>
</div>
<NotFound v-else/>
</template>
<script>
2021-06-16 07:08:38 -07:00
import {head, listToDict} from "../src/helpers";
import { pronouns } from "~/src/data";
import { buildPronoun } from "../src/buildPronoun";
import ClientOnly from 'vue-client-only'
export default {
components: { ClientOnly },
data() {
2020-10-28 08:22:29 -07:00
return {
2021-06-16 07:08:38 -07:00
profiles: {},
glue: ' ' + this.$t('pronouns.or') + ' ',
allFlags: process.env.FLAGS,
saving: false,
terms: [],
}
},
async asyncData({ app, route }) {
return {
profiles: await app.$axios.$get(`/profile/get/${encodeURIComponent(route.params.pathMatch)}`),
};
},
async mounted() {
if (this.config.nouns.terms.enabled) {
this.terms = await this.$axios.$get(`/terms`);
}
},
computed: {
2021-01-01 12:11:37 -08:00
username() {
const base = this.$route.params.pathMatch;
if (!this.profile) {
return base;
}
if (this.profile.username !== base && process.client) {
history.pushState(
'',
document.title,
'/@' + this.profile.username,
);
}
return this.profile.username;
},
profile() {
for (let locale in this.profiles) {
if (locale === this.config.locale) {
return this.profiles[locale];
}
}
return null;
},
pronounOpinions() {
const pronounOpinions = [];
2020-10-24 12:50:08 -07:00
for (let pronoun in this.profile.pronouns) {
if (!this.profile.pronouns.hasOwnProperty(pronoun)) { continue; }
let link = decodeURIComponent(
2020-11-24 15:54:02 -08:00
pronoun
2020-12-05 13:01:12 -08:00
.trim()
2020-11-24 15:54:02 -08:00
.replace(new RegExp('^' + this.$base), '')
.replace(new RegExp('^' + this.$base.replace(/^https?:\/\//, '')), '')
.replace(new RegExp('^/'), '')
);
if (!link.startsWith(':')) {
link = link.toLowerCase();
}
if (link === this.config.pronouns.any || link === this.config.pronouns.avoiding) {
pronounOpinions.push({
link,
pronoun: link,
opinion: this.profile.pronouns[pronoun],
});
continue;
}
const pronounEntity = buildPronoun(pronouns, link);
2020-10-24 12:50:08 -07:00
if (pronounEntity) {
pronounOpinions.push({
2020-10-24 12:50:08 -07:00
link,
pronoun: pronounEntity,
opinion: this.profile.pronouns[pronoun],
2020-10-24 12:50:08 -07:00
});
}
}
return pronounOpinions;
2020-10-24 12:50:08 -07:00
},
2020-12-08 14:14:40 -08:00
mainPronoun() {
let mainPronoun = buildPronoun(pronouns, this.config.profile.flags.defaultPronoun);
let mainOpinion = -1;
for (let {pronoun, opinion} of this.pronounOpinions) {
if (typeof pronoun === 'string') {
continue;
}
2020-12-08 14:14:40 -08:00
if (opinion === 2) {
opinion = 0.5;
}
if (opinion > mainOpinion) {
mainPronoun = pronoun;
mainOpinion = opinion;
}
}
return mainPronoun;
},
},
2021-06-16 07:08:38 -07:00
methods: {
async ban() {
await this.$confirm(this.$t('ban.confirm', {username: this.username}), 'danger');
this.saving = true;
try {
await this.$post(`/admin/ban/${encodeURIComponent(this.username)}`, {
reason: this.profile.bannedReason,
});
window.location.reload();
} finally {
this.saving = false;
}
}
},
head() {
return head({
title: `@${this.username}`,
2020-10-31 13:33:59 -07:00
banner: `api/banner/@${this.username}.png`,
});
},
}
</script>
<style lang="scss" scoped>
2020-11-23 14:14:31 -08:00
@import "assets/variables";
2020-10-28 08:22:29 -07:00
.avatar {
width: 100%;
max-width: 5rem;
max-height: 5rem;
}
2020-10-28 08:22:29 -07:00
.list-group-item-hoverable {
&:hover {
color: $primary;
2021-03-01 13:35:03 -08:00
border-inline-start: 3px solid $primary;
padding-inline-start: calc(#{$list-group-item-padding-x} - 2px);
2020-10-28 08:22:29 -07:00
}
}
2021-01-21 09:56:08 -08:00
@include media-breakpoint-up('md', $grid-breakpoints) {
.locale-list {
max-width: 16rem;
text-align: right;
}
}
</style>