revert unnecessary changes

This commit is contained in:
Henry Jameson 2019-04-10 21:48:42 +03:00
parent 6471bec0d9
commit 262760d258
120 changed files with 2128 additions and 4290 deletions

View File

@ -1,112 +1,51 @@
<template> <template>
<div <div id="app" v-bind:style="bgAppStyle">
id="app" <div class="app-bg-wrapper" v-bind:style="bgStyle"></div>
:style="bgAppStyle"
>
<div
class="app-bg-wrapper"
:style="bgStyle"
/>
<MobileNav v-if="isMobileLayout" /> <MobileNav v-if="isMobileLayout" />
<nav <nav v-else class='nav-bar container' @click="scrollToTop()" id="nav">
v-else <div class='logo' :style='logoBgStyle'>
id="nav" <div class='mask' :style='logoMaskStyle'></div>
class="nav-bar container" <img :src='logo' :style='logoStyle'>
@click="scrollToTop()"
>
<div
class="logo"
:style="logoBgStyle"
>
<div
class="mask"
:style="logoMaskStyle"
/>
<img
:src="logo"
:style="logoStyle"
>
</div> </div>
<div class="inner-nav"> <div class='inner-nav'>
<div class="item"> <div class='item'>
<router-link <router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link>
class="site-name"
:to="{ name: 'root' }"
active-class="home"
>
{{ sitename }}
</router-link>
</div> </div>
<div class="item right"> <div class='item right'>
<user-finder <user-finder class="button-icon nav-icon mobile-hidden" @toggled="onFinderToggled"></user-finder>
class="button-icon nav-icon mobile-hidden" <router-link class="mobile-hidden" :to="{ name: 'settings'}"><i class="button-icon icon-cog nav-icon" :title="$t('nav.preferences')"></i></router-link>
@toggled="onFinderToggled" <a href="#" class="mobile-hidden" v-if="currentUser" @click.prevent="logout"><i class="button-icon icon-logout nav-icon" :title="$t('login.logout')"></i></a>
/>
<router-link
class="mobile-hidden"
:to="{ name: 'settings'}"
>
<i
class="button-icon icon-cog nav-icon"
:title="$t('nav.preferences')"
/>
</router-link>
<a
v-if="currentUser"
href="#"
class="mobile-hidden"
@click.prevent="logout"
><i
class="button-icon icon-logout nav-icon"
:title="$t('login.logout')"
/></a>
</div> </div>
</div> </div>
</nav> </nav>
<div <div v-if="" class="container" id="content">
id="content" <div class="sidebar-flexer mobile-hidden" v-if="!isMobileLayout">
class="container"
>
<div
v-if="!isMobileLayout"
class="sidebar-flexer mobile-hidden"
>
<div class="sidebar-bounds"> <div class="sidebar-bounds">
<div class="sidebar-scroller"> <div class="sidebar-scroller">
<div class="sidebar"> <div class="sidebar">
<user-panel /> <user-panel></user-panel>
<nav-panel /> <nav-panel></nav-panel>
<instance-specific-panel v-if="showInstanceSpecificPanel" /> <instance-specific-panel v-if="showInstanceSpecificPanel"></instance-specific-panel>
<features-panel v-if="!currentUser && showFeaturesPanel" /> <features-panel v-if="!currentUser && showFeaturesPanel"></features-panel>
<who-to-follow-panel v-if="currentUser && suggestionsEnabled" /> <who-to-follow-panel v-if="currentUser && suggestionsEnabled"></who-to-follow-panel>
<notifications v-if="currentUser" /> <notifications v-if="currentUser"></notifications>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<div <div v-if="!currentUser" class="login-hint panel panel-default">
v-if="!currentUser" <router-link :to="{ name: 'login' }" class="panel-body">
class="login-hint panel panel-default"
>
<router-link
:to="{ name: 'login' }"
class="panel-body"
>
{{ $t("login.hint") }} {{ $t("login.hint") }}
</router-link> </router-link>
</div> </div>
<transition name="fade"> <transition name="fade">
<router-view /> <router-view></router-view>
</transition> </transition>
</div> </div>
<media-modal /> <media-modal></media-modal>
</div> </div>
<chat-panel <chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel>
v-if="currentUser && chat"
:floating="true"
class="floating-chat mobile-hidden"
/>
</div> </div>
</template> </template>

View File

@ -24,8 +24,8 @@ export default (store) => {
path: '/', path: '/',
redirect: _to => { redirect: _to => {
return (store.state.users.currentUser return (store.state.users.currentUser
? store.state.instance.redirectRootLogin ? store.state.instance.redirectRootLogin
: store.state.instance.redirectRootNoLogin) || '/main/all' : store.state.instance.redirectRootNoLogin) || '/main/all'
} }
}, },
{ name: 'public-external-timeline', path: '/main/all', component: PublicAndExternalTimeline }, { name: 'public-external-timeline', path: '/main/all', component: PublicAndExternalTimeline },

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="sidebar"> <div class="sidebar">
<instance-specific-panel /> <instance-specific-panel></instance-specific-panel>
<features-panel v-if="showFeaturesPanel" /> <features-panel v-if="showFeaturesPanel"></features-panel>
<terms-of-service-panel /> <terms-of-service-panel></terms-of-service-panel>
</div> </div>
</template> </template>

View File

@ -51,7 +51,7 @@ const Attachment = {
} }
}, },
methods: { methods: {
linkClicked ({ target }) { linkClicked ({target}) {
if (target.tagName === 'A') { if (target.tagName === 'A') {
window.open(target.href, '_blank') window.open(target.href, '_blank')
} }

View File

@ -1,104 +1,54 @@
<template> <template>
<div <div v-if="usePlaceHolder" @click="openModal">
v-if="usePlaceHolder" <a class="placeholder"
@click="openModal"
>
<a
v-if="type !== 'html'" v-if="type !== 'html'"
class="placeholder" target="_blank" :href="attachment.url"
target="_blank"
:href="attachment.url"
> >
[{{ nsfw ? "NSFW/" : "" }}{{ type.toUpperCase() }}] [{{nsfw ? "NSFW/" : ""}}{{type.toUpperCase()}}]
</a> </a>
</div> </div>
<div <div
v-else v-else class="attachment"
v-show="!isEmpty"
class="attachment"
:class="{[type]: true, loading, 'fullwidth': fullwidth, 'nsfw-placeholder': hidden}" :class="{[type]: true, loading, 'fullwidth': fullwidth, 'nsfw-placeholder': hidden}"
v-show="!isEmpty"
> >
<a <a class="image-attachment" v-if="hidden" :href="attachment.url" @click.prevent="toggleHidden">
v-if="hidden" <img class="nsfw" :key="nsfwImage" :src="nsfwImage" :class="{'small': isSmall}"/>
class="image-attachment" <i v-if="type === 'video'" class="play-icon icon-play-circled"></i>
:href="attachment.url"
@click.prevent="toggleHidden"
>
<img
:key="nsfwImage"
class="nsfw"
:src="nsfwImage"
:class="{'small': isSmall}"
>
<i
v-if="type === 'video'"
class="play-icon icon-play-circled"
/>
</a> </a>
<div <div class="hider" v-if="nsfw && hideNsfwLocal && !hidden">
v-if="nsfw && hideNsfwLocal && !hidden" <a href="#" @click.prevent="toggleHidden">Hide</a>
class="hider"
>
<a
href="#"
@click.prevent="toggleHidden"
>Hide</a>
</div> </div>
<a <a v-if="type === 'image' && (!hidden || preloadImage)"
v-if="type === 'image' && (!hidden || preloadImage)" @click="openModal"
class="image-attachment" class="image-attachment"
:class="{'hidden': hidden && preloadImage }" :class="{'hidden': hidden && preloadImage }"
:href="attachment.url" :href="attachment.url" target="_blank"
target="_blank"
:title="attachment.description" :title="attachment.description"
@click="openModal"
> >
<StillImage <StillImage :referrerpolicy="referrerpolicy" :mimetype="attachment.mimetype" :src="attachment.large_thumb_url || attachment.url"/>
:referrerpolicy="referrerpolicy"
:mimetype="attachment.mimetype"
:src="attachment.large_thumb_url || attachment.url"
/>
</a> </a>
<a <a class="video-container"
@click="openModal"
v-if="type === 'video' && !hidden" v-if="type === 'video' && !hidden"
class="video-container"
:class="{'small': isSmall}" :class="{'small': isSmall}"
:href="allowPlay ? undefined : attachment.url" :href="allowPlay ? undefined : attachment.url"
@click="openModal"
> >
<VideoAttachment <VideoAttachment class="video" :attachment="attachment" :controls="allowPlay" />
class="video" <i v-if="!allowPlay" class="play-icon icon-play-circled"></i>
:attachment="attachment"
:controls="allowPlay"
/>
<i
v-if="!allowPlay"
class="play-icon icon-play-circled"
/>
</a> </a>
<audio <audio v-if="type === 'audio'" :src="attachment.url" controls></audio>
v-if="type === 'audio'"
:src="attachment.url"
controls
/>
<div <div @click.prevent="linkClicked" v-if="type === 'html' && attachment.oembed" class="oembed">
v-if="type === 'html' && attachment.oembed" <div v-if="attachment.thumb_url" class="image">
class="oembed" <img :src="attachment.thumb_url"/>
@click.prevent="linkClicked"
>
<div
v-if="attachment.thumb_url"
class="image"
>
<img :src="attachment.thumb_url">
</div> </div>
<div class="text"> <div class="text">
<h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1> <h1><a :href="attachment.url">{{attachment.oembed.title}}</a></h1>
<div v-html="attachment.oembed.oembedHTML" /> <div v-html="attachment.oembed.oembedHTML"></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,49 +1,22 @@
<template> <template>
<div class="basic-user-card"> <div class="basic-user-card">
<router-link :to="userProfileLink(user)"> <router-link :to="userProfileLink(user)">
<UserAvatar <UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
class="avatar"
:src="user.profile_image_url"
@click.prevent.native="toggleUserExpanded"
/>
</router-link> </router-link>
<div <div class="basic-user-card-expanded-content" v-if="userExpanded">
v-if="userExpanded" <UserCard :user="user" :rounded="true" :bordered="true"/>
class="basic-user-card-expanded-content"
>
<UserCard
:user="user"
:rounded="true"
:bordered="true"
/>
</div> </div>
<div <div class="basic-user-card-collapsed-content" v-else>
v-else <div :title="user.name" class="basic-user-card-user-name">
class="basic-user-card-collapsed-content" <span v-if="user.name_html" class="basic-user-card-user-name-value" v-html="user.name_html"></span>
> <span v-else class="basic-user-card-user-name-value">{{ user.name }}</span>
<div
:title="user.name"
class="basic-user-card-user-name"
>
<span
v-if="user.name_html"
class="basic-user-card-user-name-value"
v-html="user.name_html"
/>
<span
v-else
class="basic-user-card-user-name-value"
>{{ user.name }}</span>
</div> </div>
<div> <div>
<router-link <router-link class="basic-user-card-screen-name" :to="userProfileLink(user)">
class="basic-user-card-screen-name" @{{user.screen_name}}
:to="userProfileLink(user)"
>
@{{ user.screen_name }}
</router-link> </router-link>
</div> </div>
<slot /> <slot></slot>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,12 +1,7 @@
<template> <template>
<basic-user-card :user="user"> <basic-user-card :user="user">
<div class="block-card-content-container"> <div class="block-card-content-container">
<button <button class="btn btn-default" @click="unblockUser" :disabled="progress" v-if="blocked">
v-if="blocked"
class="btn btn-default"
:disabled="progress"
@click="unblockUser"
>
<template v-if="progress"> <template v-if="progress">
{{ $t('user_card.unblock_progress') }} {{ $t('user_card.unblock_progress') }}
</template> </template>
@ -14,12 +9,7 @@
{{ $t('user_card.unblock') }} {{ $t('user_card.unblock') }}
</template> </template>
</button> </button>
<button <button class="btn btn-default" @click="blockUser" :disabled="progress" v-else>
v-else
class="btn btn-default"
:disabled="progress"
@click="blockUser"
>
<template v-if="progress"> <template v-if="progress">
{{ $t('user_card.block_progress') }} {{ $t('user_card.block_progress') }}
</template> </template>

View File

@ -16,7 +16,7 @@ const chatPanel = {
}, },
methods: { methods: {
submit (message) { submit (message) {
this.$store.state.chat.channel.push('new_msg', { text: message }, 10000) this.$store.state.chat.channel.push('new_msg', {text: message}, 10000)
this.currentMessage = '' this.currentMessage = ''
}, },
togglePanel () { togglePanel () {

View File

@ -1,70 +1,41 @@
<template> <template>
<div <div class="chat-panel" v-if="!this.collapsed || !this.floating">
v-if="!this.collapsed || !this.floating"
class="chat-panel"
>
<div class="panel panel-default"> <div class="panel panel-default">
<div <div class="panel-heading timeline-heading" :class="{ 'chat-heading': floating }" @click.stop.prevent="togglePanel">
class="panel-heading timeline-heading"
:class="{ 'chat-heading': floating }"
@click.stop.prevent="togglePanel"
>
<div class="title"> <div class="title">
<span>{{ $t('chat.title') }}</span> <span>{{$t('chat.title')}}</span>
<i <i class="icon-cancel" v-if="floating"></i>
v-if="floating"
class="icon-cancel"
/>
</div> </div>
</div> </div>
<div <div class="chat-window" v-chat-scroll>
v-chat-scroll <div class="chat-message" v-for="message in messages" :key="message.id">
class="chat-window"
>
<div
v-for="message in messages"
:key="message.id"
class="chat-message"
>
<span class="chat-avatar"> <span class="chat-avatar">
<img :src="message.author.avatar"> <img :src="message.author.avatar" />
</span> </span>
<div class="chat-content"> <div class="chat-content">
<router-link <router-link
class="chat-name" class="chat-name"
:to="userProfileLink(message.author)" :to="userProfileLink(message.author)">
> {{message.author.username}}
{{ message.author.username }}
</router-link> </router-link>
<br> <br>
<span class="chat-text"> <span class="chat-text">
{{ message.text }} {{message.text}}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
<div class="chat-input"> <div class="chat-input">
<textarea <textarea @keyup.enter="submit(currentMessage)" v-model="currentMessage" class="chat-input-textarea" rows="1"></textarea>
v-model="currentMessage"
class="chat-input-textarea"
rows="1"
@keyup.enter="submit(currentMessage)"
/>
</div> </div>
</div> </div>
</div> </div>
<div <div v-else class="chat-panel">
v-else
class="chat-panel"
>
<div class="panel panel-default"> <div class="panel panel-default">
<div <div class="panel-heading stub timeline-heading chat-heading" @click.stop.prevent="togglePanel">
class="panel-heading stub timeline-heading chat-heading"
@click.stop.prevent="togglePanel"
>
<div class="title"> <div class="title">
<i class="icon-comment-empty" /> <i class="icon-comment-empty"></i>
{{ $t('chat.title') }} {{$t('chat.title')}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,44 +1,33 @@
<template> <template>
<div <div class="color-control style-control" :class="{ disabled: !present || disabled }">
class="color-control style-control" <label :for="name" class="label">
:class="{ disabled: !present || disabled }" {{label}}
> </label>
<label <input
:for="name" v-if="typeof fallback !== 'undefined'"
class="label" class="opt exlcude-disabled"
:id="name + '-o'"
type="checkbox"
:checked="present"
@input="$emit('input', typeof value === 'undefined' ? fallback : undefined)">
<label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label>
<input
:id="name"
class="color-input"
type="color"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('input', $event.target.value)"
> >
{{ label }} <input
</label> :id="name + '-t'"
<input class="text-input"
v-if="typeof fallback !== 'undefined'" type="text"
:id="name + '-o'" :value="value || fallback"
class="opt exlcude-disabled" :disabled="!present || disabled"
type="checkbox" @input="$emit('input', $event.target.value)"
:checked="present"
@input="$emit('input', typeof value === 'undefined' ? fallback : undefined)"
> >
<label </div>
v-if="typeof fallback !== 'undefined'"
class="opt-l"
:for="name + '-o'"
/>
<input
:id="name"
class="color-input"
type="color"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('input', $event.target.value)"
>
<input
:id="name + '-t'"
class="text-input"
type="text"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('input', $event.target.value)"
>
</div>
</template> </template>
<script> <script>

View File

@ -1,38 +1,28 @@
<template> <template>
<span <span v-if="contrast" class="contrast-ratio">
v-if="contrast" <span :title="hint" class="rating">
class="contrast-ratio" <span v-if="contrast.aaa">
> <i class="icon-thumbs-up-alt"/>
<span
:title="hint"
class="rating"
>
<span v-if="contrast.aaa">
<i class="icon-thumbs-up-alt" />
</span>
<span v-if="!contrast.aaa && contrast.aa">
<i class="icon-adjust" />
</span>
<span v-if="!contrast.aaa && !contrast.aa">
<i class="icon-attention" />
</span>
</span> </span>
<span <span v-if="!contrast.aaa && contrast.aa">
v-if="contrast && large" <i class="icon-adjust"/>
class="rating" </span>
:title="hint_18pt" <span v-if="!contrast.aaa && !contrast.aa">
> <i class="icon-attention"/>
<span v-if="contrast.laaa">
<i class="icon-thumbs-up-alt" />
</span>
<span v-if="!contrast.laaa && contrast.laa">
<i class="icon-adjust" />
</span>
<span v-if="!contrast.laaa && !contrast.laa">
<i class="icon-attention" />
</span>
</span> </span>
</span> </span>
<span class="rating" v-if="contrast && large" :title="hint_18pt">
<span v-if="contrast.laaa">
<i class="icon-thumbs-up-alt"/>
</span>
<span v-if="!contrast.laaa && contrast.laa">
<i class="icon-adjust"/>
</span>
<span v-if="!contrast.laaa && !contrast.laa">
<i class="icon-attention"/>
</span>
</span>
</span>
</template> </template>
<script> <script>

View File

@ -1,9 +1,9 @@
<template> <template>
<conversation <conversation
:collapsable="false" :collapsable="false"
is-page="true" isPage="true"
:statusoid="statusoid" :statusoid="statusoid"
/> ></conversation>
</template> </template>
<script src="./conversation-page.js"></script> <script src="./conversation-page.js"></script>

View File

@ -94,8 +94,8 @@ const conversation = {
}, },
replies () { replies () {
let i = 1 let i = 1
/* eslint-disable camelcase */ return reduce(this.conversation, (result, {id, in_reply_to_status_id}) => {
return reduce(this.conversation, (result, { id, in_reply_to_status_id }) => { /* eslint-disable camelcase */
const irid = in_reply_to_status_id const irid = in_reply_to_status_id
/* eslint-enable camelcase */ /* eslint-enable camelcase */
if (irid) { if (irid) {
@ -127,8 +127,8 @@ const conversation = {
methods: { methods: {
fetchConversation () { fetchConversation () {
if (this.status) { if (this.status) {
this.$store.state.api.backendInteractor.fetchConversation({ id: this.status.id }) this.$store.state.api.backendInteractor.fetchConversation({id: this.status.id})
.then(({ ancestors, descendants }) => { .then(({ancestors, descendants}) => {
this.$store.dispatch('addNewStatuses', { statuses: ancestors }) this.$store.dispatch('addNewStatuses', { statuses: ancestors })
this.$store.dispatch('addNewStatuses', { statuses: descendants }) this.$store.dispatch('addNewStatuses', { statuses: descendants })
set(this, 'converationStatusIds', [].concat( set(this, 'converationStatusIds', [].concat(
@ -139,7 +139,7 @@ const conversation = {
.then(() => this.setHighlight(this.statusId)) .then(() => this.setHighlight(this.statusId))
} else { } else {
const id = this.$route.params.id const id = this.$route.params.id
this.$store.state.api.backendInteractor.fetchStatus({ id }) this.$store.state.api.backendInteractor.fetchStatus({id})
.then((status) => this.$store.dispatch('addNewStatuses', { statuses: [status] })) .then((status) => this.$store.dispatch('addNewStatuses', { statuses: [status] }))
.then(() => this.fetchConversation()) .then(() => this.fetchConversation())
} }

View File

@ -1,33 +1,24 @@
<template> <template>
<div <div class="timeline panel-default" :class="[isExpanded ? 'panel' : 'panel-disabled']">
class="timeline panel-default" <div v-if="isExpanded" class="panel-heading conversation-heading">
:class="[isExpanded ? 'panel' : 'panel-disabled']"
>
<div
v-if="isExpanded"
class="panel-heading conversation-heading"
>
<span class="title"> {{ $t('timeline.conversation') }} </span> <span class="title"> {{ $t('timeline.conversation') }} </span>
<span v-if="collapsable"> <span v-if="collapsable">
<a <a href="#" @click.prevent="toggleExpanded">{{ $t('timeline.collapse') }}</a>
href="#"
@click.prevent="toggleExpanded"
>{{ $t('timeline.collapse') }}</a>
</span> </span>
</div> </div>
<status <status
v-for="status in conversation" v-for="status in conversation"
@goto="setHighlight"
@toggleExpanded="toggleExpanded"
:key="status.id" :key="status.id"
:inline-expanded="collapsable" :inlineExpanded="collapsable"
:statusoid="status" :statusoid="status"
:expandable="!expanded" :expandable='!expanded'
:focused="focused(status.id)" :focused="focused(status.id)"
:in-conversation="isExpanded" :inConversation="isExpanded"
:highlight="getHighlight()" :highlight="getHighlight()"
:replies="getReplies(status.id)" :replies="getReplies(status.id)"
class="status-fadein panel-body" class="status-fadein panel-body"
@goto="setHighlight"
@toggleExpanded="toggleExpanded"
/> />
</div> </div>
</template> </template>

View File

@ -10,7 +10,7 @@ const DeleteButton = {
}, },
computed: { computed: {
currentUser () { return this.$store.state.users.currentUser }, currentUser () { return this.$store.state.users.currentUser },
canDelete () { return (this.currentUser && this.currentUser.rights.delete_others_notice) || this.status.user.id === this.currentUser.id } canDelete () { return this.currentUser && this.currentUser.rights.delete_others_notice || this.status.user.id === this.currentUser.id }
} }
} }

View File

@ -1,10 +1,7 @@
<template> <template>
<div v-if="canDelete"> <div v-if="canDelete">
<a <a href="#" v-on:click.prevent="deleteStatus()">
href="#" <i class='button-icon icon-cancel delete-status'></i>
@click.prevent="deleteStatus()"
>
<i class="button-icon icon-cancel delete-status" />
</a> </a>
</div> </div>
</template> </template>

View File

@ -1,9 +1,5 @@
<template> <template>
<Timeline <Timeline :title="$t('nav.dms')" v-bind:timeline="timeline" v-bind:timeline-name="'dms'"/>
:title="$t('nav.dms')"
:timeline="timeline"
:timeline-name="'dms'"
/>
</template> </template>
<script src="./dm_timeline.js"></script> <script src="./dm_timeline.js"></script>

View File

@ -23,8 +23,7 @@ const EmojiInput = {
if (matchedEmoji.length <= 0) { if (matchedEmoji.length <= 0) {
return false return false
} }
// eslint-disable-next-line camelcase return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
return map(take(matchedEmoji, 5), ({ shortcode, image_url, utf }, index) => ({
shortcode: `:${shortcode}:`, shortcode: `:${shortcode}:`,
utf: utf || '', utf: utf || '',
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
@ -99,7 +98,7 @@ const EmojiInput = {
onInput (e) { onInput (e) {
this.$emit('input', e.target.value) this.$emit('input', e.target.value)
}, },
setCaret ({ target: { selectionStart } }) { setCaret ({target: {selectionStart}}) {
this.caret = selectionStart this.caret = selectionStart
} }
} }

View File

@ -9,13 +9,13 @@
@input="onInput" @input="onInput"
@click="setCaret" @click="setCaret"
@keyup="setCaret" @keyup="setCaret"
@keydown.exact="onKeydown" @keydown="onKeydown"
@keydown.down.exact="cycleForward" @keydown.down="cycleForward"
@keydown.up.exact="cycleBackward" @keydown.up="cycleBackward"
@keydown.shift.tab.exact="cycleBackward" @keydown.shift.tab="cycleBackward"
@keydown.tab.exact="cycleForward" @keydown.tab="cycleForward"
@keydown.enter.exact="replaceEmoji" @keydown.enter="replaceEmoji"
> />
<textarea <textarea
v-else v-else
:class="classname" :class="classname"
@ -24,30 +24,27 @@
@input="onInput" @input="onInput"
@click="setCaret" @click="setCaret"
@keyup="setCaret" @keyup="setCaret"
@keydown.exact="onKeydown" @keydown="onKeydown"
@keydown.down.exact="cycleForward" @keydown.down="cycleForward"
@keydown.up.exact="cycleBackward" @keydown.up="cycleBackward"
@keydown.shift.tab.exact="cycleBackward" @keydown.shift.tab="cycleBackward"
@keydown.tab.exact="cycleForward" @keydown.tab="cycleForward"
@keydown.enter.exact="replaceEmoji" @keydown.enter="replaceEmoji"
/> ></textarea>
<div <div class="autocomplete-panel" v-if="suggestions">
v-if="suggestions"
class="autocomplete-panel"
>
<div class="autocomplete-panel-body"> <div class="autocomplete-panel-body">
<div <div
v-for="(emoji, index) in suggestions" v-for="(emoji, index) in suggestions"
:key="index" :key="index"
@click="replace(emoji.utf || (emoji.shortcode + ' '))"
class="autocomplete-item" class="autocomplete-item"
:class="{ highlighted: emoji.highlighted }" :class="{ highlighted: emoji.highlighted }"
@click="replace(emoji.utf || (emoji.shortcode + ' '))"
> >
<span v-if="emoji.img"> <span v-if="emoji.img">
<img :src="emoji.img"> <img :src="emoji.img" />
</span> </span>
<span v-else>{{ emoji.utf }}</span> <span v-else>{{emoji.utf}}</span>
<span>{{ emoji.shortcode }}</span> <span>{{emoji.shortcode}}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,27 +1,12 @@
<template> <template>
<div class="import-export-container"> <div class="import-export-container">
<slot name="before" /> <slot name="before"/>
<button <button class="btn" @click="exportData">{{ exportLabel }}</button>
class="btn" <button class="btn" @click="importData">{{ importLabel }}</button>
@click="exportData" <slot name="afterButtons"/>
> <p v-if="importFailed" class="alert error">{{ importFailedText }}</p>
{{ exportLabel }} <slot name="afterError"/>
</button> </div>
<button
class="btn"
@click="importData"
>
{{ importLabel }}
</button>
<slot name="afterButtons" />
<p
v-if="importFailed"
class="alert error"
>
{{ importFailedText }}
</p>
<slot name="afterError" />
</div>
</template> </template>
<script> <script>
@ -64,7 +49,7 @@ export default {
if (event.target.files[0]) { if (event.target.files[0]) {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const reader = new FileReader() const reader = new FileReader()
reader.onload = ({ target }) => { reader.onload = ({target}) => {
try { try {
const parsed = JSON.parse(target.result) const parsed = JSON.parse(target.result)
const valid = this.validator(parsed) const valid = this.validator(parsed)

View File

@ -11,9 +11,9 @@ const FavoriteButton = {
methods: { methods: {
favorite () { favorite () {
if (!this.status.favorited) { if (!this.status.favorited) {
this.$store.dispatch('favorite', { id: this.status.id }) this.$store.dispatch('favorite', {id: this.status.id})
} else { } else {
this.$store.dispatch('unfavorite', { id: this.status.id }) this.$store.dispatch('unfavorite', {id: this.status.id})
} }
this.animated = true this.animated = true
setTimeout(() => { setTimeout(() => {

View File

@ -1,20 +1,11 @@
<template> <template>
<div v-if="loggedIn"> <div v-if="loggedIn">
<i <i :class='classes' class='button-icon favorite-button fav-active' @click.prevent='favorite()' :title="$t('tool_tip.favorite')"/>
:class="classes" <span v-if='!hidePostStatsLocal && status.fave_num > 0'>{{status.fave_num}}</span>
class="button-icon favorite-button fav-active"
:title="$t('tool_tip.favorite')"
@click.prevent="favorite()"
/>
<span v-if="!hidePostStatsLocal && status.fave_num > 0">{{ status.fave_num }}</span>
</div> </div>
<div v-else> <div v-else>
<i <i :class='classes' class='button-icon favorite-button' :title="$t('tool_tip.favorite')"/>
:class="classes" <span v-if='!hidePostStatsLocal && status.fave_num > 0'>{{status.fave_num}}</span>
class="button-icon favorite-button"
:title="$t('tool_tip.favorite')"
/>
<span v-if="!hidePostStatsLocal && status.fave_num > 0">{{ status.fave_num }}</span>
</div> </div>
</template> </template>

View File

@ -3,25 +3,17 @@
<div class="panel panel-default base01-background"> <div class="panel panel-default base01-background">
<div class="panel-heading timeline-heading base02-background base04"> <div class="panel-heading timeline-heading base02-background base04">
<div class="title"> <div class="title">
{{ $t('features_panel.title') }} {{$t('features_panel.title')}}
</div> </div>
</div> </div>
<div class="panel-body features-panel"> <div class="panel-body features-panel">
<ul> <ul>
<li v-if="chat"> <li v-if="chat">{{$t('features_panel.chat')}}</li>
{{ $t('features_panel.chat') }} <li v-if="gopher">{{$t('features_panel.gopher')}}</li>
</li> <li v-if="whoToFollow">{{$t('features_panel.who_to_follow')}}</li>
<li v-if="gopher"> <li v-if="mediaProxy">{{$t('features_panel.media_proxy')}}</li>
{{ $t('features_panel.gopher') }} <li>{{$t('features_panel.scope_options')}}</li>
</li> <li>{{$t('features_panel.text_limit')}} = {{textlimit}}</li>
<li v-if="whoToFollow">
{{ $t('features_panel.who_to_follow') }}
</li>
<li v-if="mediaProxy">
{{ $t('features_panel.media_proxy') }}
</li>
<li>{{ $t('features_panel.scope_options') }}</li>
<li>{{ $t('features_panel.text_limit') }} = {{ textlimit }}</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -22,7 +22,7 @@ const FollowCard = {
isMe () { return this.$store.state.users.currentUser.id === this.user.id }, isMe () { return this.$store.state.users.currentUser.id === this.user.id },
following () { return this.updated ? this.updated.following : this.user.following }, following () { return this.updated ? this.updated.following : this.user.following },
showFollow () { showFollow () {
return !this.following || (this.updated && !this.updated.following) return !this.following || this.updated && !this.updated.following
}, },
loggedIn () { loggedIn () {
return this.$store.state.users.currentUser return this.$store.state.users.currentUser

View File

@ -1,24 +1,18 @@
<template> <template>
<basic-user-card :user="user"> <basic-user-card :user="user">
<div class="follow-card-content-container"> <div class="follow-card-content-container">
<span <span class="faint" v-if="!noFollowsYou && user.follows_you">
v-if="!noFollowsYou && user.follows_you"
class="faint"
>
{{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }} {{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }}
</span> </span>
<div <div class="follow-card-follow-button" v-if="showFollow && !loggedIn">
v-if="showFollow && !loggedIn"
class="follow-card-follow-button"
>
<RemoteFollow :user="user" /> <RemoteFollow :user="user" />
</div> </div>
<button <button
v-if="showFollow && loggedIn" v-if="showFollow && loggedIn"
class="btn btn-default follow-card-follow-button" class="btn btn-default follow-card-follow-button"
@click="followUser"
:disabled="inProgress" :disabled="inProgress"
:title="requestSent ? $t('user_card.follow_again') : ''" :title="requestSent ? $t('user_card.follow_again') : ''"
@click="followUser"
> >
<template v-if="inProgress"> <template v-if="inProgress">
{{ $t('user_card.follow_progress') }} {{ $t('user_card.follow_progress') }}
@ -30,12 +24,7 @@
{{ $t('user_card.follow') }} {{ $t('user_card.follow') }}
</template> </template>
</button> </button>
<button <button v-if="following" class="btn btn-default follow-card-follow-button pressed" @click="unfollowUser" :disabled="inProgress">
v-if="following"
class="btn btn-default follow-card-follow-button pressed"
:disabled="inProgress"
@click="unfollowUser"
>
<template v-if="inProgress"> <template v-if="inProgress">
{{ $t('user_card.follow_progress') }} {{ $t('user_card.follow_progress') }}
</template> </template>

View File

@ -1,18 +1,8 @@
<template> <template>
<basic-user-card :user="user"> <basic-user-card :user="user">
<div class="follow-request-card-content-container"> <div class="follow-request-card-content-container">
<button <button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button>
class="btn btn-default" <button class="btn btn-default" @click="denyUser">{{ $t('user_card.deny') }}</button>
@click="approveUser"
>
{{ $t('user_card.approve') }}
</button>
<button
class="btn btn-default"
@click="denyUser"
>
{{ $t('user_card.deny') }}
</button>
</div> </div>
</basic-user-card> </basic-user-card>
</template> </template>

View File

@ -1,14 +1,10 @@
<template> <template>
<div class="settings panel panel-default"> <div class="settings panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{{ $t('nav.friend_requests') }} {{$t('nav.friend_requests')}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<FollowRequestCard <FollowRequestCard v-for="request in requests" :key="request.id" :user="request"/>
v-for="request in requests"
:key="request.id"
:user="request"
/>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,56 +1,35 @@
<template> <template>
<div <div class="font-control style-control" :class="{ custom: isCustom }">
class="font-control style-control" <label :for="preset === 'custom' ? name : name + '-font-switcher'" class="label">
:class="{ custom: isCustom }" {{label}}
> </label>
<label <input
:for="preset === 'custom' ? name : name + '-font-switcher'" v-if="typeof fallback !== 'undefined'"
class="label" class="opt exlcude-disabled"
> type="checkbox"
{{ label }} :id="name + '-o'"
</label> :checked="present"
<input @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)">
v-if="typeof fallback !== 'undefined'" <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label>
:id="name + '-o'" <label :for="name + '-font-switcher'" class="select" :disabled="!present">
class="opt exlcude-disabled" <select
type="checkbox"
:checked="present"
@input="$emit('input', typeof value === 'undefined' ? fallback : undefined)"
>
<label
v-if="typeof fallback !== 'undefined'"
class="opt-l"
:for="name + '-o'"
/>
<label
:for="name + '-font-switcher'"
class="select"
:disabled="!present" :disabled="!present"
> v-model="preset"
<select class="font-switcher"
:id="name + '-font-switcher'" :id="name + '-font-switcher'">
v-model="preset" <option v-for="option in availableOptions" :value="option">
:disabled="!present" {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }}
class="font-switcher" </option>
> </select>
<option <i class="icon-down-open"/>
v-for="option in availableOptions" </label>
:key="option" <input
:value="option" v-if="isCustom"
> class="custom-font"
{{ option === 'custom' ? $t('settings.style.fonts.custom') : option }} type="text"
</option> :id="name"
</select> v-model="family">
<i class="icon-down-open" /> </div>
</label>
<input
v-if="isCustom"
:id="name"
v-model="family"
class="custom-font"
type="text"
>
</div>
</template> </template>
<script src="./font_control.js" ></script> <script src="./font_control.js" ></script>

View File

@ -1,9 +1,5 @@
<template> <template>
<Timeline <Timeline :title="$t('nav.timeline')" v-bind:timeline="timeline" v-bind:timeline-name="'friends'"/>
:title="$t('nav.timeline')"
:timeline="timeline"
:timeline-name="'friends'"
/>
</template> </template>
<script src="./friends_timeline.js"></script> <script src="./friends_timeline.js"></script>

View File

@ -1,22 +1,13 @@
<template> <template>
<div <div ref="galleryContainer" style="width: 100%;">
ref="galleryContainer" <div class="gallery-row" v-for="row in rows" :style="rowHeight(row.length)" :class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }">
style="width: 100%;"
>
<div
v-for="row in rows"
:key="row"
class="gallery-row"
:style="rowHeight(row.length)"
:class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"
>
<attachment <attachment
v-for="attachment in row" v-for="attachment in row"
:key="attachment.id" :setMedia="setMedia"
:set-media="setMedia"
:nsfw="nsfw" :nsfw="nsfw"
:attachment="attachment" :attachment="attachment"
:allow-play="false" :allowPlay="false"
:key="attachment.id"
/> />
</div> </div>
</div> </div>

View File

@ -2,57 +2,20 @@
<div class="image-cropper"> <div class="image-cropper">
<div v-if="dataUrl"> <div v-if="dataUrl">
<div class="image-cropper-image-container"> <div class="image-cropper-image-container">
<img <img ref="img" :src="dataUrl" alt="" @load.stop="createCropper" />
ref="img"
:src="dataUrl"
alt=""
@load.stop="createCropper"
>
</div> </div>
<div class="image-cropper-buttons-wrapper"> <div class="image-cropper-buttons-wrapper">
<button <button class="btn" type="button" :disabled="submitting" @click="submit" v-text="saveText"></button>
class="btn" <button class="btn" type="button" :disabled="submitting" @click="destroy" v-text="cancelText"></button>
type="button" <button class="btn" type="button" :disabled="submitting" @click="submitWithoutCropping" v-text="saveWithoutCroppingText"></button>
:disabled="submitting" <i class="icon-spin4 animate-spin" v-if="submitting"></i>
@click="submit"
v-text="saveText"
/>
<button
class="btn"
type="button"
:disabled="submitting"
@click="destroy"
v-text="cancelText"
/>
<button
class="btn"
type="button"
:disabled="submitting"
@click="submitWithoutCropping"
v-text="saveWithoutCroppingText"
/>
<i
v-if="submitting"
class="icon-spin4 animate-spin"
/>
</div> </div>
<div <div class="alert error" v-if="submitError">
v-if="submitError" {{submitErrorMsg}}
class="alert error" <i class="button-icon icon-cancel" @click="clearError"></i>
>
{{ submitErrorMsg }}
<i
class="button-icon icon-cancel"
@click="clearError"
/>
</div> </div>
</div> </div>
<input <input ref="input" type="file" class="image-cropper-img-input" :accept="mimes">
ref="input"
type="file"
class="image-cropper-img-input"
:accept="mimes"
>
</div> </div>
</template> </template>

View File

@ -1,11 +1,9 @@
<template> <template>
<div <div v-if="show" class="instance-specific-panel">
v-if="show"
class="instance-specific-panel"
>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<div v-html="instanceSpecificPanelContent" /> <div v-html="instanceSpecificPanelContent">
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,49 +3,39 @@
<label for="interface-language-switcher"> <label for="interface-language-switcher">
{{ $t('settings.interfaceLanguage') }} {{ $t('settings.interfaceLanguage') }}
</label> </label>
<label <label for="interface-language-switcher" class='select'>
for="interface-language-switcher" <select id="interface-language-switcher" v-model="language">
class="select" <option v-for="(langCode, i) in languageCodes" :value="langCode">
>
<select
id="interface-language-switcher"
v-model="language"
>
<option
v-for="(langCode, i) in languageCodes"
:key="langCode"
:value="langCode"
>
{{ languageNames[i] }} {{ languageNames[i] }}
</option> </option>
</select> </select>
<i class="icon-down-open" /> <i class="icon-down-open"/>
</label> </label>
</div> </div>
</template> </template>
<script> <script>
import languagesObject from '../../i18n/messages' import languagesObject from '../../i18n/messages'
import ISO6391 from 'iso-639-1' import ISO6391 from 'iso-639-1'
import _ from 'lodash' import _ from 'lodash'
export default { export default {
computed: { computed: {
languageCodes () { languageCodes () {
return Object.keys(languagesObject) return Object.keys(languagesObject)
}, },
languageNames () { languageNames () {
return _.map(this.languageCodes, ISO6391.getName) return _.map(this.languageCodes, ISO6391.getName)
}, },
language: { language: {
get: function () { return this.$store.state.config.interfaceLanguage }, get: function () { return this.$store.state.config.interfaceLanguage },
set: function (val) { set: function (val) {
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
this.$i18n.locale = val this.$i18n.locale = val
}
} }
} }
} }
}
</script> </script>

View File

@ -1,25 +1,13 @@
<template> <template>
<div> <div>
<a <a class="link-preview-card" :href="card.url" target="_blank" rel="noopener">
class="link-preview-card" <div class="card-image" :class="{ 'small-image': size === 'small' }" v-if="useImage">
:href="card.url" <img :src="card.image"></img>
target="_blank"
rel="noopener"
>
<div
v-if="useImage"
class="card-image"
:class="{ 'small-image': size === 'small' }"
>
<img :src="card.image">
</div> </div>
<div class="card-content"> <div class="card-content">
<span class="card-host faint">{{ card.provider_name }}</span> <span class="card-host faint">{{ card.provider_name }}</span>
<h4 class="card-title">{{ card.title }}</h4> <h4 class="card-title">{{ card.title }}</h4>
<p <p class="card-description" v-if="useDescription">{{ card.description }}</p>
v-if="useDescription"
class="card-description"
>{{ card.description }}</p>
</div> </div>
</a> </a>
</div> </div>

View File

@ -39,7 +39,7 @@ const LoginForm = {
} }
this.$store.commit('setToken', result.access_token) this.$store.commit('setToken', result.access_token)
this.$store.dispatch('loginUser', result.access_token) this.$store.dispatch('loginUser', result.access_token)
this.$router.push({ name: 'friends' }) this.$router.push({name: 'friends'})
}) })
}) })
}, },

View File

@ -2,96 +2,42 @@
<div class="login panel panel-default"> <div class="login panel panel-default">
<!-- Default panel contents --> <!-- Default panel contents -->
<div class="panel-heading"> <div class="panel-heading">
{{ $t('login.login') }} {{$t('login.login')}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<form <form v-if="loginMethod == 'password'" v-on:submit.prevent='submit(user)' class='login-form'>
v-if="loginMethod == 'password'" <div class='form-group'>
class="login-form" <label for='username'>{{$t('login.username')}}</label>
@submit.prevent="submit(user)" <input :disabled="loggingIn" v-model='user.username' class='form-control' id='username' v-bind:placeholder="$t('login.placeholder')">
>
<div class="form-group">
<label for="username">{{ $t('login.username') }}</label>
<input
id="username"
v-model="user.username"
:disabled="loggingIn"
class="form-control"
:placeholder="$t('login.placeholder')"
>
</div> </div>
<div class="form-group"> <div class='form-group'>
<label for="password">{{ $t('login.password') }}</label> <label for='password'>{{$t('login.password')}}</label>
<input <input :disabled="loggingIn" v-model='user.password' class='form-control' id='password' type='password'>
id="password"
v-model="user.password"
:disabled="loggingIn"
class="form-control"
type="password"
>
</div> </div>
<div class="form-group"> <div class='form-group'>
<div class="login-bottom"> <div class='login-bottom'>
<div> <div><router-link :to="{name: 'registration'}" v-if='registrationOpen' class='register'>{{$t('login.register')}}</router-link></div>
<router-link <button :disabled="loggingIn" type='submit' class='btn btn-default'>{{$t('login.login')}}</button>
v-if="registrationOpen"
:to="{name: 'registration'}"
class="register"
>
{{ $t('login.register') }}
</router-link>
</div>
<button
:disabled="loggingIn"
type="submit"
class="btn btn-default"
>
{{ $t('login.login') }}
</button>
</div> </div>
</div> </div>
</form> </form>
<form <form v-if="loginMethod == 'token'" v-on:submit.prevent='oAuthLogin' class="login-form">
v-if="loginMethod == 'token'"
class="login-form"
@submit.prevent="oAuthLogin"
>
<div class="form-group"> <div class="form-group">
<p>{{ $t('login.description') }}</p> <p>{{$t('login.description')}}</p>
</div> </div>
<div class="form-group"> <div class='form-group'>
<div class="login-bottom"> <div class='login-bottom'>
<div> <div><router-link :to="{name: 'registration'}" v-if='registrationOpen' class='register'>{{$t('login.register')}}</router-link></div>
<router-link <button :disabled="loggingIn" type='submit' class='btn btn-default'>{{$t('login.login')}}</button>
v-if="registrationOpen"
:to="{name: 'registration'}"
class="register"
>
{{ $t('login.register') }}
</router-link>
</div>
<button
:disabled="loggingIn"
type="submit"
class="btn btn-default"
>
{{ $t('login.login') }}
</button>
</div> </div>
</div> </div>
</form> </form>
<div <div v-if="authError" class='form-group'>
v-if="authError" <div class='alert error'>
class="form-group" {{authError}}
> <i class="button-icon icon-cancel" @click="clearError"></i>
<div class="alert error">
{{ authError }}
<i
class="button-icon icon-cancel"
@click="clearError"
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,33 +1,25 @@
<template> <template>
<div <div class="modal-view media-modal-view" v-if="showing" @click.prevent="hide">
v-if="showing" <img class="modal-image" v-if="type === 'image'" :src="currentMedia.url"></img>
class="modal-view media-modal-view"
@click.prevent="hide"
>
<img
v-if="type === 'image'"
class="modal-image"
:src="currentMedia.url"
>
<VideoAttachment <VideoAttachment
v-if="type === 'video'"
class="modal-image" class="modal-image"
v-if="type === 'video'"
:attachment="currentMedia" :attachment="currentMedia"
:controls="true" :controls="true"
@click.stop.native="" @click.stop.native="">
/> </VideoAttachment>
<button <button
v-if="canNavigate"
:title="$t('media_modal.previous')" :title="$t('media_modal.previous')"
class="modal-view-button-arrow modal-view-button-arrow--prev" class="modal-view-button-arrow modal-view-button-arrow--prev"
v-if="canNavigate"
@click.stop.prevent="goPrev" @click.stop.prevent="goPrev"
> >
<i class="icon-left-open arrow-icon" /> <i class="icon-left-open arrow-icon" />
</button> </button>
<button <button
v-if="canNavigate"
:title="$t('media_modal.next')" :title="$t('media_modal.next')"
class="modal-view-button-arrow modal-view-button-arrow--next" class="modal-view-button-arrow modal-view-button-arrow--next"
v-if="canNavigate"
@click.stop.prevent="goNext" @click.stop.prevent="goNext"
> >
<i class="icon-right-open arrow-icon" /> <i class="icon-right-open arrow-icon" />

View File

@ -16,7 +16,7 @@ const mediaUpload = {
if (file.size > store.state.instance.uploadlimit) { if (file.size > store.state.instance.uploadlimit) {
const filesize = fileSizeFormatService.fileSizeFormat(file.size) const filesize = fileSizeFormatService.fileSizeFormat(file.size)
const allowedsize = fileSizeFormatService.fileSizeFormat(store.state.instance.uploadlimit) const allowedsize = fileSizeFormatService.fileSizeFormat(store.state.instance.uploadlimit)
self.$emit('upload-failed', 'file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit }) self.$emit('upload-failed', 'file_too_big', {filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit})
return return
} }
const formData = new FormData() const formData = new FormData()
@ -36,7 +36,7 @@ const mediaUpload = {
}, },
fileDrop (e) { fileDrop (e) {
if (e.dataTransfer.files.length > 0) { if (e.dataTransfer.files.length > 0) {
e.preventDefault() // allow dropping text like before e.preventDefault() // allow dropping text like before
this.uploadFile(e.dataTransfer.files[0]) this.uploadFile(e.dataTransfer.files[0])
} }
}, },
@ -54,7 +54,7 @@ const mediaUpload = {
this.uploadReady = true this.uploadReady = true
}) })
}, },
change ({ target }) { change ({target}) {
for (var i = 0; i < target.files.length; i++) { for (var i = 0; i < target.files.length; i++) {
let file = target.files[i] let file = target.files[i]
this.uploadFile(file) this.uploadFile(file)

View File

@ -1,29 +1,9 @@
<template> <template>
<div <div class="media-upload" @drop.prevent @dragover.prevent="fileDrag" @drop="fileDrop">
class="media-upload" <label class="btn btn-default" :title="$t('tool_tip.media_upload')">
@drop.prevent <i class="icon-spin4 animate-spin" v-if="uploading"></i>
@dragover.prevent="fileDrag" <i class="icon-upload" v-if="!uploading"></i>
@drop="fileDrop" <input type="file" v-if="uploadReady" @change="change" style="position: fixed; top: -100em" multiple="true"></input>
>
<label
class="btn btn-default"
:title="$t('tool_tip.media_upload')"
>
<i
v-if="uploading"
class="icon-spin4 animate-spin"
/>
<i
v-if="!uploading"
class="icon-upload"
/>
<input
v-if="uploadReady"
type="file"
style="position: fixed; top: -100em"
multiple="true"
@change="change"
>
</label> </label>
</div> </div>
</template> </template>

View File

@ -1,9 +1,5 @@
<template> <template>
<Timeline <Timeline :title="$t('nav.mentions')" v-bind:timeline="timeline" v-bind:timeline-name="'mentions'"/>
:title="$t('nav.mentions')"
:timeline="timeline"
:timeline-name="'mentions'"
/>
</template> </template>
<script src="./mentions.js"></script> <script src="./mentions.js"></script>

View File

@ -1,71 +1,34 @@
<template> <template>
<nav <nav class='nav-bar container' id="nav">
id="nav" <div class='mobile-inner-nav' @click="scrollToTop()">
class="nav-bar container" <div class='item'>
> <a href="#" class="mobile-nav-button" @click.stop.prevent="toggleMobileSidebar()">
<div <i class="button-icon icon-menu"></i>
class="mobile-inner-nav"
@click="scrollToTop()"
>
<div class="item">
<a
href="#"
class="mobile-nav-button"
@click.stop.prevent="toggleMobileSidebar()"
>
<i class="button-icon icon-menu" />
</a> </a>
<router-link <router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link>
class="site-name"
:to="{ name: 'root' }"
active-class="home"
>
{{ sitename }}
</router-link>
</div> </div>
<div class="item right"> <div class='item right'>
<a <a class="mobile-nav-button" v-if="currentUser" href="#" @click.stop.prevent="openMobileNotifications()">
v-if="currentUser" <i class="button-icon icon-bell-alt"></i>
class="mobile-nav-button" <div class="alert-dot" v-if="unseenNotificationsCount"></div>
href="#"
@click.stop.prevent="openMobileNotifications()"
>
<i class="button-icon icon-bell-alt" />
<div
v-if="unseenNotificationsCount"
class="alert-dot"
/>
</a> </a>
</div> </div>
</div> </div>
<SideDrawer <SideDrawer ref="sideDrawer" :logout="logout"/>
ref="sideDrawer" <div v-if="currentUser"
:logout="logout"
/>
<div
v-if="currentUser"
class="mobile-notifications-drawer" class="mobile-notifications-drawer"
:class="{ 'closed': !notificationsOpen }" :class="{ 'closed': !notificationsOpen }"
@touchstart="notificationsTouchStart" @touchstart="notificationsTouchStart"
@touchmove="notificationsTouchMove" @touchmove="notificationsTouchMove"
> >
<div class="mobile-notifications-header"> <div class="mobile-notifications-header">
<span class="title">{{ $t('notifications.notifications') }}</span> <span class="title">{{$t('notifications.notifications')}}</span>
<a <a class="mobile-nav-button" @click.stop.prevent="closeMobileNotifications()">
class="mobile-nav-button" <i class="button-icon icon-cancel"/>
@click.stop.prevent="closeMobileNotifications()"
>
<i class="button-icon icon-cancel" />
</a> </a>
</div> </div>
<div <div v-if="currentUser" class="mobile-notifications">
v-if="currentUser" <Notifications ref="notifications" noHeading="true"/>
class="mobile-notifications"
>
<Notifications
ref="notifications"
no-heading="true"
/>
</div> </div>
</div> </div>
<MobilePostStatusModal /> <MobilePostStatusModal />

View File

@ -1,31 +1,23 @@
<template> <template>
<div v-if="currentUser"> <div v-if="currentUser">
<div <div
v-show="postFormOpen" class="post-form-modal-view modal-view"
class="post-form-modal-view modal-view" v-show="postFormOpen"
@click="closePostForm" @click="closePostForm"
> >
<div <div class="post-form-modal-panel panel" @click.stop="">
class="post-form-modal-panel panel" <div class="panel-heading">{{$t('post_status.new_status')}}</div>
@click.stop="" <PostStatusForm class="panel-body" @posted="closePostForm"/>
>
<div class="panel-heading">
{{ $t('post_status.new_status') }}
</div>
<PostStatusForm
class="panel-body"
@posted="closePostForm"
/>
</div>
</div> </div>
<button
class="new-status-button"
:class="{ 'hidden': isHidden }"
@click="openPostForm"
>
<i class="icon-edit" />
</button>
</div> </div>
<button
class="new-status-button"
:class="{ 'hidden': isHidden }"
@click="openPostForm"
>
<i class="icon-edit" />
</button>
</div>
</template> </template>
<script src="./mobile_post_status_modal.js"></script> <script src="./mobile_post_status_modal.js"></script>

View File

@ -1,12 +1,7 @@
<template> <template>
<basic-user-card :user="user"> <basic-user-card :user="user">
<div class="mute-card-content-container"> <div class="mute-card-content-container">
<button <button class="btn btn-default" @click="unmuteUser" :disabled="progress" v-if="muted">
v-if="muted"
class="btn btn-default"
:disabled="progress"
@click="unmuteUser"
>
<template v-if="progress"> <template v-if="progress">
{{ $t('user_card.unmute_progress') }} {{ $t('user_card.unmute_progress') }}
</template> </template>
@ -14,12 +9,7 @@
{{ $t('user_card.unmute') }} {{ $t('user_card.unmute') }}
</template> </template>
</button> </button>
<button <button class="btn btn-default" @click="muteUser" :disabled="progress" v-else>
v-else
class="btn btn-default"
:disabled="progress"
@click="muteUser"
>
<template v-if="progress"> <template v-if="progress">
{{ $t('user_card.mute_progress') }} {{ $t('user_card.mute_progress') }}
</template> </template>

View File

@ -2,29 +2,26 @@
<div class="nav-panel"> <div class="nav-panel">
<div class="panel panel-default"> <div class="panel panel-default">
<ul> <ul>
<li v-if="currentUser"> <li v-if='currentUser'>
<router-link :to="{ name: 'friends' }"> <router-link :to="{ name: 'friends' }">
{{ $t("nav.timeline") }} {{ $t("nav.timeline") }}
</router-link> </router-link>
</li> </li>
<li v-if="currentUser"> <li v-if='currentUser'>
<router-link :to="{ name: 'mentions', params: { username: currentUser.screen_name } }"> <router-link :to="{ name: 'mentions', params: { username: currentUser.screen_name } }">
{{ $t("nav.mentions") }} {{ $t("nav.mentions") }}
</router-link> </router-link>
</li> </li>
<li v-if="currentUser"> <li v-if='currentUser'>
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
{{ $t("nav.dms") }} {{ $t("nav.dms") }}
</router-link> </router-link>
</li> </li>
<li v-if="currentUser && currentUser.locked"> <li v-if='currentUser && currentUser.locked'>
<router-link :to="{ name: 'friend-requests' }"> <router-link :to="{ name: 'friend-requests' }">
{{ $t("nav.friend_requests") }} {{ $t("nav.friend_requests")}}
<span <span v-if='followRequestCount > 0' class="badge follow-request-count">
v-if="followRequestCount > 0" {{followRequestCount}}
class="badge follow-request-count"
>
{{ followRequestCount }}
</span> </span>
</router-link> </router-link>
</li> </li>

View File

@ -1,90 +1,41 @@
<template> <template>
<status <status v-if="notification.type === 'mention'" :compact="true" :statusoid="notification.status"></status>
v-if="notification.type === 'mention'" <div class="non-mention" :class="[userClass, { highlighted: userStyle }]" :style="[ userStyle ]" v-else>
:compact="true" <a class='avatar-container' :href="notification.action.user.statusnet_profile_url" @click.stop.prevent.capture="toggleUserExpanded">
:statusoid="notification.status" <UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/>
/>
<div
v-else
class="non-mention"
:class="[userClass, { highlighted: userStyle }]"
:style="[ userStyle ]"
>
<a
class="avatar-container"
:href="notification.action.user.statusnet_profile_url"
@click.stop.prevent.capture="toggleUserExpanded"
>
<UserAvatar
:compact="true"
:better-shadow="betterShadow"
:src="notification.action.user.profile_image_url_original"
/>
</a> </a>
<div class="notification-right"> <div class='notification-right'>
<UserCard <UserCard :user="user" :rounded="true" :bordered="true" v-if="userExpanded"/>
v-if="userExpanded"
:user="user"
:rounded="true"
:bordered="true"
/>
<span class="notification-details"> <span class="notification-details">
<div class="name-and-action"> <div class="name-and-action">
<span <span class="username" v-if="!!notification.action.user.name_html" :title="'@'+notification.action.user.screen_name" v-html="notification.action.user.name_html"></span>
v-if="!!notification.action.user.name_html" <span class="username" v-else :title="'@'+notification.action.user.screen_name">{{ notification.action.user.name }}</span>
class="username"
:title="'@'+notification.action.user.screen_name"
v-html="notification.action.user.name_html"
/>
<span
v-else
class="username"
:title="'@'+notification.action.user.screen_name"
>{{ notification.action.user.name }}</span>
<span v-if="notification.type === 'like'"> <span v-if="notification.type === 'like'">
<i class="fa icon-star lit" /> <i class="fa icon-star lit"></i>
<small>{{ $t('notifications.favorited_you') }}</small> <small>{{$t('notifications.favorited_you')}}</small>
</span> </span>
<span v-if="notification.type === 'repeat'"> <span v-if="notification.type === 'repeat'">
<i <i class="fa icon-retweet lit" :title="$t('tool_tip.repeat')"></i>
class="fa icon-retweet lit" <small>{{$t('notifications.repeated_you')}}</small>
:title="$t('tool_tip.repeat')"
/>
<small>{{ $t('notifications.repeated_you') }}</small>
</span> </span>
<span v-if="notification.type === 'follow'"> <span v-if="notification.type === 'follow'">
<i class="fa icon-user-plus lit" /> <i class="fa icon-user-plus lit"></i>
<small>{{ $t('notifications.followed_you') }}</small> <small>{{$t('notifications.followed_you')}}</small>
</span> </span>
</div> </div>
<div class="timeago"> <div class="timeago">
<router-link <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" class="faint-link">
v-if="notification.status" <timeago :since="notification.action.created_at" :auto-update="240"></timeago>
:to="{ name: 'conversation', params: { id: notification.status.id } }"
class="faint-link"
>
<timeago
:since="notification.action.created_at"
:auto-update="240"
/>
</router-link> </router-link>
</div> </div>
</span> </span>
<div <div class="follow-text" v-if="notification.type === 'follow'">
v-if="notification.type === 'follow'"
class="follow-text"
>
<router-link :to="userProfileLink(notification.action.user)"> <router-link :to="userProfileLink(notification.action.user)">
@{{ notification.action.user.screen_name }} @{{notification.action.user.screen_name}}
</router-link> </router-link>
</div> </div>
<template v-else> <template v-else>
<status <status class="faint" :compact="true" :statusoid="notification.status" :noHeading="true"></status>
class="faint"
:compact="true"
:statusoid="notification.status"
:no-heading="true"
/>
</template> </template>
</div> </div>
</div> </div>

View File

@ -1,62 +1,31 @@
<template> <template>
<div class="notifications"> <div class="notifications">
<div class="panel panel-default"> <div class="panel panel-default">
<div <div v-if="!noHeading" class="panel-heading">
v-if="!noHeading"
class="panel-heading"
>
<div class="title"> <div class="title">
{{ $t('notifications.notifications') }} {{$t('notifications.notifications')}}
<span <span class="badge badge-notification unseen-count" v-if="unseenCount">{{unseenCount}}</span>
v-if="unseenCount"
class="badge badge-notification unseen-count"
>{{ unseenCount }}</span>
</div> </div>
<div <div @click.prevent class="loadmore-error alert error" v-if="error">
v-if="error" {{$t('timeline.error_fetching')}}
class="loadmore-error alert error"
@click.prevent
>
{{ $t('timeline.error_fetching') }}
</div> </div>
<button <button v-if="unseenCount" @click.prevent="markAsSeen" class="read-button">{{$t('notifications.read')}}</button>
v-if="unseenCount"
class="read-button"
@click.prevent="markAsSeen"
>
{{ $t('notifications.read') }}
</button>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div <div v-for="notification in visibleNotifications" :key="notification.action.id" class="notification" :class='{"unseen": !notification.seen}'>
v-for="notification in visibleNotifications" <div class="notification-overlay"></div>
:key="notification.action.id" <notification :notification="notification"></notification>
class="notification"
:class="{&quot;unseen&quot;: !notification.seen}"
>
<div class="notification-overlay" />
<notification :notification="notification" />
</div> </div>
</div> </div>
<div class="panel-footer"> <div class="panel-footer">
<div <div v-if="bottomedOut" class="new-status-notification text-center panel-footer faint">
v-if="bottomedOut" {{$t('notifications.no_more_notifications')}}
class="new-status-notification text-center panel-footer faint"
>
{{ $t('notifications.no_more_notifications') }}
</div> </div>
<a <a v-else-if="!loading" href="#" v-on:click.prevent="fetchOlderNotifications()">
v-else-if="!loading" <div class="new-status-notification text-center panel-footer">{{$t('notifications.load_older')}}</div>
href="#"
@click.prevent="fetchOlderNotifications()"
>
<div class="new-status-notification text-center panel-footer">{{ $t('notifications.load_older') }}</div>
</a> </a>
<div <div v-else class="new-status-notification text-center panel-footer">
v-else <i class="icon-spin3 animate-spin"/>
class="new-status-notification text-center panel-footer"
>
<i class="icon-spin3 animate-spin" />
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,7 +11,7 @@ const oac = {
}).then((result) => { }).then((result) => {
this.$store.commit('setToken', result.access_token) this.$store.commit('setToken', result.access_token)
this.$store.dispatch('loginUser', result.access_token) this.$store.dispatch('loginUser', result.access_token)
this.$router.push({ name: 'friends' }) this.$router.push({name: 'friends'})
}) })
} }
} }

View File

@ -1,39 +1,27 @@
<template> <template>
<div <div class="opacity-control style-control" :class="{ disabled: !present || disabled }">
class="opacity-control style-control" <label :for="name" class="label">
:class="{ disabled: !present || disabled }" {{$t('settings.style.common.opacity')}}
> </label>
<label <input
:for="name" v-if="typeof fallback !== 'undefined'"
class="label" class="opt exclude-disabled"
> :id="name + '-o'"
{{ $t('settings.style.common.opacity') }} type="checkbox"
</label> :checked="present"
<input @input="$emit('input', !present ? fallback : undefined)">
v-if="typeof fallback !== 'undefined'" <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label>
:id="name + '-o'" <input
class="opt exclude-disabled" :id="name"
type="checkbox" class="input-number"
:checked="present" type="number"
@input="$emit('input', !present ? fallback : undefined)" :value="value || fallback"
> :disabled="!present || disabled"
<label @input="$emit('input', $event.target.value)"
v-if="typeof fallback !== 'undefined'" max="1"
class="opt-l" min="0"
:for="name + '-o'" step=".05">
/> </div>
<input
:id="name"
class="input-number"
type="number"
:value="value || fallback"
:disabled="!present || disabled"
max="1"
min="0"
step=".05"
@input="$emit('input', $event.target.value)"
>
</div>
</template> </template>
<script> <script>

View File

@ -6,13 +6,13 @@ import fileTypeService from '../../services/file_type/file_type.service.js'
import Completion from '../../services/completion/completion.js' import Completion from '../../services/completion/completion.js'
import { take, filter, reject, map, uniqBy } from 'lodash' import { take, filter, reject, map, uniqBy } from 'lodash'
const buildMentionsString = ({ user, attentions }, currentUser) => { const buildMentionsString = ({user, attentions}, currentUser) => {
let allAttentions = [...attentions] let allAttentions = [...attentions]
allAttentions.unshift(user) allAttentions.unshift(user)
allAttentions = uniqBy(allAttentions, 'id') allAttentions = uniqBy(allAttentions, 'id')
allAttentions = reject(allAttentions, { id: currentUser.id }) allAttentions = reject(allAttentions, {id: currentUser.id})
let mentions = map(allAttentions, (attention) => { let mentions = map(allAttentions, (attention) => {
return `@${attention.screen_name}` return `@${attention.screen_name}`
@ -48,17 +48,17 @@ const PostStatusForm = {
let statusText = preset || '' let statusText = preset || ''
const scopeCopy = typeof this.$store.state.config.scopeCopy === 'undefined' const scopeCopy = typeof this.$store.state.config.scopeCopy === 'undefined'
? this.$store.state.instance.scopeCopy ? this.$store.state.instance.scopeCopy
: this.$store.state.config.scopeCopy : this.$store.state.config.scopeCopy
if (this.replyTo) { if (this.replyTo) {
const currentUser = this.$store.state.users.currentUser const currentUser = this.$store.state.users.currentUser
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
} }
const scope = ((this.copyMessageScope && scopeCopy) || this.copyMessageScope === 'direct') const scope = (this.copyMessageScope && scopeCopy || this.copyMessageScope === 'direct')
? this.copyMessageScope ? this.copyMessageScope
: this.$store.state.users.currentUser.default_scope : this.$store.state.users.currentUser.default_scope
const contentType = typeof this.$store.state.config.postContentType === 'undefined' const contentType = typeof this.$store.state.config.postContentType === 'undefined'
? this.$store.state.instance.postContentType ? this.$store.state.instance.postContentType
@ -88,13 +88,13 @@ const PostStatusForm = {
const query = this.textAtCaret.slice(1).toUpperCase() const query = this.textAtCaret.slice(1).toUpperCase()
const matchedUsers = filter(this.users, (user) => { const matchedUsers = filter(this.users, (user) => {
return user.screen_name.toUpperCase().startsWith(query) || return user.screen_name.toUpperCase().startsWith(query) ||
(user.name && user.name.toUpperCase().startsWith(query)) user.name && user.name.toUpperCase().startsWith(query)
}) })
if (matchedUsers.length <= 0) { if (matchedUsers.length <= 0) {
return false return false
} }
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
return map(take(matchedUsers, 5), ({ screen_name, name, profile_image_url_original }, index) => ({ return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}, index) => ({
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
screen_name: `@${screen_name}`, screen_name: `@${screen_name}`,
name: name, name: name,
@ -107,8 +107,7 @@ const PostStatusForm = {
if (matchedEmoji.length <= 0) { if (matchedEmoji.length <= 0) {
return false return false
} }
// eslint-disable-next-line camelcase return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
return map(take(matchedEmoji, 5), ({ shortcode, image_url, utf }, index) => ({
screen_name: `:${shortcode}:`, screen_name: `:${shortcode}:`,
name: '', name: '',
utf: utf || '', utf: utf || '',
@ -135,8 +134,8 @@ const PostStatusForm = {
}, },
showAllScopes () { showAllScopes () {
const minimalScopesMode = typeof this.$store.state.config.minimalScopesMode === 'undefined' const minimalScopesMode = typeof this.$store.state.config.minimalScopesMode === 'undefined'
? this.$store.state.instance.minimalScopesMode ? this.$store.state.instance.minimalScopesMode
: this.$store.state.config.minimalScopesMode : this.$store.state.config.minimalScopesMode
return !minimalScopesMode return !minimalScopesMode
}, },
emoji () { emoji () {
@ -234,7 +233,7 @@ const PostStatusForm = {
onKeydown (e) { onKeydown (e) {
e.stopPropagation() e.stopPropagation()
}, },
setCaret ({ target: { selectionStart } }) { setCaret ({target: {selectionStart}}) {
this.caret = selectionStart this.caret = selectionStart
}, },
postStatus (newStatus) { postStatus (newStatus) {
@ -315,7 +314,7 @@ const PostStatusForm = {
}, },
fileDrop (e) { fileDrop (e) {
if (e.dataTransfer.files.length > 0) { if (e.dataTransfer.files.length > 0) {
e.preventDefault() // allow dropping text like before e.preventDefault() // allow dropping text like before
this.dropFiles = e.dataTransfer.files this.dropFiles = e.dataTransfer.files
} }
}, },

View File

@ -1,204 +1,107 @@
<template> <template>
<div class="post-status-form"> <div class="post-status-form">
<form @submit.prevent="postStatus(newStatus)"> <form @submit.prevent="postStatus(newStatus)">
<div class="form-group"> <div class="form-group" >
<i18n <i18n
v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private'" v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private'"
path="post_status.account_not_locked_warning" path="post_status.account_not_locked_warning"
tag="p" tag="p"
class="visibility-notice" class="visibility-notice">
> <router-link :to="{ name: 'user-settings' }">{{ $t('post_status.account_not_locked_warning_link') }}</router-link>
<router-link :to="{ name: 'user-settings' }"> </i18n>
{{ $t('post_status.account_not_locked_warning_link') }} <p v-if="newStatus.visibility === 'direct'" class="visibility-notice">
</router-link> <span v-if="safeDMEnabled">{{ $t('post_status.direct_warning_to_first_only') }}</span>
</i18n> <span v-else>{{ $t('post_status.direct_warning_to_all') }}</span>
<p </p>
v-if="newStatus.visibility === 'direct'" <EmojiInput
class="visibility-notice" v-if="newStatus.spoilerText || alwaysShowSubject"
> type="text"
<span v-if="safeDMEnabled">{{ $t('post_status.direct_warning_to_first_only') }}</span> :placeholder="$t('post_status.content_warning')"
<span v-else>{{ $t('post_status.direct_warning_to_all') }}</span> v-model="newStatus.spoilerText"
</p> classname="form-control"
<EmojiInput />
v-if="newStatus.spoilerText || alwaysShowSubject" <textarea
v-model="newStatus.spoilerText" ref="textarea"
type="text" @click="setCaret"
:placeholder="$t('post_status.content_warning')" @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
classname="form-control" @keydown="onKeydown"
/> @keydown.down="cycleForward"
<textarea @keydown.up="cycleBackward"
ref="textarea" @keydown.shift.tab="cycleBackward"
v-model="newStatus.status" @keydown.tab="cycleForward"
:placeholder="$t('post_status.default')" @keydown.enter="replaceCandidate"
rows="1" @keydown.meta.enter="postStatus(newStatus)"
class="form-control" @keyup.ctrl.enter="postStatus(newStatus)"
:disabled="posting" @drop="fileDrop"
@click="setCaret" @dragover.prevent="fileDrag"
@keyup.exact="setCaret" @input="resize"
@keydown.exact="onKeydown" @paste="paste"
@keydown.exact.down="cycleForward" :disabled="posting"
@keydown.exact.up="cycleBackward"
@keydown.exact.shift.tab="cycleBackward"
@keydown.exact.tab="cycleForward"
@keydown.exact.enter="replaceCandidate"
@keydown.exact.meta.enter="postStatus(newStatus)"
@keyup.exact.ctrl.enter="postStatus(newStatus)"
@drop="fileDrop"
@dragover.prevent="fileDrag"
@input="resize"
@paste="paste"
/>
<div class="visibility-tray">
<span
v-if="formattingOptionsEnabled"
class="text-format"
>
<label
for="post-content-type"
class="select"
>
<select
id="post-content-type"
v-model="newStatus.contentType"
class="form-control"
>
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
</option>
</select>
<i class="icon-down-open" />
</label>
</span>
<scope-selector
:show-all="showAllScopes"
:user-default="userDefaultScope"
:original-scope="copyMessageScope"
:initial-scope="newStatus.visibility"
:on-scope-change="changeVis"
/>
</div>
</div>
<div
v-if="candidates"
class="autocomplete-panel"
> >
</textarea>
<div class="visibility-tray">
<span class="text-format" v-if="formattingOptionsEnabled">
<label for="post-content-type" class="select">
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
<option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
{{$t(`post_status.content_type["${postFormat}"]`)}}
</option>
</select>
<i class="icon-down-open"></i>
</label>
</span>
<scope-selector
:showAll="showAllScopes"
:userDefault="userDefaultScope"
:originalScope="copyMessageScope"
:initialScope="newStatus.visibility"
:onScopeChange="changeVis"/>
</div>
</div>
<div class="autocomplete-panel" v-if="candidates">
<div class="autocomplete-panel-body"> <div class="autocomplete-panel-body">
<div <div
v-for="(candidate, index) in candidates" v-for="(candidate, index) in candidates"
:key="index" :key="index"
@click="replace(candidate.utf || (candidate.screen_name + ' '))"
class="autocomplete-item" class="autocomplete-item"
:class="{ highlighted: candidate.highlighted }" :class="{ highlighted: candidate.highlighted }"
@click="replace(candidate.utf || (candidate.screen_name + ' '))"
> >
<span v-if="candidate.img"><img :src="candidate.img"></span> <span v-if="candidate.img"><img :src="candidate.img" /></span>
<span v-else>{{ candidate.utf }}</span> <span v-else>{{candidate.utf}}</span>
<span>{{ candidate.screen_name }}<small>{{ candidate.name }}</small></span> <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
</div> </div>
</div> </div>
</div> </div>
<div class="form-bottom"> <div class='form-bottom'>
<media-upload <media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
ref="mediaUpload"
:drop-files="dropFiles"
@uploading="disableSubmit"
@uploaded="addMediaFile"
@upload-failed="uploadFailed"
/>
<p <p v-if="isOverLengthLimit" class="error">{{ charactersLeft }}</p>
v-if="isOverLengthLimit" <p class="faint" v-else-if="hasStatusLengthLimit">{{ charactersLeft }}</p>
class="error"
>
{{ charactersLeft }}
</p>
<p
v-else-if="hasStatusLengthLimit"
class="faint"
>
{{ charactersLeft }}
</p>
<button <button v-if="posting" disabled class="btn btn-default">{{$t('post_status.posting')}}</button>
v-if="posting" <button v-else-if="isOverLengthLimit" disabled class="btn btn-default">{{$t('general.submit')}}</button>
disabled <button v-else :disabled="submitDisabled" type="submit" class="btn btn-default">{{$t('general.submit')}}</button>
class="btn btn-default"
>
{{ $t('post_status.posting') }}
</button>
<button
v-else-if="isOverLengthLimit"
disabled
class="btn btn-default"
>
{{ $t('general.submit') }}
</button>
<button
v-else
:disabled="submitDisabled"
type="submit"
class="btn btn-default"
>
{{ $t('general.submit') }}
</button>
</div> </div>
<div <div class='alert error' v-if="error">
v-if="error"
class="alert error"
>
Error: {{ error }} Error: {{ error }}
<i <i class="button-icon icon-cancel" @click="clearError"></i>
class="button-icon icon-cancel"
@click="clearError"
/>
</div> </div>
<div class="attachments"> <div class="attachments">
<div <div class="media-upload-wrapper" v-for="file in newStatus.files">
v-for="file in newStatus.files" <i class="fa button-icon icon-cancel" @click="removeMediaFile(file)"></i>
:key="file.url"
class="media-upload-wrapper"
>
<i
class="fa button-icon icon-cancel"
@click="removeMediaFile(file)"
/>
<div class="media-upload-container attachment"> <div class="media-upload-container attachment">
<img <img class="thumbnail media-upload" :src="file.url" v-if="type(file) === 'image'"></img>
v-if="type(file) === 'image'" <video v-if="type(file) === 'video'" :src="file.url" controls></video>
class="thumbnail media-upload" <audio v-if="type(file) === 'audio'" :src="file.url" controls></audio>
:src="file.url" <a v-if="type(file) === 'unknown'" :href="file.url">{{file.url}}</a>
>
<video
v-if="type(file) === 'video'"
:src="file.url"
controls
/>
<audio
v-if="type(file) === 'audio'"
:src="file.url"
controls
/>
<a
v-if="type(file) === 'unknown'"
:href="file.url"
>{{ file.url }}</a>
</div> </div>
</div> </div>
</div> </div>
<div <div class="upload_settings" v-if="newStatus.files.length > 0">
v-if="newStatus.files.length > 0" <input type="checkbox" id="filesSensitive" v-model="newStatus.nsfw">
class="upload_settings" <label for="filesSensitive">{{$t('post_status.attachments_sensitive')}}</label>
>
<input
id="filesSensitive"
v-model="newStatus.nsfw"
type="checkbox"
>
<label for="filesSensitive">{{ $t('post_status.attachments_sensitive') }}</label>
</div> </div>
</form> </form>
</div> </div>
@ -311,6 +214,7 @@
} }
} }
.btn { .btn {
cursor: pointer; cursor: pointer;
} }

View File

@ -1,9 +1,5 @@
<template> <template>
<Timeline <Timeline :title="$t('nav.twkn')" v-bind:timeline="timeline" v-bind:timeline-name="'publicAndExternal'"/>
:title="$t('nav.twkn')"
:timeline="timeline"
:timeline-name="'publicAndExternal'"
/>
</template> </template>
<script src="./public_and_external_timeline.js"></script> <script src="./public_and_external_timeline.js"></script>

View File

@ -1,9 +1,5 @@
<template> <template>
<Timeline <Timeline :title="$t('nav.public_tl')" v-bind:timeline="timeline" v-bind:timeline-name="'public'"/>
:title="$t('nav.public_tl')"
:timeline="timeline"
:timeline-name="'public'"
/>
</template> </template>
<script src="./public_timeline.js"></script> <script src="./public_timeline.js"></script>

View File

@ -1,50 +1,37 @@
<template> <template>
<div <div class="range-control style-control" :class="{ disabled: !present || disabled }">
class="range-control style-control" <label :for="name" class="label">
:class="{ disabled: !present || disabled }" {{label}}
> </label>
<label <input
:for="name" v-if="typeof fallback !== 'undefined'"
class="label" class="opt exclude-disabled"
> :id="name + '-o'"
{{ label }} type="checkbox"
</label> :checked="present"
<input @input="$emit('input', !present ? fallback : undefined)">
v-if="typeof fallback !== 'undefined'" <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label>
:id="name + '-o'" <input
class="opt exclude-disabled" :id="name"
type="checkbox" class="input-number"
:checked="present" type="range"
@input="$emit('input', !present ? fallback : undefined)" :value="value || fallback"
> :disabled="!present || disabled"
<label @input="$emit('input', $event.target.value)"
v-if="typeof fallback !== 'undefined'" :max="max || hardMax || 100"
class="opt-l" :min="min || hardMin || 0"
:for="name + '-o'" :step="step || 1">
/> <input
<input :id="name"
:id="name" class="input-number"
class="input-number" type="number"
type="range" :value="value || fallback"
:value="value || fallback" :disabled="!present || disabled"
:disabled="!present || disabled" @input="$emit('input', $event.target.value)"
:max="max || hardMax || 100" :max="hardMax"
:min="min || hardMin || 0" :min="hardMin"
:step="step || 1" :step="step || 1">
@input="$emit('input', $event.target.value)" </div>
>
<input
:id="name"
class="input-number"
type="number"
:value="value || fallback"
:disabled="!present || disabled"
:max="hardMax"
:min="hardMin"
:step="step || 1"
@input="$emit('input', $event.target.value)"
>
</div>
</template> </template>
<script> <script>

View File

@ -28,7 +28,7 @@ const registration = {
}, },
created () { created () {
if ((!this.registrationOpen && !this.token) || this.signedIn) { if ((!this.registrationOpen && !this.token) || this.signedIn) {
this.$router.push({ name: 'root' }) this.$router.push({name: 'root'})
} }
this.setCaptcha() this.setCaptcha()
@ -61,7 +61,7 @@ const registration = {
if (!this.$v.$invalid) { if (!this.$v.$invalid) {
try { try {
await this.signUp(this.user) await this.signUp(this.user)
this.$router.push({ name: 'friends' }) this.$router.push({name: 'friends'})
} catch (error) { } catch (error) {
console.warn('Registration failed: ' + error) console.warn('Registration failed: ' + error)
} }

View File

@ -1,234 +1,109 @@
<template> <template>
<div class="settings panel panel-default"> <div class="settings panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{{ $t('registration.registration') }} {{$t('registration.registration')}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<form <form v-on:submit.prevent='submit(user)' class='registration-form'>
class="registration-form" <div class='container'>
@submit.prevent="submit(user)" <div class='text-fields'>
> <div class='form-group' :class="{ 'form-group--error': $v.user.username.$error }">
<div class="container"> <label class='form--label' for='sign-up-username'>{{$t('login.username')}}</label>
<div class="text-fields"> <input :disabled="isPending" v-model.trim='$v.user.username.$model' class='form-control' id='sign-up-username' :placeholder="$t('registration.username_placeholder')">
<div
class="form-group"
:class="{ 'form-group--error': $v.user.username.$error }"
>
<label
class="form--label"
for="sign-up-username"
>{{ $t('login.username') }}</label>
<input
id="sign-up-username"
v-model.trim="$v.user.username.$model"
:disabled="isPending"
class="form-control"
:placeholder="$t('registration.username_placeholder')"
>
</div> </div>
<div <div class="form-error" v-if="$v.user.username.$dirty">
v-if="$v.user.username.$dirty"
class="form-error"
>
<ul> <ul>
<li v-if="!$v.user.username.required"> <li v-if="!$v.user.username.required">
<span>{{ $t('registration.validations.username_required') }}</span> <span>{{$t('registration.validations.username_required')}}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div <div class='form-group' :class="{ 'form-group--error': $v.user.fullname.$error }">
class="form-group" <label class='form--label' for='sign-up-fullname'>{{$t('registration.fullname')}}</label>
:class="{ 'form-group--error': $v.user.fullname.$error }" <input :disabled="isPending" v-model.trim='$v.user.fullname.$model' class='form-control' id='sign-up-fullname' :placeholder="$t('registration.fullname_placeholder')">
>
<label
class="form--label"
for="sign-up-fullname"
>{{ $t('registration.fullname') }}</label>
<input
id="sign-up-fullname"
v-model.trim="$v.user.fullname.$model"
:disabled="isPending"
class="form-control"
:placeholder="$t('registration.fullname_placeholder')"
>
</div> </div>
<div <div class="form-error" v-if="$v.user.fullname.$dirty">
v-if="$v.user.fullname.$dirty"
class="form-error"
>
<ul> <ul>
<li v-if="!$v.user.fullname.required"> <li v-if="!$v.user.fullname.required">
<span>{{ $t('registration.validations.fullname_required') }}</span> <span>{{$t('registration.validations.fullname_required')}}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div <div class='form-group' :class="{ 'form-group--error': $v.user.email.$error }">
class="form-group" <label class='form--label' for='email'>{{$t('registration.email')}}</label>
:class="{ 'form-group--error': $v.user.email.$error }" <input :disabled="isPending" v-model='$v.user.email.$model' class='form-control' id='email' type="email">
>
<label
class="form--label"
for="email"
>{{ $t('registration.email') }}</label>
<input
id="email"
v-model="$v.user.email.$model"
:disabled="isPending"
class="form-control"
type="email"
>
</div> </div>
<div <div class="form-error" v-if="$v.user.email.$dirty">
v-if="$v.user.email.$dirty"
class="form-error"
>
<ul> <ul>
<li v-if="!$v.user.email.required"> <li v-if="!$v.user.email.required">
<span>{{ $t('registration.validations.email_required') }}</span> <span>{{$t('registration.validations.email_required')}}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div class="form-group"> <div class='form-group'>
<label <label class='form--label' for='bio'>{{$t('registration.bio')}} ({{$t('general.optional')}})</label>
class="form--label" <textarea :disabled="isPending" v-model='user.bio' class='form-control' id='bio' :placeholder="bioPlaceholder"></textarea>
for="bio"
>{{ $t('registration.bio') }} ({{ $t('general.optional') }})</label>
<textarea
id="bio"
v-model="user.bio"
:disabled="isPending"
class="form-control"
:placeholder="bioPlaceholder"
/>
</div> </div>
<div <div class='form-group' :class="{ 'form-group--error': $v.user.password.$error }">
class="form-group" <label class='form--label' for='sign-up-password'>{{$t('login.password')}}</label>
:class="{ 'form-group--error': $v.user.password.$error }" <input :disabled="isPending" v-model='user.password' class='form-control' id='sign-up-password' type='password'>
>
<label
class="form--label"
for="sign-up-password"
>{{ $t('login.password') }}</label>
<input
id="sign-up-password"
v-model="user.password"
:disabled="isPending"
class="form-control"
type="password"
>
</div> </div>
<div <div class="form-error" v-if="$v.user.password.$dirty">
v-if="$v.user.password.$dirty"
class="form-error"
>
<ul> <ul>
<li v-if="!$v.user.password.required"> <li v-if="!$v.user.password.required">
<span>{{ $t('registration.validations.password_required') }}</span> <span>{{$t('registration.validations.password_required')}}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div <div class='form-group' :class="{ 'form-group--error': $v.user.confirm.$error }">
class="form-group" <label class='form--label' for='sign-up-password-confirmation'>{{$t('registration.password_confirm')}}</label>
:class="{ 'form-group--error': $v.user.confirm.$error }" <input :disabled="isPending" v-model='user.confirm' class='form-control' id='sign-up-password-confirmation' type='password'>
>
<label
class="form--label"
for="sign-up-password-confirmation"
>{{ $t('registration.password_confirm') }}</label>
<input
id="sign-up-password-confirmation"
v-model="user.confirm"
:disabled="isPending"
class="form-control"
type="password"
>
</div> </div>
<div <div class="form-error" v-if="$v.user.confirm.$dirty">
v-if="$v.user.confirm.$dirty"
class="form-error"
>
<ul> <ul>
<li v-if="!$v.user.confirm.required"> <li v-if="!$v.user.confirm.required">
<span>{{ $t('registration.validations.password_confirmation_required') }}</span> <span>{{$t('registration.validations.password_confirmation_required')}}</span>
</li> </li>
<li v-if="!$v.user.confirm.sameAsPassword"> <li v-if="!$v.user.confirm.sameAsPassword">
<span>{{ $t('registration.validations.password_confirmation_match') }}</span> <span>{{$t('registration.validations.password_confirmation_match')}}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div <div class="form-group" id="captcha-group" v-if="captcha.type != 'none'">
v-if="captcha.type != 'none'" <label class='form--label' for='captcha-label'>{{$t('captcha')}}</label>
id="captcha-group"
class="form-group"
>
<label
class="form--label"
for="captcha-label"
>{{ $t('captcha') }}</label>
<template v-if="captcha.type == 'kocaptcha'"> <template v-if="captcha.type == 'kocaptcha'">
<img <img v-bind:src="captcha.url" v-on:click="setCaptcha">
:src="captcha.url"
@click="setCaptcha"
>
<sub>{{ $t('registration.new_captcha') }}</sub> <sub>{{$t('registration.new_captcha')}}</sub>
<input <input :disabled="isPending"
id="captcha-answer" v-model='captcha.solution'
v-model="captcha.solution" class='form-control' id='captcha-answer' type='text' autocomplete="off">
:disabled="isPending"
class="form-control"
type="text"
autocomplete="off"
>
</template> </template>
</div> </div>
<div <div class='form-group' v-if='token' >
v-if="token" <label for='token'>{{$t('registration.token')}}</label>
class="form-group" <input disabled='true' v-model='token' class='form-control' id='token' type='text'>
>
<label for="token">{{ $t('registration.token') }}</label>
<input
id="token"
v-model="token"
disabled="true"
class="form-control"
type="text"
>
</div> </div>
<div class="form-group"> <div class='form-group'>
<button <button :disabled="isPending" type='submit' class='btn btn-default'>{{$t('general.submit')}}</button>
:disabled="isPending"
type="submit"
class="btn btn-default"
>
{{ $t('general.submit') }}
</button>
</div> </div>
</div> </div>
<div <div class='terms-of-service' v-html="termsOfService">
class="terms-of-service" </div>
v-html="termsOfService"
/>
</div> </div>
<div <div v-if="serverValidationErrors.length" class='form-group'>
v-if="serverValidationErrors.length" <div class='alert error'>
class="form-group" <span v-for="error in serverValidationErrors">{{error}}</span>
>
<div class="alert error">
<span
v-for="error in serverValidationErrors"
:key="error"
>{{ error }}</span>
</div> </div>
</div> </div>
</form> </form>

View File

@ -1,23 +1,9 @@
<template> <template>
<div class="remote-follow"> <div class="remote-follow">
<form <form method="POST" :action='subscribeUrl'>
method="POST" <input type="hidden" name="nickname" :value="user.screen_name">
:action="subscribeUrl" <input type="hidden" name="profile" value="">
> <button click="submit" class="remote-button">
<input
type="hidden"
name="nickname"
:value="user.screen_name"
>
<input
type="hidden"
name="profile"
value=""
>
<button
click="submit"
class="remote-button"
>
{{ $t('user_card.remote_follow') }} {{ $t('user_card.remote_follow') }}
</button> </button>
</form> </form>

View File

@ -11,9 +11,9 @@ const RetweetButton = {
methods: { methods: {
retweet () { retweet () {
if (!this.status.repeated) { if (!this.status.repeated) {
this.$store.dispatch('retweet', { id: this.status.id }) this.$store.dispatch('retweet', {id: this.status.id})
} else { } else {
this.$store.dispatch('unretweet', { id: this.status.id }) this.$store.dispatch('unretweet', {id: this.status.id})
} }
this.animated = true this.animated = true
setTimeout(() => { setTimeout(() => {

View File

@ -1,29 +1,16 @@
<template> <template>
<div v-if="loggedIn"> <div v-if="loggedIn">
<template v-if="visibility !== 'private' && visibility !== 'direct'"> <template v-if="visibility !== 'private' && visibility !== 'direct'">
<i <i :class='classes' class='button-icon retweet-button icon-retweet rt-active' v-on:click.prevent='retweet()' :title="$t('tool_tip.repeat')"></i>
:class="classes" <span v-if='!hidePostStatsLocal && status.repeat_num > 0'>{{status.repeat_num}}</span>
class="button-icon retweet-button icon-retweet rt-active"
:title="$t('tool_tip.repeat')"
@click.prevent="retweet()"
/>
<span v-if="!hidePostStatsLocal && status.repeat_num > 0">{{ status.repeat_num }}</span>
</template> </template>
<template v-else> <template v-else>
<i <i :class='classes' class='button-icon icon-lock' :title="$t('timeline.no_retweet_hint')"></i>
:class="classes"
class="button-icon icon-lock"
:title="$t('timeline.no_retweet_hint')"
/>
</template> </template>
</div> </div>
<div v-else-if="!loggedIn"> <div v-else-if="!loggedIn">
<i <i :class='classes' class='button-icon icon-retweet' :title="$t('tool_tip.repeat')"></i>
:class="classes" <span v-if='!hidePostStatsLocal && status.repeat_num > 0'>{{status.repeat_num}}</span>
class="button-icon icon-retweet"
:title="$t('tool_tip.repeat')"
/>
<span v-if="!hidePostStatsLocal && status.repeat_num > 0">{{ status.repeat_num }}</span>
</div> </div>
</template> </template>

View File

@ -29,10 +29,10 @@ const ScopeSelector = {
}, },
css () { css () {
return { return {
public: { selected: this.currentScope === 'public' }, public: {selected: this.currentScope === 'public'},
unlisted: { selected: this.currentScope === 'unlisted' }, unlisted: {selected: this.currentScope === 'unlisted'},
private: { selected: this.currentScope === 'private' }, private: {selected: this.currentScope === 'private'},
direct: { selected: this.currentScope === 'direct' } direct: {selected: this.currentScope === 'direct'}
} }
} }
}, },

View File

@ -1,34 +1,30 @@
<template> <template>
<div v-if="!showNothing"> <div v-if="!showNothing">
<i <i class="icon-mail-alt"
v-if="showDirect" :class="css.direct"
class="icon-mail-alt" :title="$t('post_status.scope.direct')"
:class="css.direct" v-if="showDirect"
:title="$t('post_status.scope.direct')" @click="changeVis('direct')">
@click="changeVis('direct')" </i>
/> <i class="icon-lock"
<i :class="css.private"
v-if="showPrivate" :title="$t('post_status.scope.private')"
class="icon-lock" v-if="showPrivate"
:class="css.private" v-on:click="changeVis('private')">
:title="$t('post_status.scope.private')" </i>
@click="changeVis('private')" <i class="icon-lock-open-alt"
/> :class="css.unlisted"
<i :title="$t('post_status.scope.unlisted')"
v-if="showUnlisted" v-if="showUnlisted"
class="icon-lock-open-alt" @click="changeVis('unlisted')">
:class="css.unlisted" </i>
:title="$t('post_status.scope.unlisted')" <i class="icon-globe"
@click="changeVis('unlisted')" :class="css.public"
/> :title="$t('post_status.scope.public')"
<i v-if="showPublic"
v-if="showPublic" @click="changeVis('public')">
class="icon-globe" </i>
:class="css.public" </div>
:title="$t('post_status.scope.public')"
@click="changeVis('public')"
/>
</div>
</template> </template>
<script src="./scope_selector.js"></script> <script src="./scope_selector.js"></script>

View File

@ -1,476 +1,302 @@
<template> <template>
<div class="settings panel panel-default"> <div class="settings panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<div class="title"> <div class="title">
{{ $t('settings.settings') }} {{$t('settings.settings')}}
</div>
<transition name="fade">
<template v-if="currentSaveStateNotice">
<div @click.prevent class="alert error" v-if="currentSaveStateNotice.error">
{{ $t('settings.saving_err') }}
</div>
<div @click.prevent class="alert transparent" v-if="!currentSaveStateNotice.error">
{{ $t('settings.saving_ok') }}
</div>
</template>
</transition>
</div>
<div class="panel-body">
<keep-alive>
<tab-switcher>
<div :label="$t('settings.general')" >
<div class="setting-item">
<h2>{{ $t('settings.interface') }}</h2>
<ul class="setting-list">
<li>
<interface-language-switcher />
</li>
<li v-if="instanceSpecificPanelPresent">
<input type="checkbox" id="hideISP" v-model="hideISPLocal">
<label for="hideISP">{{$t('settings.hide_isp')}}</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{$t('nav.timeline')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="hideMutedPosts" v-model="hideMutedPostsLocal">
<label for="hideMutedPosts">{{$t('settings.hide_muted_posts')}} {{$t('settings.instance_default', { value: hideMutedPostsDefault })}}</label>
</li>
<li>
<input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal">
<label for="collapseMessageWithSubject">
{{$t('settings.collapse_subject')}} {{$t('settings.instance_default', { value: collapseMessageWithSubjectDefault })}}
</label>
</li>
<li>
<input type="checkbox" id="streaming" v-model="streamingLocal">
<label for="streaming">{{$t('settings.streaming')}}</label>
<ul class="setting-list suboptions" :class="[{disabled: !streamingLocal}]">
<li>
<input :disabled="!streamingLocal" type="checkbox" id="pauseOnUnfocused" v-model="pauseOnUnfocusedLocal">
<label for="pauseOnUnfocused">{{$t('settings.pause_on_unfocused')}}</label>
</li>
</ul>
</li>
<li>
<input type="checkbox" id="autoload" v-model="autoLoadLocal">
<label for="autoload">{{$t('settings.autoload')}}</label>
</li>
<li>
<input type="checkbox" id="hoverPreview" v-model="hoverPreviewLocal">
<label for="hoverPreview">{{$t('settings.reply_link_preview')}}</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{$t('settings.composing')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="scopeCopy" v-model="scopeCopyLocal">
<label for="scopeCopy">
{{$t('settings.scope_copy')}} {{$t('settings.instance_default', { value: scopeCopyDefault })}}
</label>
</li>
<li>
<input type="checkbox" id="subjectHide" v-model="alwaysShowSubjectInputLocal">
<label for="subjectHide">
{{$t('settings.subject_input_always_show')}} {{$t('settings.instance_default', { value: alwaysShowSubjectInputDefault })}}
</label>
</li>
<li>
<div>
{{$t('settings.subject_line_behavior')}}
<label for="subjectLineBehavior" class="select">
<select id="subjectLineBehavior" v-model="subjectLineBehaviorLocal">
<option value="email">
{{$t('settings.subject_line_email')}}
{{subjectLineBehaviorDefault == 'email' ? $t('settings.instance_default_simple') : ''}}
</option>
<option value="masto">
{{$t('settings.subject_line_mastodon')}}
{{subjectLineBehaviorDefault == 'mastodon' ? $t('settings.instance_default_simple') : ''}}
</option>
<option value="noop">
{{$t('settings.subject_line_noop')}}
{{subjectLineBehaviorDefault == 'noop' ? $t('settings.instance_default_simple') : ''}}
</option>
</select>
<i class="icon-down-open"/>
</label>
</div>
</li>
<li>
<div>
{{$t('settings.post_status_content_type')}}
<label for="postContentType" class="select">
<select id="postContentType" v-model="postContentTypeLocal">
<option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
{{$t(`post_status.content_type["${postFormat}"]`)}}
{{postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : ''}}
</option>
</select>
<i class="icon-down-open"/>
</label>
</div>
</li>
<li>
<input type="checkbox" id="minimalScopesMode" v-model="minimalScopesModeLocal">
<label for="minimalScopesMode">
{{$t('settings.minimal_scopes_mode')}} {{$t('settings.instance_default', { value: minimalScopesModeDefault })}}
</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{$t('settings.attachments')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
<label for="hideAttachments">{{$t('settings.hide_attachments_in_tl')}}</label>
</li>
<li>
<input type="checkbox" id="hideAttachmentsInConv" v-model="hideAttachmentsInConvLocal">
<label for="hideAttachmentsInConv">{{$t('settings.hide_attachments_in_convo')}}</label>
</li>
<li>
<label for="maxThumbnails">{{$t('settings.max_thumbnails')}}</label>
<input class="number-input" type="number" id="maxThumbnails" v-model.number="maxThumbnails" min="0" step="1">
</li>
<li>
<input type="checkbox" id="hideNsfw" v-model="hideNsfwLocal">
<label for="hideNsfw">{{$t('settings.nsfw_clickthrough')}}</label>
</li>
<ul class="setting-list suboptions" >
<li>
<input :disabled="!hideNsfwLocal" type="checkbox" id="preloadImage" v-model="preloadImage">
<label for="preloadImage">{{$t('settings.preload_images')}}</label>
</li>
<li>
<input :disabled="!hideNsfwLocal" type="checkbox" id="useOneClickNsfw" v-model="useOneClickNsfw">
<label for="useOneClickNsfw">{{$t('settings.use_one_click_nsfw')}}</label>
</li>
</ul>
<li>
<input type="checkbox" id="stopGifs" v-model="stopGifs">
<label for="stopGifs">{{$t('settings.stop_gifs')}}</label>
</li>
<li>
<input type="checkbox" id="loopVideo" v-model="loopVideoLocal">
<label for="loopVideo">{{$t('settings.loop_video')}}</label>
<ul class="setting-list suboptions" :class="[{disabled: !streamingLocal}]">
<li>
<input :disabled="!loopVideoLocal || !loopSilentAvailable" type="checkbox" id="loopVideoSilentOnly" v-model="loopVideoSilentOnlyLocal">
<label for="loopVideoSilentOnly">{{$t('settings.loop_video_silent_only')}}</label>
<div v-if="!loopSilentAvailable" class="unavailable">
<i class="icon-globe"/>! {{$t('settings.limited_availability')}}
</div>
</li>
</ul>
</li>
<li>
<input type="checkbox" id="playVideosInModal" v-model="playVideosInModal">
<label for="playVideosInModal">{{$t('settings.play_videos_in_modal')}}</label>
</li>
<li>
<input type="checkbox" id="useContainFit" v-model="useContainFit">
<label for="useContainFit">{{$t('settings.use_contain_fit')}}</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{$t('settings.notifications')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="webPushNotifications" v-model="webPushNotificationsLocal">
<label for="webPushNotifications">
{{$t('settings.enable_web_push_notifications')}}
</label>
</li>
</ul>
</div>
</div> </div>
<transition name="fade"> <div :label="$t('settings.theme')" >
<template v-if="currentSaveStateNotice"> <div class="setting-item">
<div <style-switcher></style-switcher>
v-if="currentSaveStateNotice.error" </div>
class="alert error" </div>
@click.prevent
>
{{ $t('settings.saving_err') }}
</div>
<div <div :label="$t('settings.filtering')" >
v-if="!currentSaveStateNotice.error" <div class="setting-item">
class="alert transparent" <div class="select-multiple">
@click.prevent <span class="label">{{$t('settings.notification_visibility')}}</span>
> <ul class="option-list">
{{ $t('settings.saving_ok') }} <li>
</div> <input type="checkbox" id="notification-visibility-likes" v-model="notificationVisibilityLocal.likes">
</template> <label for="notification-visibility-likes">
</transition> {{$t('settings.notification_visibility_likes')}}
</div>
<div class="panel-body">
<keep-alive>
<tab-switcher>
<div :label="$t('settings.general')">
<div class="setting-item">
<h2>{{ $t('settings.interface') }}</h2>
<ul class="setting-list">
<li>
<interface-language-switcher />
</li>
<li v-if="instanceSpecificPanelPresent">
<input
id="hideISP"
v-model="hideISPLocal"
type="checkbox"
>
<label for="hideISP">{{ $t('settings.hide_isp') }}</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('nav.timeline') }}</h2>
<ul class="setting-list">
<li>
<input
id="hideMutedPosts"
v-model="hideMutedPostsLocal"
type="checkbox"
>
<label for="hideMutedPosts">{{ $t('settings.hide_muted_posts') }} {{ $t('settings.instance_default', { value: hideMutedPostsDefault }) }}</label>
</li>
<li>
<input
id="collapseMessageWithSubject"
v-model="collapseMessageWithSubjectLocal"
type="checkbox"
>
<label for="collapseMessageWithSubject">
{{ $t('settings.collapse_subject') }} {{ $t('settings.instance_default', { value: collapseMessageWithSubjectDefault }) }}
</label>
</li>
<li>
<input
id="streaming"
v-model="streamingLocal"
type="checkbox"
>
<label for="streaming">{{ $t('settings.streaming') }}</label>
<ul
class="setting-list suboptions"
:class="[{disabled: !streamingLocal}]"
>
<li>
<input
id="pauseOnUnfocused"
v-model="pauseOnUnfocusedLocal"
:disabled="!streamingLocal"
type="checkbox"
>
<label for="pauseOnUnfocused">{{ $t('settings.pause_on_unfocused') }}</label>
</li>
</ul>
</li>
<li>
<input
id="autoload"
v-model="autoLoadLocal"
type="checkbox"
>
<label for="autoload">{{ $t('settings.autoload') }}</label>
</li>
<li>
<input
id="hoverPreview"
v-model="hoverPreviewLocal"
type="checkbox"
>
<label for="hoverPreview">{{ $t('settings.reply_link_preview') }}</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('settings.composing') }}</h2>
<ul class="setting-list">
<li>
<input
id="scopeCopy"
v-model="scopeCopyLocal"
type="checkbox"
>
<label for="scopeCopy">
{{ $t('settings.scope_copy') }} {{ $t('settings.instance_default', { value: scopeCopyDefault }) }}
</label>
</li>
<li>
<input
id="subjectHide"
v-model="alwaysShowSubjectInputLocal"
type="checkbox"
>
<label for="subjectHide">
{{ $t('settings.subject_input_always_show') }} {{ $t('settings.instance_default', { value: alwaysShowSubjectInputDefault }) }}
</label>
</li>
<li>
<div>
{{ $t('settings.subject_line_behavior') }}
<label
for="subjectLineBehavior"
class="select"
>
<select
id="subjectLineBehavior"
v-model="subjectLineBehaviorLocal"
>
<option value="email">
{{ $t('settings.subject_line_email') }}
{{ subjectLineBehaviorDefault == 'email' ? $t('settings.instance_default_simple') : '' }}
</option>
<option value="masto">
{{ $t('settings.subject_line_mastodon') }}
{{ subjectLineBehaviorDefault == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
</option>
<option value="noop">
{{ $t('settings.subject_line_noop') }}
{{ subjectLineBehaviorDefault == 'noop' ? $t('settings.instance_default_simple') : '' }}
</option>
</select>
<i class="icon-down-open" />
</label>
</div>
</li>
<li>
<div>
{{ $t('settings.post_status_content_type') }}
<label
for="postContentType"
class="select"
>
<select
id="postContentType"
v-model="postContentTypeLocal"
>
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
{{ postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : '' }}
</option>
</select>
<i class="icon-down-open" />
</label>
</div>
</li>
<li>
<input
id="minimalScopesMode"
v-model="minimalScopesModeLocal"
type="checkbox"
>
<label for="minimalScopesMode">
{{ $t('settings.minimal_scopes_mode') }} {{ $t('settings.instance_default', { value: minimalScopesModeDefault }) }}
</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('settings.attachments') }}</h2>
<ul class="setting-list">
<li>
<input
id="hideAttachments"
v-model="hideAttachmentsLocal"
type="checkbox"
>
<label for="hideAttachments">{{ $t('settings.hide_attachments_in_tl') }}</label>
</li>
<li>
<input
id="hideAttachmentsInConv"
v-model="hideAttachmentsInConvLocal"
type="checkbox"
>
<label for="hideAttachmentsInConv">{{ $t('settings.hide_attachments_in_convo') }}</label>
</li>
<li>
<label for="maxThumbnails">{{ $t('settings.max_thumbnails') }}</label>
<input
id="maxThumbnails"
v-model.number="maxThumbnails"
class="number-input"
type="number"
min="0"
step="1"
>
</li>
<li>
<input
id="hideNsfw"
v-model="hideNsfwLocal"
type="checkbox"
>
<label for="hideNsfw">{{ $t('settings.nsfw_clickthrough') }}</label>
</li>
<ul class="setting-list suboptions">
<li>
<input
id="preloadImage"
v-model="preloadImage"
:disabled="!hideNsfwLocal"
type="checkbox"
>
<label for="preloadImage">{{ $t('settings.preload_images') }}</label>
</li>
<li>
<input
id="useOneClickNsfw"
v-model="useOneClickNsfw"
:disabled="!hideNsfwLocal"
type="checkbox"
>
<label for="useOneClickNsfw">{{ $t('settings.use_one_click_nsfw') }}</label>
</li>
</ul>
<li>
<input
id="stopGifs"
v-model="stopGifs"
type="checkbox"
>
<label for="stopGifs">{{ $t('settings.stop_gifs') }}</label>
</li>
<li>
<input
id="loopVideo"
v-model="loopVideoLocal"
type="checkbox"
>
<label for="loopVideo">{{ $t('settings.loop_video') }}</label>
<ul
class="setting-list suboptions"
:class="[{disabled: !streamingLocal}]"
>
<li>
<input
id="loopVideoSilentOnly"
v-model="loopVideoSilentOnlyLocal"
:disabled="!loopVideoLocal || !loopSilentAvailable"
type="checkbox"
>
<label for="loopVideoSilentOnly">{{ $t('settings.loop_video_silent_only') }}</label>
<div
v-if="!loopSilentAvailable"
class="unavailable"
>
<i class="icon-globe" />! {{ $t('settings.limited_availability') }}
</div>
</li>
</ul>
</li>
<li>
<input
id="playVideosInModal"
v-model="playVideosInModal"
type="checkbox"
>
<label for="playVideosInModal">{{ $t('settings.play_videos_in_modal') }}</label>
</li>
<li>
<input
id="useContainFit"
v-model="useContainFit"
type="checkbox"
>
<label for="useContainFit">{{ $t('settings.use_contain_fit') }}</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('settings.notifications') }}</h2>
<ul class="setting-list">
<li>
<input
id="webPushNotifications"
v-model="webPushNotificationsLocal"
type="checkbox"
>
<label for="webPushNotifications">
{{ $t('settings.enable_web_push_notifications') }}
</label>
</li>
</ul>
</div>
</div>
<div :label="$t('settings.theme')">
<div class="setting-item">
<style-switcher />
</div>
</div>
<div :label="$t('settings.filtering')">
<div class="setting-item">
<div class="select-multiple">
<span class="label">{{ $t('settings.notification_visibility') }}</span>
<ul class="option-list">
<li>
<input
id="notification-visibility-likes"
v-model="notificationVisibilityLocal.likes"
type="checkbox"
>
<label for="notification-visibility-likes">
{{ $t('settings.notification_visibility_likes') }}
</label>
</li>
<li>
<input
id="notification-visibility-repeats"
v-model="notificationVisibilityLocal.repeats"
type="checkbox"
>
<label for="notification-visibility-repeats">
{{ $t('settings.notification_visibility_repeats') }}
</label>
</li>
<li>
<input
id="notification-visibility-follows"
v-model="notificationVisibilityLocal.follows"
type="checkbox"
>
<label for="notification-visibility-follows">
{{ $t('settings.notification_visibility_follows') }}
</label>
</li>
<li>
<input
id="notification-visibility-mentions"
v-model="notificationVisibilityLocal.mentions"
type="checkbox"
>
<label for="notification-visibility-mentions">
{{ $t('settings.notification_visibility_mentions') }}
</label>
</li>
</ul>
</div>
<div>
{{ $t('settings.replies_in_timeline') }}
<label
for="replyVisibility"
class="select"
>
<select
id="replyVisibility"
v-model="replyVisibilityLocal"
>
<option
value="all"
selected
>{{ $t('settings.reply_visibility_all') }}</option>
<option value="following">{{ $t('settings.reply_visibility_following') }}</option>
<option value="self">{{ $t('settings.reply_visibility_self') }}</option>
</select>
<i class="icon-down-open" />
</label> </label>
</div> </li>
<div> <li>
<input <input type="checkbox" id="notification-visibility-repeats" v-model="notificationVisibilityLocal.repeats">
id="hidePostStats" <label for="notification-visibility-repeats">
v-model="hidePostStatsLocal" {{$t('settings.notification_visibility_repeats')}}
type="checkbox"
>
<label for="hidePostStats">
{{ $t('settings.hide_post_stats') }} {{ $t('settings.instance_default', { value: hidePostStatsDefault }) }}
</label> </label>
</div> </li>
<div> <li>
<input <input type="checkbox" id="notification-visibility-follows" v-model="notificationVisibilityLocal.follows">
id="hideUserStats" <label for="notification-visibility-follows">
v-model="hideUserStatsLocal" {{$t('settings.notification_visibility_follows')}}
type="checkbox"
>
<label for="hideUserStats">
{{ $t('settings.hide_user_stats') }} {{ $t('settings.instance_default', { value: hideUserStatsDefault }) }}
</label> </label>
</div> </li>
</div> <li>
<div class="setting-item"> <input type="checkbox" id="notification-visibility-mentions" v-model="notificationVisibilityLocal.mentions">
<div> <label for="notification-visibility-mentions">
<p>{{ $t('settings.filtering_explanation') }}</p> {{$t('settings.notification_visibility_mentions')}}
<textarea
id="muteWords"
v-model="muteWordsString"
/>
</div>
<div>
<input
id="hideFilteredStatuses"
v-model="hideFilteredStatusesLocal"
type="checkbox"
>
<label for="hideFilteredStatuses">
{{ $t('settings.hide_filtered_statuses') }} {{ $t('settings.instance_default', { value: hideFilteredStatusesDefault }) }}
</label> </label>
</div> </li>
</div> </ul>
</div> </div>
<div :label="$t('settings.version.title')"> <div>
<div class="setting-item"> {{$t('settings.replies_in_timeline')}}
<ul class="setting-list"> <label for="replyVisibility" class="select">
<select id="replyVisibility" v-model="replyVisibilityLocal">
<option value="all" selected>{{$t('settings.reply_visibility_all')}}</option>
<option value="following">{{$t('settings.reply_visibility_following')}}</option>
<option value="self">{{$t('settings.reply_visibility_self')}}</option>
</select>
<i class="icon-down-open"/>
</label>
</div>
<div>
<input type="checkbox" id="hidePostStats" v-model="hidePostStatsLocal">
<label for="hidePostStats">
{{$t('settings.hide_post_stats')}} {{$t('settings.instance_default', { value: hidePostStatsDefault })}}
</label>
</div>
<div>
<input type="checkbox" id="hideUserStats" v-model="hideUserStatsLocal">
<label for="hideUserStats">
{{$t('settings.hide_user_stats')}} {{$t('settings.instance_default', { value: hideUserStatsDefault })}}
</label>
</div>
</div>
<div class="setting-item">
<div>
<p>{{$t('settings.filtering_explanation')}}</p>
<textarea id="muteWords" v-model="muteWordsString"></textarea>
</div>
<div>
<input type="checkbox" id="hideFilteredStatuses" v-model="hideFilteredStatusesLocal">
<label for="hideFilteredStatuses">
{{$t('settings.hide_filtered_statuses')}} {{$t('settings.instance_default', { value: hideFilteredStatusesDefault })}}
</label>
</div>
</div>
</div>
<div :label="$t('settings.version.title')" >
<div class="setting-item">
<ul class="setting-list">
<li>
<p>{{$t('settings.version.backend_version')}}</p>
<ul class="option-list">
<li> <li>
<p>{{ $t('settings.version.backend_version') }}</p> <a :href="backendVersionLink" target="_blank">{{backendVersion}}</a>
<ul class="option-list">
<li>
<a
:href="backendVersionLink"
target="_blank"
>{{ backendVersion }}</a>
</li>
</ul>
</li>
<li>
<p>{{ $t('settings.version.frontend_version') }}</p>
<ul class="option-list">
<li>
<a
:href="frontendVersionLink"
target="_blank"
>{{ frontendVersion }}</a>
</li>
</ul>
</li> </li>
</ul> </ul>
</div> </li>
</div> <li>
</tab-switcher> <p>{{$t('settings.version.frontend_version')}}</p>
</keep-alive> <ul class="option-list">
</div> <li>
<a :href="frontendVersionLink" target="_blank">{{frontendVersion}}</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</tab-switcher>
</keep-alive>
</div> </div>
</div>
</template> </template>
<script src="./settings.js"> <script src="./settings.js">
@ -501,6 +327,7 @@
min-width: 10em; min-width: 10em;
} }
textarea { textarea {
width: 100%; width: 100%;
height: 100px; height: 100px;

View File

@ -1,207 +1,134 @@
<template> <template>
<div <div class="shadow-control" :class="{ disabled: !present }">
class="shadow-control" <div class="shadow-preview-container">
:class="{ disabled: !present }" <div :disabled="!present" class="y-shift-control">
> <input
<div class="shadow-preview-container"> v-model="selected.y"
<div
:disabled="!present" :disabled="!present"
class="y-shift-control" class="input-number"
> type="number">
<div class="wrap">
<input <input
v-model="selected.y" v-model="selected.y"
:disabled="!present" :disabled="!present"
class="input-number" class="input-range"
type="number" type="range"
> max="20"
<div class="wrap"> min="-20">
<input
v-model="selected.y"
:disabled="!present"
class="input-range"
type="range"
max="20"
min="-20"
>
</div>
</div> </div>
<div class="preview-window"> </div>
<div <div class="preview-window">
class="preview-block" <div class="preview-block" :style="style"></div>
:style="style" </div>
/> <div :disabled="!present" class="x-shift-control">
</div> <input
<div v-model="selected.x"
:disabled="!present" :disabled="!present"
class="x-shift-control" class="input-number"
> type="number">
<div class="wrap">
<input <input
v-model="selected.x" v-model="selected.x"
:disabled="!present" :disabled="!present"
class="input-number"
type="number"
>
<div class="wrap">
<input
v-model="selected.x"
:disabled="!present"
class="input-range"
type="range"
max="20"
min="-20"
>
</div>
</div>
</div>
<div class="shadow-tweak">
<div
:disabled="usingFallback"
class="id-control style-control"
>
<label
for="shadow-switcher"
class="select"
:disabled="!ready || usingFallback"
>
<select
id="shadow-switcher"
v-model="selectedId"
class="shadow-switcher"
:disabled="!ready || usingFallback"
>
<option
v-for="(shadow, index) in cValue"
:key="index"
:value="index"
>
{{ $t('settings.style.shadows.shadow_id', { value: index }) }}
</option>
</select>
<i class="icon-down-open" />
</label>
<button
class="btn btn-default"
:disabled="!ready || !present"
@click="del"
>
<i class="icon-cancel" />
</button>
<button
class="btn btn-default"
:disabled="!moveUpValid"
@click="moveUp"
>
<i class="icon-up-open" />
</button>
<button
class="btn btn-default"
:disabled="!moveDnValid"
@click="moveDn"
>
<i class="icon-down-open" />
</button>
<button
class="btn btn-default"
:disabled="usingFallback"
@click="add"
>
<i class="icon-plus" />
</button>
</div>
<div
:disabled="!present"
class="inset-control style-control"
>
<label
for="inset"
class="label"
>
{{ $t('settings.style.shadows.inset') }}
</label>
<input
id="inset"
v-model="selected.inset"
:disabled="!present"
name="inset"
class="input-inset"
type="checkbox"
>
<label
class="checkbox-label"
for="inset"
/>
</div>
<div
:disabled="!present"
class="blur-control style-control"
>
<label
for="spread"
class="label"
>
{{ $t('settings.style.shadows.blur') }}
</label>
<input
id="blur"
v-model="selected.blur"
:disabled="!present"
name="blur"
class="input-range" class="input-range"
type="range" type="range"
max="20" max="20"
min="0" min="-20">
>
<input
v-model="selected.blur"
:disabled="!present"
class="input-number"
type="number"
min="0"
>
</div> </div>
<div
:disabled="!present"
class="spread-control style-control"
>
<label
for="spread"
class="label"
>
{{ $t('settings.style.shadows.spread') }}
</label>
<input
id="spread"
v-model="selected.spread"
:disabled="!present"
name="spread"
class="input-range"
type="range"
max="20"
min="-20"
>
<input
v-model="selected.spread"
:disabled="!present"
class="input-number"
type="number"
>
</div>
<ColorInput
v-model="selected.color"
:disabled="!present"
:label="$t('settings.style.common.color')"
name="shadow"
/>
<OpacityInput
v-model="selected.alpha"
:disabled="!present"
/>
<p>
{{ $t('settings.style.shadows.hint') }}
</p>
</div> </div>
</div> </div>
<div class="shadow-tweak">
<div :disabled="usingFallback" class="id-control style-control">
<label for="shadow-switcher" class="select" :disabled="!ready || usingFallback">
<select
v-model="selectedId" class="shadow-switcher"
:disabled="!ready || usingFallback"
id="shadow-switcher">
<option v-for="(shadow, index) in cValue" :value="index">
{{$t('settings.style.shadows.shadow_id', { value: index })}}
</option>
</select>
<i class="icon-down-open"/>
</label>
<button class="btn btn-default" :disabled="!ready || !present" @click="del">
<i class="icon-cancel"/>
</button>
<button class="btn btn-default" :disabled="!moveUpValid" @click="moveUp">
<i class="icon-up-open"/>
</button>
<button class="btn btn-default" :disabled="!moveDnValid" @click="moveDn">
<i class="icon-down-open"/>
</button>
<button class="btn btn-default" :disabled="usingFallback" @click="add">
<i class="icon-plus"/>
</button>
</div>
<div :disabled="!present" class="inset-control style-control">
<label for="inset" class="label">
{{$t('settings.style.shadows.inset')}}
</label>
<input
v-model="selected.inset"
:disabled="!present"
name="inset"
id="inset"
class="input-inset"
type="checkbox">
<label class="checkbox-label" for="inset"></label>
</div>
<div :disabled="!present" class="blur-control style-control">
<label for="spread" class="label">
{{$t('settings.style.shadows.blur')}}
</label>
<input
v-model="selected.blur"
:disabled="!present"
name="blur"
id="blur"
class="input-range"
type="range"
max="20"
min="0">
<input
v-model="selected.blur"
:disabled="!present"
class="input-number"
type="number"
min="0">
</div>
<div :disabled="!present" class="spread-control style-control">
<label for="spread" class="label">
{{$t('settings.style.shadows.spread')}}
</label>
<input
v-model="selected.spread"
:disabled="!present"
name="spread"
id="spread"
class="input-range"
type="range"
max="20"
min="-20">
<input
v-model="selected.spread"
:disabled="!present"
class="input-number"
type="number">
</div>
<ColorInput
v-model="selected.color"
:disabled="!present"
:label="$t('settings.style.common.color')"
name="shadow"/>
<OpacityInput
v-model="selected.alpha"
:disabled="!present"/>
<p>
{{$t('settings.style.shadows.hint')}}
</p>
</div>
</div>
</template> </template>
<script src="./shadow_control.js" ></script> <script src="./shadow_control.js" ></script>

View File

@ -1,90 +1,58 @@
<template> <template>
<div <div class="side-drawer-container"
class="side-drawer-container"
:class="{ 'side-drawer-container-closed': closed, 'side-drawer-container-open': !closed }" :class="{ 'side-drawer-container-closed': closed, 'side-drawer-container-open': !closed }"
> >
<div <div class="side-drawer-darken" :class="{ 'side-drawer-darken-closed': closed}" />
class="side-drawer-darken" <div class="side-drawer"
:class="{ 'side-drawer-darken-closed': closed}"
/>
<div
class="side-drawer"
:class="{'side-drawer-closed': closed}" :class="{'side-drawer-closed': closed}"
@touchstart="touchStart" @touchstart="touchStart"
@touchmove="touchMove" @touchmove="touchMove"
> >
<div <div class="side-drawer-heading" @click="toggleDrawer">
class="side-drawer-heading" <UserCard :user="currentUser" :hideBio="true" v-if="currentUser"/>
@click="toggleDrawer" <div class="side-drawer-logo-wrapper" v-else>
> <img :src="logo"/>
<UserCard <span>{{sitename}}</span>
v-if="currentUser"
:user="currentUser"
:hide-bio="true"
/>
<div
v-else
class="side-drawer-logo-wrapper"
>
<img :src="logo">
<span>{{ sitename }}</span>
</div> </div>
</div> </div>
<ul> <ul>
<li <li v-if="!currentUser" @click="toggleDrawer">
v-if="!currentUser"
@click="toggleDrawer"
>
<router-link :to="{ name: 'login' }"> <router-link :to="{ name: 'login' }">
{{ $t("login.login") }} {{ $t("login.login") }}
</router-link> </router-link>
</li> </li>
<li <li v-if="currentUser" @click="toggleDrawer">
v-if="currentUser"
@click="toggleDrawer"
>
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
{{ $t("nav.dms") }} {{ $t("nav.dms") }}
</router-link> </router-link>
</li> </li>
</ul> </ul>
<ul> <ul>
<li <li v-if="currentUser" @click="toggleDrawer">
v-if="currentUser"
@click="toggleDrawer"
>
<router-link :to="{ name: 'friends' }"> <router-link :to="{ name: 'friends' }">
{{ $t("nav.timeline") }} {{ $t("nav.timeline") }}
</router-link> </router-link>
</li> </li>
<li <li v-if="currentUser && currentUser.locked" @click="toggleDrawer">
v-if="currentUser && currentUser.locked" <router-link to='/friend-requests'>
@click="toggleDrawer"
>
<router-link to="/friend-requests">
{{ $t("nav.friend_requests") }} {{ $t("nav.friend_requests") }}
<span <span v-if='followRequestCount > 0' class="badge follow-request-count">
v-if="followRequestCount > 0" {{followRequestCount}}
class="badge follow-request-count"
>
{{ followRequestCount }}
</span> </span>
</router-link> </router-link>
</li> </li>
<li @click="toggleDrawer"> <li @click="toggleDrawer">
<router-link to="/main/public"> <router-link to='/main/public'>
{{ $t("nav.public_tl") }} {{ $t("nav.public_tl") }}
</router-link> </router-link>
</li> </li>
<li @click="toggleDrawer"> <li @click="toggleDrawer">
<router-link to="/main/all"> <router-link to='/main/all'>
{{ $t("nav.twkn") }} {{ $t("nav.twkn") }}
</router-link> </router-link>
</li> </li>
<li <li v-if="currentUser && chat" @click="toggleDrawer">
v-if="currentUser && chat"
@click="toggleDrawer"
>
<router-link :to="{ name: 'chat' }"> <router-link :to="{ name: 'chat' }">
{{ $t("nav.chat") }} {{ $t("nav.chat") }}
</router-link> </router-link>
@ -96,10 +64,7 @@
{{ $t("nav.user_search") }} {{ $t("nav.user_search") }}
</router-link> </router-link>
</li> </li>
<li <li v-if="currentUser && suggestionsEnabled" @click="toggleDrawer">
v-if="currentUser && suggestionsEnabled"
@click="toggleDrawer"
>
<router-link :to="{ name: 'who-to-follow' }"> <router-link :to="{ name: 'who-to-follow' }">
{{ $t("nav.who_to_follow") }} {{ $t("nav.who_to_follow") }}
</router-link> </router-link>
@ -114,24 +79,17 @@
{{ $t("nav.about") }} {{ $t("nav.about") }}
</router-link> </router-link>
</li> </li>
<li <li v-if="currentUser" @click="toggleDrawer">
v-if="currentUser" <a @click="doLogout" href="#">
@click="toggleDrawer"
>
<a
href="#"
@click="doLogout"
>
{{ $t("login.logout") }} {{ $t("login.logout") }}
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div <div class="side-drawer-click-outside"
class="side-drawer-click-outside"
:class="{'side-drawer-click-outside-closed': closed}"
@click.stop.prevent="toggleDrawer" @click.stop.prevent="toggleDrawer"
/> :class="{'side-drawer-click-outside-closed': closed}"
></div>
</div> </div>
</template> </template>

View File

@ -210,10 +210,10 @@ const Status = {
if (!this.status.summary) return '' if (!this.status.summary) return ''
const decodedSummary = unescape(this.status.summary) const decodedSummary = unescape(this.status.summary)
const behavior = typeof this.$store.state.config.subjectLineBehavior === 'undefined' const behavior = typeof this.$store.state.config.subjectLineBehavior === 'undefined'
? this.$store.state.instance.subjectLineBehavior ? this.$store.state.instance.subjectLineBehavior
: this.$store.state.config.subjectLineBehavior : this.$store.state.config.subjectLineBehavior
const startsWithRe = decodedSummary.match(/^re[: ]/i) const startsWithRe = decodedSummary.match(/^re[: ]/i)
if ((behavior !== 'noop' && startsWithRe) || behavior === 'masto') { if (behavior !== 'noop' && startsWithRe || behavior === 'masto') {
return decodedSummary return decodedSummary
} else if (behavior === 'email') { } else if (behavior === 'email') {
return 're: '.concat(decodedSummary) return 're: '.concat(decodedSummary)
@ -350,7 +350,7 @@ const Status = {
this.preview = find(statuses, { 'id': targetId }) this.preview = find(statuses, { 'id': targetId })
// or if we have to fetch it // or if we have to fetch it
if (!this.preview) { if (!this.preview) {
this.$store.state.api.backendInteractor.fetchStatus({ id }).then((status) => { this.$store.state.api.backendInteractor.fetchStatus({id}).then((status) => {
this.preview = status this.preview = status
}) })
} }

View File

@ -1,366 +1,152 @@
<template> <template>
<div <div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
v-if="!hideStatus"
class="status-el"
:class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"
>
<template v-if="muted && !isPreview"> <template v-if="muted && !isPreview">
<div class="media status container muted"> <div class="media status container muted">
<small> <small>
<router-link :to="userProfileLink"> <router-link :to="userProfileLink">
{{ status.user.screen_name }} {{status.user.screen_name}}
</router-link> </router-link>
</small> </small>
<small class="muteWords">{{ muteWordHits.join(', ') }}</small> <small class="muteWords">{{muteWordHits.join(', ')}}</small>
<a <a href="#" class="unmute" @click.prevent="toggleMute"><i class="button-icon icon-eye-off"></i></a>
href="#"
class="unmute"
@click.prevent="toggleMute"
><i class="button-icon icon-eye-off" /></a>
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div <div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
v-if="retweet && !noHeading && !inConversation" <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/>
:class="[repeaterClass, { highlighted: repeaterStyle }]"
:style="[repeaterStyle]"
class="media container retweet-info"
>
<UserAvatar
v-if="retweet"
class="media-left"
:better-shadow="betterShadow"
:src="statusoid.user.profile_image_url_original"
/>
<div class="media-body faint"> <div class="media-body faint">
<span class="user-name"> <span class="user-name">
<router-link <router-link v-if="retweeterHtml" :to="retweeterProfileLink" v-html="retweeterHtml"/>
v-if="retweeterHtml" <router-link v-else :to="retweeterProfileLink">{{retweeter}}</router-link>
:to="retweeterProfileLink"
v-html="retweeterHtml"
/>
<router-link
v-else
:to="retweeterProfileLink"
>{{ retweeter }}</router-link>
</span> </span>
<i <i class='fa icon-retweet retweeted' :title="$t('tool_tip.repeat')"></i>
class="fa icon-retweet retweeted" {{$t('timeline.repeated')}}
:title="$t('tool_tip.repeat')"
/>
{{ $t('timeline.repeated') }}
</div> </div>
</div> </div>
<div <div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet && !inConversation }]" :style="[ userStyle ]" class="media status">
:class="[userClass, { highlighted: userStyle, 'is-retweet': retweet && !inConversation }]" <div v-if="!noHeading" class="media-left">
:style="[ userStyle ]" <router-link :to="userProfileLink" @click.stop.prevent.capture.native="toggleUserExpanded">
class="media status" <UserAvatar :compact="compact" :betterShadow="betterShadow" :src="status.user.profile_image_url_original"/>
>
<div
v-if="!noHeading"
class="media-left"
>
<router-link
:to="userProfileLink"
@click.stop.prevent.capture.native="toggleUserExpanded"
>
<UserAvatar
:compact="compact"
:better-shadow="betterShadow"
:src="status.user.profile_image_url_original"
/>
</router-link> </router-link>
</div> </div>
<div class="status-body"> <div class="status-body">
<UserCard <UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard" v-if="userExpanded"/>
v-if="userExpanded" <div v-if="!noHeading" class="media-heading">
:user="status.user"
:rounded="true"
:bordered="true"
class="status-usercard"
/>
<div
v-if="!noHeading"
class="media-heading"
>
<div class="heading-name-row"> <div class="heading-name-row">
<div class="name-and-account-name"> <div class="name-and-account-name">
<h4 <h4 class="user-name" v-if="status.user.name_html" v-html="status.user.name_html"></h4>
v-if="status.user.name_html" <h4 class="user-name" v-else>{{status.user.name}}</h4>
class="user-name" <router-link class="account-name" :to="userProfileLink">
v-html="status.user.name_html" {{status.user.screen_name}}
/>
<h4
v-else
class="user-name"
>
{{ status.user.name }}
</h4>
<router-link
class="account-name"
:to="userProfileLink"
>
{{ status.user.screen_name }}
</router-link> </router-link>
</div> </div>
<span class="heading-right"> <span class="heading-right">
<router-link <router-link class="timeago faint-link" :to="{ name: 'conversation', params: { id: status.id } }">
class="timeago faint-link" <timeago :since="status.created_at" :auto-update="60"></timeago>
:to="{ name: 'conversation', params: { id: status.id } }"
>
<timeago
:since="status.created_at"
:auto-update="60"
/>
</router-link> </router-link>
<div <div class="button-icon visibility-icon" v-if="status.visibility">
v-if="status.visibility" <i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i>
class="button-icon visibility-icon"
>
<i
:class="visibilityIcon(status.visibility)"
:title="status.visibility | capitalize"
/>
</div> </div>
<a <a :href="status.external_url" target="_blank" v-if="!status.is_local && !isPreview" class="source_url" title="Source">
v-if="!status.is_local && !isPreview" <i class="button-icon icon-link-ext-alt"></i>
:href="status.external_url"
target="_blank"
class="source_url"
title="Source"
>
<i class="button-icon icon-link-ext-alt" />
</a> </a>
<template v-if="expandable && !isPreview"> <template v-if="expandable && !isPreview">
<a <a href="#" @click.prevent="toggleExpanded" title="Expand">
href="#" <i class="button-icon icon-plus-squared"></i>
title="Expand"
@click.prevent="toggleExpanded"
>
<i class="button-icon icon-plus-squared" />
</a> </a>
</template> </template>
<a <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="button-icon icon-eye-off"></i></a>
v-if="unmuted"
href="#"
@click.prevent="toggleMute"
><i class="button-icon icon-eye-off" /></a>
</span> </span>
</div> </div>
<div class="heading-reply-row"> <div class="heading-reply-row">
<div <div v-if="isReply" class="reply-to-and-accountname">
v-if="isReply" <a class="reply-to"
class="reply-to-and-accountname" href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"
>
<a
class="reply-to"
href="#"
:aria-label="$t('tool_tip.reply')" :aria-label="$t('tool_tip.reply')"
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
@mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)" @mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)"
@mouseleave.prevent.stop="replyLeave()" @mouseleave.prevent.stop="replyLeave()"
> >
<i <i class="button-icon icon-reply" v-if="!isPreview"></i>
v-if="!isPreview" <span class="faint-link reply-to-text">{{$t('status.reply_to')}}</span>
class="button-icon icon-reply"
/>
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
</a> </a>
<router-link :to="replyProfileLink"> <router-link :to="replyProfileLink">
{{ replyToName }} {{replyToName}}
</router-link> </router-link>
<span <span class="faint replies-separator" v-if="replies && replies.length">
v-if="replies && replies.length"
class="faint replies-separator"
>
- -
</span> </span>
</div> </div>
<div <div class="replies" v-if="inConversation && !isPreview">
v-if="inConversation && !isPreview" <span class="faint" v-if="replies && replies.length">{{$t('status.replies_list')}}</span>
class="replies" <span class="reply-link faint" v-if="replies" v-for="reply in replies">
> <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}</a>
<span </span>
v-if="replies && replies.length"
class="faint"
>{{ $t('status.replies_list') }}</span>
<template
v-if="replies"
>
<span
v-for="reply in replies"
:key="reply.name"
class="reply-link faint"
>
<a
href="#"
@click.prevent="gotoOriginal(reply.id)"
@mouseenter="replyEnter(reply.id, $event)"
@mouseout="replyLeave()"
>{{ reply.name }}</a>
</span>
</template>
</div> </div>
</div> </div>
</div> </div>
<div <div v-if="showPreview" class="status-preview-container">
v-if="showPreview" <status class="status-preview" v-if="preview" :isPreview="true" :statusoid="preview" :compact=true></status>
class="status-preview-container" <div class="status-preview status-preview-loading" v-else>
> <i class="icon-spin4 animate-spin"></i>
<status
v-if="preview"
class="status-preview"
:is-preview="true"
:statusoid="preview"
:compact="true"
/>
<div
v-else
class="status-preview status-preview-loading"
>
<i class="icon-spin4 animate-spin" />
</div> </div>
</div> </div>
<div <div class="status-content-wrapper" :class="{ 'tall-status': !showingLongSubject }" v-if="longSubject">
v-if="longSubject" <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="!showingLongSubject" href="#" @click.prevent="showingLongSubject=true">{{$t("general.show_more")}}</a>
class="status-content-wrapper" <div @click.prevent="linkClicked" class="status-content media-body" v-html="contentHtml"></div>
:class="{ 'tall-status': !showingLongSubject }" <a v-if="showingLongSubject" href="#" class="status-unhider" @click.prevent="showingLongSubject=false">{{$t("general.show_less")}}</a>
>
<a
v-if="!showingLongSubject"
class="tall-status-hider"
:class="{ 'tall-status-hider_focused': isFocused }"
href="#"
@click.prevent="showingLongSubject=true"
>{{ $t("general.show_more") }}</a>
<div
class="status-content media-body"
@click.prevent="linkClicked"
v-html="contentHtml"
/>
<a
v-if="showingLongSubject"
href="#"
class="status-unhider"
@click.prevent="showingLongSubject=false"
>{{ $t("general.show_less") }}</a>
</div> </div>
<div <div :class="{'tall-status': hideTallStatus}" class="status-content-wrapper" v-else>
v-else <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="hideTallStatus" href="#" @click.prevent="toggleShowMore">{{$t("general.show_more")}}</a>
:class="{'tall-status': hideTallStatus}" <div @click.prevent="linkClicked" class="status-content media-body" v-html="contentHtml" v-if="!hideSubjectStatus"></div>
class="status-content-wrapper" <div @click.prevent="linkClicked" class="status-content media-body" v-html="status.summary_html" v-else></div>
> <a v-if="hideSubjectStatus" href="#" class="cw-status-hider" @click.prevent="toggleShowMore">{{$t("general.show_more")}}</a>
<a <a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">{{$t("general.show_less")}}</a>
v-if="hideTallStatus"
class="tall-status-hider"
:class="{ 'tall-status-hider_focused': isFocused }"
href="#"
@click.prevent="toggleShowMore"
>{{ $t("general.show_more") }}</a>
<div
v-if="!hideSubjectStatus"
class="status-content media-body"
@click.prevent="linkClicked"
v-html="contentHtml"
/>
<div
v-else
class="status-content media-body"
@click.prevent="linkClicked"
v-html="status.summary_html"
/>
<a
v-if="hideSubjectStatus"
href="#"
class="cw-status-hider"
@click.prevent="toggleShowMore"
>{{ $t("general.show_more") }}</a>
<a
v-if="showingMore"
href="#"
class="status-unhider"
@click.prevent="toggleShowMore"
>{{ $t("general.show_less") }}</a>
</div> </div>
<div <div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)"
class="attachments media-body"
>
<attachment <attachment
v-for="attachment in nonGalleryAttachments"
:key="attachment.id"
class="non-gallery" class="non-gallery"
v-for="attachment in nonGalleryAttachments"
:size="attachmentSize" :size="attachmentSize"
:nsfw="nsfwClickthrough" :nsfw="nsfwClickthrough"
:attachment="attachment" :attachment="attachment"
:allow-play="true" :allowPlay="true"
:set-media="setMedia()" :setMedia="setMedia()"
:key="attachment.id"
/> />
<gallery <gallery
v-if="galleryAttachments.length > 0" v-if="galleryAttachments.length > 0"
:nsfw="nsfwClickthrough" :nsfw="nsfwClickthrough"
:attachments="galleryAttachments" :attachments="galleryAttachments"
:set-media="setMedia()" :setMedia="setMedia()"
/> />
</div> </div>
<div <div v-if="status.card && !hideSubjectStatus && !noHeading" class="link-preview media-body">
v-if="status.card && !hideSubjectStatus && !noHeading" <link-preview :card="status.card" :size="attachmentSize" :nsfw="nsfwClickthrough" />
class="link-preview media-body"
>
<link-preview
:card="status.card"
:size="attachmentSize"
:nsfw="nsfwClickthrough"
/>
</div> </div>
<div <div v-if="!noHeading && !isPreview" class='status-actions media-body'>
v-if="!noHeading && !isPreview"
class="status-actions media-body"
>
<div v-if="loggedIn"> <div v-if="loggedIn">
<i <i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'icon-reply-active': replying}"></i>
class="button-icon icon-reply" <span v-if="status.replies_count > 0">{{status.replies_count}}</span>
:title="$t('tool_tip.reply')"
:class="{'icon-reply-active': replying}"
@click.prevent="toggleReplying"
/>
<span v-if="status.replies_count > 0">{{ status.replies_count }}</span>
</div> </div>
<retweet-button <retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button>
:visibility="status.visibility" <favorite-button :loggedIn='loggedIn' :status='status'></favorite-button>
:logged-in="loggedIn" <delete-button :status='status'></delete-button>
:status="status"
/>
<favorite-button
:logged-in="loggedIn"
:status="status"
/>
<delete-button :status="status" />
</div> </div>
</div> </div>
</div> </div>
<div <div class="container" v-if="replying">
v-if="replying" <div class="reply-left"/>
class="container" <post-status-form class="reply-body" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" :copy-message-scope="status.visibility" :subject="replySubject" v-on:posted="toggleReplying"/>
>
<div class="reply-left" />
<post-status-form
class="reply-body"
:reply-to="status.id"
:attentions="status.attentions"
:replied-user="status.user"
:copy-message-scope="status.visibility"
:subject="replySubject"
@posted="toggleReplying"
/>
</div> </div>
</template> </template>
</div> </div>

View File

@ -1,19 +1,7 @@
<template> <template>
<div <div class='still-image' :class='{ animated: animated }' >
class="still-image" <canvas ref="canvas" v-if="animated"></canvas>
:class="{ animated: animated }" <img ref="src" :src="src" :referrerpolicy="referrerpolicy" v-on:load="onLoad" @error="onError"/>
>
<canvas
v-if="animated"
ref="canvas"
/>
<img
ref="src"
:src="src"
:referrerpolicy="referrerpolicy"
@load="onLoad"
@error="onError"
>
</div> </div>
</template> </template>

View File

@ -1,101 +1,78 @@
<template> <template>
<div class="panel dummy"> <div class="panel dummy">
<div class="panel-heading"> <div class="panel-heading">
<div class="title"> <div class="title">
{{ $t('settings.style.preview.header') }} {{$t('settings.style.preview.header')}}
<span class="badge badge-notification"> <span class="badge badge-notification">
99 99
</span>
</div>
<span class="faint">
{{ $t('settings.style.preview.header_faint') }}
</span> </span>
<span class="alert error"> </div>
{{ $t('settings.style.preview.error') }} <span class="faint">
{{$t('settings.style.preview.header_faint')}}
</span>
<span class="alert error">
{{$t('settings.style.preview.error')}}
</span>
<button class="btn">
{{$t('settings.style.preview.button')}}
</button>
</div>
<div class="panel-body theme-preview-content">
<div class="post">
<div class="avatar">
( ͡° ͜ʖ ͡°)
</div>
<div class="content">
<h4>
{{$t('settings.style.preview.content')}}
</h4>
<i18n path="settings.style.preview.text">
<code style="font-family: var(--postCodeFont)">
{{$t('settings.style.preview.mono')}}
</code>
<a style="color: var(--link)">
{{$t('settings.style.preview.link')}}
</a>
</i18n>
<div class="icons">
<i style="color: var(--cBlue)" class="button-icon icon-reply"/>
<i style="color: var(--cGreen)" class="button-icon icon-retweet"/>
<i style="color: var(--cOrange)" class="button-icon icon-star"/>
<i style="color: var(--cRed)" class="button-icon icon-cancel"/>
</div>
</div>
</div>
<div class="after-post">
<div class="avatar-alt">
:^)
</div>
<div class="content">
<i18n path="settings.style.preview.fine_print" tag="span" class="faint">
<a style="color: var(--faintLink)">
{{$t('settings.style.preview.faint_link')}}
</a>
</i18n>
</div>
</div>
<div class="separator"></div>
<span class="alert error">
{{$t('settings.style.preview.error')}}
</span>
<input :value="$t('settings.style.preview.input')" type="text">
<div class="actions">
<span class="checkbox">
<input checked="very yes" type="checkbox" id="preview_checkbox">
<label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label>
</span> </span>
<button class="btn"> <button class="btn">
{{ $t('settings.style.preview.button') }} {{$t('settings.style.preview.button')}}
</button> </button>
</div> </div>
<div class="panel-body theme-preview-content">
<div class="post">
<div class="avatar">
( ͡° ͜ʖ ͡°)
</div>
<div class="content">
<h4>
{{ $t('settings.style.preview.content') }}
</h4>
<i18n path="settings.style.preview.text">
<code style="font-family: var(--postCodeFont)">
{{ $t('settings.style.preview.mono') }}
</code>
<a style="color: var(--link)">
{{ $t('settings.style.preview.link') }}
</a>
</i18n>
<div class="icons">
<i
style="color: var(--cBlue)"
class="button-icon icon-reply"
/>
<i
style="color: var(--cGreen)"
class="button-icon icon-retweet"
/>
<i
style="color: var(--cOrange)"
class="button-icon icon-star"
/>
<i
style="color: var(--cRed)"
class="button-icon icon-cancel"
/>
</div>
</div>
</div>
<div class="after-post">
<div class="avatar-alt">
:^)
</div>
<div class="content">
<i18n
path="settings.style.preview.fine_print"
tag="span"
class="faint"
>
<a style="color: var(--faintLink)">
{{ $t('settings.style.preview.faint_link') }}
</a>
</i18n>
</div>
</div>
<div class="separator" />
<span class="alert error">
{{ $t('settings.style.preview.error') }}
</span>
<input
:value="$t('settings.style.preview.input')"
type="text"
>
<div class="actions">
<span class="checkbox">
<input
id="preview_checkbox"
checked="very yes"
type="checkbox"
>
<label for="preview_checkbox">{{ $t('settings.style.preview.checkbox') }}</label>
</span>
<button class="btn">
{{ $t('settings.style.preview.button') }}
</button>
</div>
</div>
</div> </div>
</div>
</template> </template>

View File

@ -1,593 +1,274 @@
<template> <template>
<div class="style-switcher"> <div class="style-switcher">
<div class="presets-container"> <div class="presets-container">
<div class="save-load"> <div class="save-load">
<export-import <export-import
:export-object="exportedTheme" :exportObject='exportedTheme'
:export-label="$t(&quot;settings.export_theme&quot;)" :exportLabel='$t("settings.export_theme")'
:import-label="$t(&quot;settings.import_theme&quot;)" :importLabel='$t("settings.import_theme")'
:import-failed-text="$t(&quot;settings.invalid_theme_imported&quot;)" :importFailedText='$t("settings.invalid_theme_imported")'
:on-import="onImport" :onImport='onImport'
:validator="importValidator" :validator='importValidator'>
> <template slot="before">
<template slot="before"> <div class="presets">
<div class="presets"> {{$t('settings.presets')}}
{{ $t('settings.presets') }} <label for="preset-switcher" class='select'>
<label <select id="preset-switcher" v-model="selected" class="preset-switcher">
for="preset-switcher" <option v-for="style in availableStyles"
class="select" :value="style"
> :style="{
<select backgroundColor: style[1] || style.theme.colors.bg,
id="preset-switcher" color: style[3] || style.theme.colors.text
v-model="selected" }">
class="preset-switcher" {{style[0] || style.name}}
> </option>
<option </select>
v-for="style in availableStyles" <i class="icon-down-open"/>
:key="style.name" </label>
:value="style" </div>
:style="{ </template>
backgroundColor: style[1] || style.theme.colors.bg, </export-import>
color: style[3] || style.theme.colors.text
}"
>
{{ style[0] || style.name }}
</option>
</select>
<i class="icon-down-open" />
</label>
</div>
</template>
</export-import>
</div>
<div class="save-load-options">
<span class="keep-option">
<input
id="keep-color"
v-model="keepColor"
type="checkbox"
>
<label for="keep-color">{{ $t('settings.style.switcher.keep_color') }}</label>
</span>
<span class="keep-option">
<input
id="keep-shadows"
v-model="keepShadows"
type="checkbox"
>
<label for="keep-shadows">{{ $t('settings.style.switcher.keep_shadows') }}</label>
</span>
<span class="keep-option">
<input
id="keep-opacity"
v-model="keepOpacity"
type="checkbox"
>
<label for="keep-opacity">{{ $t('settings.style.switcher.keep_opacity') }}</label>
</span>
<span class="keep-option">
<input
id="keep-roundness"
v-model="keepRoundness"
type="checkbox"
>
<label for="keep-roundness">{{ $t('settings.style.switcher.keep_roundness') }}</label>
</span>
<span class="keep-option">
<input
id="keep-fonts"
v-model="keepFonts"
type="checkbox"
>
<label for="keep-fonts">{{ $t('settings.style.switcher.keep_fonts') }}</label>
</span>
<p>{{ $t('settings.style.switcher.save_load_hint') }}</p>
</div>
</div> </div>
<div class="save-load-options">
<div class="preview-container"> <span class="keep-option">
<preview :style="previewRules" /> <input
</div> id="keep-color"
type="checkbox"
<keep-alive> v-model="keepColor">
<tab-switcher key="style-tweak"> <label for="keep-color">{{$t('settings.style.switcher.keep_color')}}</label>
<div </span>
:label="$t('settings.style.common_colors._tab_label')" <span class="keep-option">
class="color-container" <input
> id="keep-shadows"
<div class="tab-header"> type="checkbox"
<p>{{ $t('settings.theme_help') }}</p> v-model="keepShadows">
<button <label for="keep-shadows">{{$t('settings.style.switcher.keep_shadows')}}</label>
class="btn" </span>
@click="clearOpacity" <span class="keep-option">
> <input
{{ $t('settings.style.switcher.clear_opacity') }} id="keep-opacity"
</button> type="checkbox"
<button v-model="keepOpacity">
class="btn" <label for="keep-opacity">{{$t('settings.style.switcher.keep_opacity')}}</label>
@click="clearV1" </span>
> <span class="keep-option">
{{ $t('settings.style.switcher.clear_all') }} <input
</button> id="keep-roundness"
</div> type="checkbox"
<p>{{ $t('settings.theme_help_v2_1') }}</p> v-model="keepRoundness">
<h4>{{ $t('settings.style.common_colors.main') }}</h4> <label for="keep-roundness">{{$t('settings.style.switcher.keep_roundness')}}</label>
<div class="color-item"> </span>
<ColorInput <span class="keep-option">
v-model="bgColorLocal" <input
name="bgColor" id="keep-fonts"
:label="$t('settings.background')" type="checkbox"
/> v-model="keepFonts">
<OpacityInput <label for="keep-fonts">{{$t('settings.style.switcher.keep_fonts')}}</label>
v-model="bgOpacityLocal" </span>
name="bgOpacity" <p>{{$t('settings.style.switcher.save_load_hint')}}</p>
:fallback="previewTheme.opacity.bg || 1"
/>
<ColorInput
v-model="textColorLocal"
name="textColor"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.bgText" />
<ColorInput
v-model="linkColorLocal"
name="linkColor"
:label="$t('settings.links')"
/>
<ContrastRatio :contrast="previewContrast.bgLink" />
</div>
<div class="color-item">
<ColorInput
v-model="fgColorLocal"
name="fgColor"
:label="$t('settings.foreground')"
/>
<ColorInput
v-model="fgTextColorLocal"
name="fgTextColor"
:label="$t('settings.text')"
:fallback="previewTheme.colors.fgText"
/>
<ColorInput
v-model="fgLinkColorLocal"
name="fgLinkColor"
:label="$t('settings.links')"
:fallback="previewTheme.colors.fgLink"
/>
<p>{{ $t('settings.style.common_colors.foreground_hint') }}</p>
</div>
<h4>{{ $t('settings.style.common_colors.rgbo') }}</h4>
<div class="color-item">
<ColorInput
v-model="cRedColorLocal"
name="cRedColor"
:label="$t('settings.cRed')"
/>
<ContrastRatio :contrast="previewContrast.bgRed" />
<ColorInput
v-model="cBlueColorLocal"
name="cBlueColor"
:label="$t('settings.cBlue')"
/>
<ContrastRatio :contrast="previewContrast.bgBlue" />
</div>
<div class="color-item">
<ColorInput
v-model="cGreenColorLocal"
name="cGreenColor"
:label="$t('settings.cGreen')"
/>
<ContrastRatio :contrast="previewContrast.bgGreen" />
<ColorInput
v-model="cOrangeColorLocal"
name="cOrangeColor"
:label="$t('settings.cOrange')"
/>
<ContrastRatio :contrast="previewContrast.bgOrange" />
</div>
<p>{{ $t('settings.theme_help_v2_2') }}</p>
</div>
<div
:label="$t('settings.style.advanced_colors._tab_label')"
class="color-container"
>
<div class="tab-header">
<p>{{ $t('settings.theme_help') }}</p>
<button
class="btn"
@click="clearOpacity"
>
{{ $t('settings.style.switcher.clear_opacity') }}
</button>
<button
class="btn"
@click="clearV1"
>
{{ $t('settings.style.switcher.clear_all') }}
</button>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.alert') }}</h4>
<ColorInput
v-model="alertErrorColorLocal"
name="alertError"
:label="$t('settings.style.advanced_colors.alert_error')"
:fallback="previewTheme.colors.alertError"
/>
<ContrastRatio :contrast="previewContrast.alertError" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.badge') }}</h4>
<ColorInput
v-model="badgeNotificationColorLocal"
name="badgeNotification"
:label="$t('settings.style.advanced_colors.badge_notification')"
:fallback="previewTheme.colors.badgeNotification"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4>
<ColorInput
v-model="panelColorLocal"
name="panelColor"
:fallback="fgColorLocal"
:label="$t('settings.background')"
/>
<OpacityInput
v-model="panelOpacityLocal"
name="panelOpacity"
:fallback="previewTheme.opacity.panel || 1"
/>
<ColorInput
v-model="panelTextColorLocal"
name="panelTextColor"
:fallback="previewTheme.colors.panelText"
:label="$t('settings.text')"
/>
<ContrastRatio
:contrast="previewContrast.panelText"
large="1"
/>
<ColorInput
v-model="panelLinkColorLocal"
name="panelLinkColor"
:fallback="previewTheme.colors.panelLink"
:label="$t('settings.links')"
/>
<ContrastRatio
:contrast="previewContrast.panelLink"
large="1"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.top_bar') }}</h4>
<ColorInput
v-model="topBarColorLocal"
name="topBarColor"
:fallback="fgColorLocal"
:label="$t('settings.background')"
/>
<ColorInput
v-model="topBarTextColorLocal"
name="topBarTextColor"
:fallback="previewTheme.colors.topBarText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.topBarText" />
<ColorInput
v-model="topBarLinkColorLocal"
name="topBarLinkColor"
:fallback="previewTheme.colors.topBarLink"
:label="$t('settings.links')"
/>
<ContrastRatio :contrast="previewContrast.topBarLink" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.inputs') }}</h4>
<ColorInput
v-model="inputColorLocal"
name="inputColor"
:fallback="fgColorLocal"
:label="$t('settings.background')"
/>
<OpacityInput
v-model="inputOpacityLocal"
name="inputOpacity"
:fallback="previewTheme.opacity.input || 1"
/>
<ColorInput
v-model="inputTextColorLocal"
name="inputTextColor"
:fallback="previewTheme.colors.inputText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.inputText" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.buttons') }}</h4>
<ColorInput
v-model="btnColorLocal"
name="btnColor"
:fallback="fgColorLocal"
:label="$t('settings.background')"
/>
<OpacityInput
v-model="btnOpacityLocal"
name="btnOpacity"
:fallback="previewTheme.opacity.btn || 1"
/>
<ColorInput
v-model="btnTextColorLocal"
name="btnTextColor"
:fallback="previewTheme.colors.btnText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.btnText" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.borders') }}</h4>
<ColorInput
v-model="borderColorLocal"
name="borderColor"
:fallback="previewTheme.colors.border"
:label="$t('settings.style.common.color')"
/>
<OpacityInput
v-model="borderOpacityLocal"
name="borderOpacity"
:fallback="previewTheme.opacity.border || 1"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.faint_text') }}</h4>
<ColorInput
v-model="faintColorLocal"
name="faintColor"
:fallback="previewTheme.colors.faint || 1"
:label="$t('settings.text')"
/>
<ColorInput
v-model="faintLinkColorLocal"
name="faintLinkColor"
:fallback="previewTheme.colors.faintLink"
:label="$t('settings.links')"
/>
<ColorInput
v-model="panelFaintColorLocal"
name="panelFaintColor"
:fallback="previewTheme.colors.panelFaint"
:label="$t('settings.style.advanced_colors.panel_header')"
/>
<OpacityInput
v-model="faintOpacityLocal"
name="faintOpacity"
:fallback="previewTheme.opacity.faint || 0.5"
/>
</div>
</div>
<div
:label="$t('settings.style.radii._tab_label')"
class="radius-container"
>
<div class="tab-header">
<p>{{ $t('settings.radii_help') }}</p>
<button
class="btn"
@click="clearRoundness"
>
{{ $t('settings.style.switcher.clear_all') }}
</button>
</div>
<RangeInput
v-model="btnRadiusLocal"
name="btnRadius"
:label="$t('settings.btnRadius')"
:fallback="previewTheme.radii.btn"
max="16"
hard-min="0"
/>
<RangeInput
v-model="inputRadiusLocal"
name="inputRadius"
:label="$t('settings.inputRadius')"
:fallback="previewTheme.radii.input"
max="9"
hard-min="0"
/>
<RangeInput
v-model="checkboxRadiusLocal"
name="checkboxRadius"
:label="$t('settings.checkboxRadius')"
:fallback="previewTheme.radii.checkbox"
max="16"
hard-min="0"
/>
<RangeInput
v-model="panelRadiusLocal"
name="panelRadius"
:label="$t('settings.panelRadius')"
:fallback="previewTheme.radii.panel"
max="50"
hard-min="0"
/>
<RangeInput
v-model="avatarRadiusLocal"
name="avatarRadius"
:label="$t('settings.avatarRadius')"
:fallback="previewTheme.radii.avatar"
max="28"
hard-min="0"
/>
<RangeInput
v-model="avatarAltRadiusLocal"
name="avatarAltRadius"
:label="$t('settings.avatarAltRadius')"
:fallback="previewTheme.radii.avatarAlt"
max="28"
hard-min="0"
/>
<RangeInput
v-model="attachmentRadiusLocal"
name="attachmentRadius"
:label="$t('settings.attachmentRadius')"
:fallback="previewTheme.radii.attachment"
max="50"
hard-min="0"
/>
<RangeInput
v-model="tooltipRadiusLocal"
name="tooltipRadius"
:label="$t('settings.tooltipRadius')"
:fallback="previewTheme.radii.tooltip"
max="50"
hard-min="0"
/>
</div>
<div
:label="$t('settings.style.shadows._tab_label')"
class="shadow-container"
>
<div class="tab-header shadow-selector">
<div class="select-container">
{{ $t('settings.style.shadows.component') }}
<label
for="shadow-switcher"
class="select"
>
<select
id="shadow-switcher"
v-model="shadowSelected"
class="shadow-switcher"
>
<option
v-for="shadow in shadowsAvailable"
:key="shadow"
:value="shadow"
>
{{ $t('settings.style.shadows.components.' + shadow) }}
</option>
</select>
<i class="icon-down-open" />
</label>
</div>
<div class="override">
<label
for="override"
class="label"
>
{{ $t('settings.style.shadows.override') }}
</label>
<input
id="override"
v-model="currentShadowOverriden"
name="override"
class="input-override"
type="checkbox"
>
<label
class="checkbox-label"
for="override"
/>
</div>
<button
class="btn"
@click="clearShadows"
>
{{ $t('settings.style.switcher.clear_all') }}
</button>
</div>
<shadow-control
v-model="currentShadow"
:ready="!!currentShadowFallback"
:fallback="currentShadowFallback"
/>
<div v-if="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'">
<i18n
path="settings.style.shadows.filter_hint.always_drop_shadow"
tag="p"
>
<code>filter: drop-shadow()</code>
</i18n>
<p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p>
<i18n
path="settings.style.shadows.filter_hint.drop_shadow_syntax"
tag="p"
>
<code>drop-shadow</code>
<code>spread-radius</code>
<code>inset</code>
</i18n>
<i18n
path="settings.style.shadows.filter_hint.inset_classic"
tag="p"
>
<code>box-shadow</code>
</i18n>
<p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p>
</div>
</div>
<div
:label="$t('settings.style.fonts._tab_label')"
class="fonts-container"
>
<div class="tab-header">
<p>{{ $t('settings.style.fonts.help') }}</p>
<button
class="btn"
@click="clearFonts"
>
{{ $t('settings.style.switcher.clear_all') }}
</button>
</div>
<FontControl
v-model="fontsLocal.interface"
name="ui"
:label="$t('settings.style.fonts.components.interface')"
:fallback="previewTheme.fonts.interface"
no-inherit="1"
/>
<FontControl
v-model="fontsLocal.input"
name="input"
:label="$t('settings.style.fonts.components.input')"
:fallback="previewTheme.fonts.input"
/>
<FontControl
v-model="fontsLocal.post"
name="post"
:label="$t('settings.style.fonts.components.post')"
:fallback="previewTheme.fonts.post"
/>
<FontControl
v-model="fontsLocal.postCode"
name="postCode"
:label="$t('settings.style.fonts.components.postCode')"
:fallback="previewTheme.fonts.postCode"
/>
</div>
</tab-switcher>
</keep-alive>
<div class="apply-container">
<button
class="btn submit"
:disabled="!themeValid"
@click="setCustomTheme"
>
{{ $t('general.apply') }}
</button>
<button
class="btn"
@click="clearAll"
>
{{ $t('settings.style.switcher.reset') }}
</button>
</div> </div>
</div> </div>
<div class="preview-container">
<preview :style="previewRules"/>
</div>
<keep-alive>
<tab-switcher key="style-tweak">
<div :label="$t('settings.style.common_colors._tab_label')" class="color-container">
<div class="tab-header">
<p>{{$t('settings.theme_help')}}</p>
<button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button>
<button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button>
</div>
<p>{{$t('settings.theme_help_v2_1')}}</p>
<h4>{{ $t('settings.style.common_colors.main') }}</h4>
<div class="color-item">
<ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/>
<OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/>
<ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/>
<ContrastRatio :contrast="previewContrast.bgText"/>
<ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/>
<ContrastRatio :contrast="previewContrast.bgLink"/>
</div>
<div class="color-item">
<ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/>
<ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/>
<ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/>
<p>{{ $t('settings.style.common_colors.foreground_hint') }}</p>
</div>
<h4>{{ $t('settings.style.common_colors.rgbo') }}</h4>
<div class="color-item">
<ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/>
<ContrastRatio :contrast="previewContrast.bgRed"/>
<ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/>
<ContrastRatio :contrast="previewContrast.bgBlue"/>
</div>
<div class="color-item">
<ColorInput name="cGreenColor" v-model="cGreenColorLocal" :label="$t('settings.cGreen')"/>
<ContrastRatio :contrast="previewContrast.bgGreen"/>
<ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/>
<ContrastRatio :contrast="previewContrast.bgOrange"/>
</div>
<p>{{$t('settings.theme_help_v2_2')}}</p>
</div>
<div :label="$t('settings.style.advanced_colors._tab_label')" class="color-container">
<div class="tab-header">
<p>{{$t('settings.theme_help')}}</p>
<button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button>
<button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.alert') }}</h4>
<ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.style.advanced_colors.alert_error')" :fallback="previewTheme.colors.alertError"/>
<ContrastRatio :contrast="previewContrast.alertError"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.badge') }}</h4>
<ColorInput name="badgeNotification" v-model="badgeNotificationColorLocal" :label="$t('settings.style.advanced_colors.badge_notification')" :fallback="previewTheme.colors.badgeNotification"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4>
<ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/>
<OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/>
<ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.text')"/>
<ContrastRatio :contrast="previewContrast.panelText" large="1"/>
<ColorInput name="panelLinkColor" v-model="panelLinkColorLocal" :fallback="previewTheme.colors.panelLink" :label="$t('settings.links')"/>
<ContrastRatio :contrast="previewContrast.panelLink" large="1"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.top_bar') }}</h4>
<ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/>
<ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/>
<ContrastRatio :contrast="previewContrast.topBarText"/>
<ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="previewTheme.colors.topBarLink" :label="$t('settings.links')"/>
<ContrastRatio :contrast="previewContrast.topBarLink"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.inputs') }}</h4>
<ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/>
<OpacityInput name="inputOpacity" v-model="inputOpacityLocal" :fallback="previewTheme.opacity.input || 1"/>
<ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/>
<ContrastRatio :contrast="previewContrast.inputText"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.buttons') }}</h4>
<ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/>
<OpacityInput name="btnOpacity" v-model="btnOpacityLocal" :fallback="previewTheme.opacity.btn || 1"/>
<ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/>
<ContrastRatio :contrast="previewContrast.btnText"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.borders') }}</h4>
<ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" :label="$t('settings.style.common.color')"/>
<OpacityInput name="borderOpacity" v-model="borderOpacityLocal" :fallback="previewTheme.opacity.border || 1"/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.faint_text') }}</h4>
<ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint || 1" :label="$t('settings.text')"/>
<ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.links')"/>
<ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.style.advanced_colors.panel_header')"/>
<OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/>
</div>
</div>
<div :label="$t('settings.style.radii._tab_label')" class="radius-container">
<div class="tab-header">
<p>{{$t('settings.radii_help')}}</p>
<button class="btn" @click="clearRoundness">{{$t('settings.style.switcher.clear_all')}}</button>
</div>
<RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/>
<RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="9" hardMin="0"/>
<RangeInput name="checkboxRadius" :label="$t('settings.checkboxRadius')" v-model="checkboxRadiusLocal" :fallback="previewTheme.radii.checkbox" max="16" hardMin="0"/>
<RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/>
<RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/>
<RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/>
<RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/>
<RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/>
</div>
<div :label="$t('settings.style.shadows._tab_label')" class="shadow-container">
<div class="tab-header shadow-selector">
<div class="select-container">
{{$t('settings.style.shadows.component')}}
<label for="shadow-switcher" class="select">
<select id="shadow-switcher" v-model="shadowSelected" class="shadow-switcher">
<option v-for="shadow in shadowsAvailable"
:value="shadow">
{{$t('settings.style.shadows.components.' + shadow)}}
</option>
</select>
<i class="icon-down-open"/>
</label>
</div>
<div class="override">
<label for="override" class="label">
{{$t('settings.style.shadows.override')}}
</label>
<input
v-model="currentShadowOverriden"
name="override"
id="override"
class="input-override"
type="checkbox">
<label class="checkbox-label" for="override"></label>
</div>
<button class="btn" @click="clearShadows">{{$t('settings.style.switcher.clear_all')}}</button>
</div>
<shadow-control :ready="!!currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/>
<div v-if="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'">
<i18n path="settings.style.shadows.filter_hint.always_drop_shadow" tag="p">
<code>filter: drop-shadow()</code>
</i18n>
<p>{{$t('settings.style.shadows.filter_hint.avatar_inset')}}</p>
<i18n path="settings.style.shadows.filter_hint.drop_shadow_syntax" tag="p">
<code>drop-shadow</code>
<code>spread-radius</code>
<code>inset</code>
</i18n>
<i18n path="settings.style.shadows.filter_hint.inset_classic" tag="p">
<code>box-shadow</code>
</i18n>
<p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p>
</div>
</div>
<div :label="$t('settings.style.fonts._tab_label')" class="fonts-container">
<div class="tab-header">
<p>{{$t('settings.style.fonts.help')}}</p>
<button class="btn" @click="clearFonts">{{$t('settings.style.switcher.clear_all')}}</button>
</div>
<FontControl
name="ui"
v-model="fontsLocal.interface"
:label="$t('settings.style.fonts.components.interface')"
:fallback="previewTheme.fonts.interface"
no-inherit="1"/>
<FontControl
name="input"
v-model="fontsLocal.input"
:label="$t('settings.style.fonts.components.input')"
:fallback="previewTheme.fonts.input"/>
<FontControl
name="post"
v-model="fontsLocal.post"
:label="$t('settings.style.fonts.components.post')"
:fallback="previewTheme.fonts.post"/>
<FontControl
name="postCode"
v-model="fontsLocal.postCode"
:label="$t('settings.style.fonts.components.postCode')"
:fallback="previewTheme.fonts.postCode"/>
</div>
</tab-switcher>
</keep-alive>
<div class="apply-container">
<button class="btn submit" :disabled="!themeValid" @click="setCustomTheme">{{$t('general.apply')}}</button>
<button class="btn" @click="clearAll">{{$t('settings.style.switcher.reset')}}</button>
</div>
</div>
</template> </template>
<script src="./style_switcher.js"></script> <script src="./style_switcher.js"></script>

View File

@ -10,12 +10,6 @@ export default Vue.component('tab-switcher', {
active: this.$slots.default.findIndex(_ => _.tag) active: this.$slots.default.findIndex(_ => _.tag)
} }
}, },
beforeUpdate () {
const currentSlot = this.$slots.default[this.active]
if (!currentSlot.tag) {
this.active = this.$slots.default.findIndex(_ => _.tag)
}
},
methods: { methods: {
activateTab (index) { activateTab (index) {
return () => { return () => {
@ -23,24 +17,30 @@ export default Vue.component('tab-switcher', {
} }
} }
}, },
beforeUpdate () {
const currentSlot = this.$slots.default[this.active]
if (!currentSlot.tag) {
this.active = this.$slots.default.findIndex(_ => _.tag)
}
},
render (h) { render (h) {
const tabs = this.$slots.default const tabs = this.$slots.default
.map((slot, index) => { .map((slot, index) => {
if (!slot.tag) return if (!slot.tag) return
const classesTab = ['tab'] const classesTab = ['tab']
const classesWrapper = ['tab-wrapper'] const classesWrapper = ['tab-wrapper']
if (index === this.active) { if (index === this.active) {
classesTab.push('active') classesTab.push('active')
classesWrapper.push('active') classesWrapper.push('active')
} }
return ( return (
<div class={ classesWrapper.join(' ')}> <div class={ classesWrapper.join(' ')}>
<button disabled={slot.data.attrs.disabled} onClick={this.activateTab(index)} class={ classesTab.join(' ') }>{slot.data.attrs.label}</button> <button disabled={slot.data.attrs.disabled} onClick={this.activateTab(index)} class={ classesTab.join(' ') }>{slot.data.attrs.label}</button>
</div> </div>
) )
}) })
const contents = this.$slots.default.map((slot, index) => { const contents = this.$slots.default.map((slot, index) => {
if (!slot.tag) return if (!slot.tag) return

View File

@ -1,10 +1,5 @@
<template> <template>
<Timeline <Timeline :title="tag" :timeline="timeline" :timeline-name="'tag'" :tag="tag" />
:title="tag"
:timeline="timeline"
:timeline-name="'tag'"
:tag="tag"
/>
</template> </template>
<script src='./tag_timeline.js'></script> <script src='./tag_timeline.js'></script>

View File

@ -2,10 +2,8 @@
<div> <div>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<div <div v-html="content" class="tos-content">
class="tos-content" </div>
v-html="content"
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -137,7 +137,7 @@ const Timeline = {
if (top < 15 && if (top < 15 &&
!this.paused && !this.paused &&
!(this.unfocused && this.$store.state.config.pauseOnUnfocused) !(this.unfocused && this.$store.state.config.pauseOnUnfocused)
) { ) {
this.showNewStatuses() this.showNewStatuses()
} else { } else {
this.paused = true this.paused = true

View File

@ -2,66 +2,41 @@
<div :class="classes.root"> <div :class="classes.root">
<div :class="classes.header"> <div :class="classes.header">
<div class="title"> <div class="title">
{{ title }} {{title}}
</div> </div>
<div <div @click.prevent class="loadmore-error alert error" v-if="timelineError">
v-if="timelineError" {{$t('timeline.error_fetching')}}
class="loadmore-error alert error"
@click.prevent
>
{{ $t('timeline.error_fetching') }}
</div> </div>
<button <button @click.prevent="showNewStatuses" class="loadmore-button" v-if="timeline.newStatusCount > 0 && !timelineError">
v-if="timeline.newStatusCount > 0 && !timelineError" {{$t('timeline.show_new')}}{{newStatusCountStr}}
class="loadmore-button"
@click.prevent="showNewStatuses"
>
{{ $t('timeline.show_new') }}{{ newStatusCountStr }}
</button> </button>
<div <div @click.prevent class="loadmore-text faint" v-if="!timeline.newStatusCount > 0 && !timelineError">
v-if="!timeline.newStatusCount > 0 && !timelineError" {{$t('timeline.up_to_date')}}
class="loadmore-text faint"
@click.prevent
>
{{ $t('timeline.up_to_date') }}
</div> </div>
</div> </div>
<div :class="classes.body"> <div :class="classes.body">
<div class="timeline"> <div class="timeline">
<conversation <conversation
v-for="status in timeline.visibleStatuses" v-for="status in timeline.visibleStatuses"
:key="status.id"
class="status-fadein" class="status-fadein"
:key="status.id"
:statusoid="status" :statusoid="status"
:collapsable="true" :collapsable="true"
/> />
</div> </div>
</div> </div>
<div :class="classes.footer"> <div :class="classes.footer">
<div <div v-if="count===0" class="new-status-notification text-center panel-footer faint">
v-if="count===0" {{$t('timeline.no_statuses')}}
class="new-status-notification text-center panel-footer faint"
>
{{ $t('timeline.no_statuses') }}
</div> </div>
<div <div v-else-if="bottomedOut" class="new-status-notification text-center panel-footer faint">
v-else-if="bottomedOut" {{$t('timeline.no_more_statuses')}}
class="new-status-notification text-center panel-footer faint"
>
{{ $t('timeline.no_more_statuses') }}
</div> </div>
<a <a v-else-if="!timeline.loading" href="#" v-on:click.prevent='fetchOlderStatuses()'>
v-else-if="!timeline.loading" <div class="new-status-notification text-center panel-footer">{{$t('timeline.load_older')}}</div>
href="#"
@click.prevent="fetchOlderStatuses()"
>
<div class="new-status-notification text-center panel-footer">{{ $t('timeline.load_older') }}</div>
</a> </a>
<div <div v-else class="new-status-notification text-center panel-footer">
v-else <i class="icon-spin3 animate-spin"/>
class="new-status-notification text-center panel-footer"
>
<i class="icon-spin3 animate-spin" />
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
class="avatar" class="avatar"
:class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }"
:src="imgSrc" :src="imgSrc"
:image-load-error="imageLoadError" :imageLoadError="imageLoadError"
/> />
</template> </template>

View File

@ -22,15 +22,15 @@ export default {
computed: { computed: {
classes () { classes () {
return [{ return [{
'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius 'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius
'user-card-rounded': this.rounded === true, // set border-radius for all sides 'user-card-rounded': this.rounded === true, // set border-radius for all sides
'user-card-bordered': this.bordered === true // set border for all sides 'user-card-bordered': this.bordered === true // set border for all sides
}] }]
}, },
style () { style () {
const color = this.$store.state.config.customTheme.colors const color = this.$store.state.config.customTheme.colors
? this.$store.state.config.customTheme.colors.bg // v2 ? this.$store.state.config.customTheme.colors.bg // v2
: this.$store.state.config.colors.bg // v1 : this.$store.state.config.colors.bg // v1
if (color) { if (color) {
const rgb = (typeof color === 'string') ? hex2rgb(color) : color const rgb = (typeof color === 'string') ? hex2rgb(color) : color
@ -72,12 +72,12 @@ export default {
userHighlightType: { userHighlightType: {
get () { get () {
const data = this.$store.state.config.highlight[this.user.screen_name] const data = this.$store.state.config.highlight[this.user.screen_name]
return (data && data.type) || 'disabled' return data && data.type || 'disabled'
}, },
set (type) { set (type) {
const data = this.$store.state.config.highlight[this.user.screen_name] const data = this.$store.state.config.highlight[this.user.screen_name]
if (type !== 'disabled') { if (type !== 'disabled') {
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: (data && data.color) || '#FFFFFF', type }) this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: data && data.color || '#FFFFFF', type })
} else { } else {
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: undefined }) this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: undefined })
} }
@ -107,7 +107,7 @@ export default {
followUser () { followUser () {
const store = this.$store const store = this.$store
this.followRequestInProgress = true this.followRequestInProgress = true
requestFollow(this.user, store).then(({ sent }) => { requestFollow(this.user, store).then(({sent}) => {
this.followRequestInProgress = false this.followRequestInProgress = false
this.followRequestSent = sent this.followRequestSent = sent
}) })
@ -138,7 +138,7 @@ export default {
store.commit('setProfileView', { v }) store.commit('setProfileView', { v })
} }
}, },
linkClicked ({ target }) { linkClicked ({target}) {
if (target.tagName === 'SPAN') { if (target.tagName === 'SPAN') {
target = target.parentNode target = target.parentNode
} }

View File

@ -1,240 +1,126 @@
<template> <template>
<div <div class="user-card" :class="classes" :style="style">
class="user-card" <div class="panel-heading">
:class="classes" <div class='user-info'>
:style="style" <div class='container'>
> <router-link :to="userProfileLink(user)">
<div class="panel-heading"> <UserAvatar :betterShadow="betterShadow" :src="user.profile_image_url_original"/>
<div class="user-info"> </router-link>
<div class="container"> <div class="name-and-screen-name">
<router-link :to="userProfileLink(user)"> <div class="top-line">
<UserAvatar <div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div>
:better-shadow="betterShadow" <div :title="user.name" class='user-name' v-else>{{user.name}}</div>
:src="user.profile_image_url_original" <router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
/> <i class="button-icon icon-pencil usersettings" :title="$t('tool_tip.user_settings')"></i>
</router-link>
<div class="name-and-screen-name">
<div class="top-line">
<div
v-if="user.name_html"
:title="user.name"
class="user-name"
v-html="user.name_html"
/>
<div
v-else
:title="user.name"
class="user-name"
>
{{ user.name }}
</div>
<router-link
v-if="!isOtherUser"
:to="{ name: 'user-settings' }"
>
<i
class="button-icon icon-pencil usersettings"
:title="$t('tool_tip.user_settings')"
/>
</router-link>
<a
v-if="isOtherUser && !user.is_local"
:href="user.statusnet_profile_url"
target="_blank"
>
<i class="icon-link-ext usersettings" />
</a>
</div>
<router-link
class="user-screen-name"
:to="userProfileLink(user)"
>
<span class="handle">@{{ user.screen_name }}
<span
v-if="!hideBio && !!visibleRole"
class="alert staff"
>{{ visibleRole }}</span>
</span><span v-if="user.locked"><i class="icon icon-lock" /></span>
<span
v-if="!hideUserStatsLocal && !hideBio"
class="dailyAvg"
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
</router-link> </router-link>
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
<i class="icon-link-ext usersettings"></i>
</a>
</div> </div>
</div>
<div class="user-meta"> <router-link class='user-screen-name' :to="userProfileLink(user)">
<div <span class="handle">@{{user.screen_name}}
v-if="user.follows_you && loggedIn && isOtherUser" <span class="alert staff" v-if="!hideBio && !!visibleRole">{{visibleRole}}</span>
class="following" </span><span v-if="user.locked"><i class="icon icon-lock"></i></span>
> <span v-if="!hideUserStatsLocal && !hideBio" class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span>
{{ $t('user_card.follows_you') }} </router-link>
</div>
<div
v-if="isOtherUser && (loggedIn || !switcher)"
class="highlighter"
>
<!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
<input
v-if="userHighlightType !== 'disabled'"
:id="'userHighlightColorTx'+user.id"
v-model="userHighlightColor"
class="userHighlightText"
type="text"
>
<input
v-if="userHighlightType !== 'disabled'"
:id="'userHighlightColor'+user.id"
v-model="userHighlightColor"
class="userHighlightCl"
type="color"
>
<label
for="style-switcher"
class="userHighlightSel select"
>
<select
:id="'userHighlightSel'+user.id"
v-model="userHighlightType"
class="userHighlightSel"
>
<option value="disabled">No highlight</option>
<option value="solid">Solid bg</option>
<option value="striped">Striped bg</option>
<option value="side">Side stripe</option>
</select>
<i class="icon-down-open" />
</label>
</div>
</div>
<div
v-if="isOtherUser"
class="user-interactions"
>
<div
v-if="loggedIn"
class="follow"
>
<span v-if="user.following">
<!--Following them!-->
<button
class="pressed"
:disabled="followRequestInProgress"
:title="$t('user_card.follow_unfollow')"
@click="unfollowUser"
>
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else>
{{ $t('user_card.following') }}
</template>
</button>
</span>
<span v-if="!user.following">
<button
:disabled="followRequestInProgress"
:title="followRequestSent ? $t('user_card.follow_again') : ''"
@click="followUser"
>
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else-if="followRequestSent">
{{ $t('user_card.follow_sent') }}
</template>
<template v-else>
{{ $t('user_card.follow') }}
</template>
</button>
</span>
</div>
<div
v-if="isOtherUser && loggedIn"
class="mute"
>
<span v-if="user.muted">
<button
class="pressed"
@click="unmuteUser"
>
{{ $t('user_card.muted') }}
</button>
</span>
<span v-if="!user.muted">
<button @click="muteUser">
{{ $t('user_card.mute') }}
</button>
</span>
</div>
<div v-if="!loggedIn && user.is_local">
<RemoteFollow :user="user" />
</div>
<div
v-if="isOtherUser && loggedIn"
class="block"
>
<span v-if="user.statusnet_blocking">
<button
class="pressed"
@click="unblockUser"
>
{{ $t('user_card.blocked') }}
</button>
</span>
<span v-if="!user.statusnet_blocking">
<button @click="blockUser">
{{ $t('user_card.block') }}
</button>
</span>
</div>
</div> </div>
</div> </div>
</div> <div class="user-meta">
<div <div v-if="user.follows_you && loggedIn && isOtherUser" class="following">
v-if="!hideBio" {{ $t('user_card.follows_you') }}
class="panel-body"
>
<div
v-if="!hideUserStatsLocal && switcher"
class="user-counts"
>
<div
class="user-count"
@click.prevent="setProfileView('statuses')"
>
<h5>{{ $t('user_card.statuses') }}</h5>
<span>{{ user.statuses_count }} <br></span>
</div> </div>
<div <div class="highlighter" v-if="isOtherUser && (loggedIn || !switcher)">
class="user-count" <!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
@click.prevent="setProfileView('friends')" <input class="userHighlightText" type="text" :id="'userHighlightColorTx'+user.id" v-if="userHighlightType !== 'disabled'" v-model="userHighlightColor"/>
> <input class="userHighlightCl" type="color" :id="'userHighlightColor'+user.id" v-if="userHighlightType !== 'disabled'" v-model="userHighlightColor"/>
<h5>{{ $t('user_card.followees') }}</h5> <label for="style-switcher" class='userHighlightSel select'>
<span>{{ user.friends_count }}</span> <select class="userHighlightSel" :id="'userHighlightSel'+user.id" v-model="userHighlightType">
</div> <option value="disabled">No highlight</option>
<div <option value="solid">Solid bg</option>
class="user-count" <option value="striped">Striped bg</option>
@click.prevent="setProfileView('followers')" <option value="side">Side stripe</option>
> </select>
<h5>{{ $t('user_card.followers') }}</h5> <i class="icon-down-open"/>
<span>{{ user.followers_count }}</span> </label>
</div>
</div>
<div v-if="isOtherUser" class="user-interactions">
<div class="follow" v-if="loggedIn">
<span v-if="user.following">
<!--Following them!-->
<button @click="unfollowUser" class="pressed" :disabled="followRequestInProgress" :title="$t('user_card.follow_unfollow')">
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else>
{{ $t('user_card.following') }}
</template>
</button>
</span>
<span v-if="!user.following">
<button @click="followUser" :disabled="followRequestInProgress" :title="followRequestSent ? $t('user_card.follow_again') : ''">
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else-if="followRequestSent">
{{ $t('user_card.follow_sent') }}
</template>
<template v-else>
{{ $t('user_card.follow') }}
</template>
</button>
</span>
</div>
<div class='mute' v-if='isOtherUser && loggedIn'>
<span v-if='user.muted'>
<button @click="unmuteUser" class="pressed">
{{ $t('user_card.muted') }}
</button>
</span>
<span v-if='!user.muted'>
<button @click="muteUser">
{{ $t('user_card.mute') }}
</button>
</span>
</div>
<div v-if='!loggedIn && user.is_local'>
<RemoteFollow :user="user" />
</div>
<div class='block' v-if='isOtherUser && loggedIn'>
<span v-if='user.statusnet_blocking'>
<button @click="unblockUser" class="pressed">
{{ $t('user_card.blocked') }}
</button>
</span>
<span v-if='!user.statusnet_blocking'>
<button @click="blockUser">
{{ $t('user_card.block') }}
</button>
</span>
</div> </div>
</div> </div>
<p
v-if="!hideBio && user.description_html"
class="user-card-bio"
@click.prevent="linkClicked"
v-html="user.description_html"
/>
<p
v-else-if="!hideBio"
class="user-card-bio"
>
{{ user.description }}
</p>
</div> </div>
</div> </div>
<div class="panel-body" v-if="!hideBio">
<div v-if="!hideUserStatsLocal && switcher" class="user-counts">
<div class="user-count" v-on:click.prevent="setProfileView('statuses')">
<h5>{{ $t('user_card.statuses') }}</h5>
<span>{{user.statuses_count}} <br></span>
</div>
<div class="user-count" v-on:click.prevent="setProfileView('friends')">
<h5>{{ $t('user_card.followees') }}</h5>
<span>{{user.friends_count}}</span>
</div>
<div class="user-count" v-on:click.prevent="setProfileView('followers')">
<h5>{{ $t('user_card.followers') }}</h5>
<span>{{user.followers_count}}</span>
</div>
</div>
<p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="user-card-bio" v-html="user.description_html"></p>
<p v-else-if="!hideBio" class="user-card-bio">{{ user.description }}</p>
</div>
</div>
</template> </template>
<script src="./user_card.js"></script> <script src="./user_card.js"></script>

View File

@ -1,38 +1,14 @@
<template> <template>
<div> <div>
<div class="user-finder-container"> <div class="user-finder-container">
<i <i class="icon-spin4 user-finder-icon animate-spin-slow" v-if="loading" />
v-if="loading" <a href="#" v-if="hidden" :title="$t('finder.find_user')"><i class="icon-user-plus user-finder-icon" @click.prevent.stop="toggleHidden" /></a>
class="icon-spin4 user-finder-icon animate-spin-slow"
/>
<a
v-if="hidden"
href="#"
:title="$t('finder.find_user')"
><i
class="icon-user-plus user-finder-icon"
@click.prevent.stop="toggleHidden"
/></a>
<template v-else> <template v-else>
<input <input class="user-finder-input" ref="userSearchInput" @keyup.enter="findUser(username)" v-model="username" :placeholder="$t('finder.find_user')" id="user-finder-input" type="text"/>
id="user-finder-input" <button class="btn search-button" @click="findUser(username)">
ref="userSearchInput" <i class="icon-search"/>
v-model="username"
class="user-finder-input"
:placeholder="$t('finder.find_user')"
type="text"
@keyup.enter="findUser(username)"
>
<button
class="btn search-button"
@click="findUser(username)"
>
<i class="icon-search" />
</button> </button>
<i <i class="button-icon icon-cancel user-finder-icon" @click.prevent.stop="toggleHidden"/>
class="button-icon icon-cancel user-finder-icon"
@click.prevent.stop="toggleHidden"
/>
</template> </template>
</div> </div>
</div> </div>
@ -49,6 +25,7 @@
align-items: baseline; align-items: baseline;
vertical-align: baseline; vertical-align: baseline;
.user-finder-input, .user-finder-input,
.search-button { .search-button {
height: 29px; height: 29px;

View File

@ -1,20 +1,12 @@
<template> <template>
<div class="user-panel"> <div class="user-panel">
<div <div v-if='user' class="panel panel-default" style="overflow: visible;">
v-if="user" <UserCard :user="user" :hideBio="true" rounded="top"/>
class="panel panel-default"
style="overflow: visible;"
>
<UserCard
:user="user"
:hide-bio="true"
rounded="top"
/>
<div class="panel-footer"> <div class="panel-footer">
<post-status-form v-if="user" /> <post-status-form v-if='user'></post-status-form>
</div> </div>
</div> </div>
<login-form v-if="!user" /> <login-form v-if='!user'></login-form>
</div> </div>
</template> </template>

View File

@ -1,84 +1,55 @@
<template> <template>
<div> <div>
<div <div v-if="user.id" class="user-profile panel panel-default">
v-if="user.id" <UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
class="user-profile panel panel-default" <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
> <Timeline
<UserCard :label="$t('user_card.statuses')"
:user="user" :disabled="!user.statuses_count"
:switcher="true" :count="user.statuses_count"
:selected="timeline.viewing" :embedded="true"
rounded="top" :title="$t('user_profile.timeline_title')"
:timeline="timeline"
:timeline-name="'user'"
:user-id="userId"
/> />
<tab-switcher <div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count">
ref="tabSwitcher" <FriendList :userId="userId" />
:render-only-focused="true" </div>
> <div :label="$t('user_card.followers')" v-if="followersTabVisible" :disabled="!user.followers_count">
<Timeline <FollowerList :userId="userId" :entryProps="{noFollowsYou: isUs}" />
:label="$t('user_card.statuses')" </div>
:disabled="!user.statuses_count" <Timeline
:count="user.statuses_count" :label="$t('user_card.media')"
:embedded="true" :disabled="!media.visibleStatuses.length"
:title="$t('user_profile.timeline_title')" :embedded="true" :title="$t('user_card.media')"
:timeline="timeline" timeline-name="media"
:timeline-name="'user'" :timeline="media"
:user-id="userId" :user-id="userId"
/> />
<div <Timeline
v-if="followsTabVisible" v-if="isUs"
:label="$t('user_card.followees')" :label="$t('user_card.favorites')"
:disabled="!user.friends_count" :disabled="!favorites.visibleStatuses.length"
> :embedded="true"
<FriendList :user-id="userId" /> :title="$t('user_card.favorites')"
</div> timeline-name="favorites"
<div :timeline="favorites"
v-if="followersTabVisible" />
:label="$t('user_card.followers')" </tab-switcher>
:disabled="!user.followers_count" </div>
> <div v-else class="panel user-profile-placeholder">
<FollowerList <div class="panel-heading">
:user-id="userId" <div class="title">
:entry-props="{noFollowsYou: isUs}" {{ $t('settings.profile_tab') }}
/> </div>
</div>
<Timeline
:label="$t('user_card.media')"
:disabled="!media.visibleStatuses.length"
:embedded="true"
:title="$t('user_card.media')"
timeline-name="media"
:timeline="media"
:user-id="userId"
/>
<Timeline
v-if="isUs"
:label="$t('user_card.favorites')"
:disabled="!favorites.visibleStatuses.length"
:embedded="true"
:title="$t('user_card.favorites')"
timeline-name="favorites"
:timeline="favorites"
/>
</tab-switcher>
</div> </div>
<div <div class="panel-body">
v-else <span v-if="error">{{ error }}</span>
class="panel user-profile-placeholder" <i class="icon-spin3 animate-spin" v-else></i>
>
<div class="panel-heading">
<div class="title">
{{ $t('settings.profile_tab') }}
</div>
</div>
<div class="panel-body">
<span v-if="error">{{ error }}</span>
<i
v-else
class="icon-spin3 animate-spin"
/>
</div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script src="./user_profile.js"></script> <script src="./user_profile.js"></script>

View File

@ -33,7 +33,7 @@ const userSearch = {
return return
} }
this.loading = true this.loading = true
userSearchApi.search({ query, store: this.$store }) userSearchApi.search({query, store: this.$store})
.then((res) => { .then((res) => {
this.loading = false this.loading = false
this.users = res this.users = res

View File

@ -1,38 +1,19 @@
<template> <template>
<div class="user-search panel panel-default"> <div class="user-search panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{{ $t('nav.user_search') }} {{$t('nav.user_search')}}
</div> </div>
<div class="user-search-input-container"> <div class="user-search-input-container">
<input <input class="user-finder-input" ref="userSearchInput" @keyup.enter="newQuery(username)" v-model="username" :placeholder="$t('finder.find_user')"/>
ref="userSearchInput" <button class="btn search-button" @click="newQuery(username)">
v-model="username" <i class="icon-search"/>
class="user-finder-input"
:placeholder="$t('finder.find_user')"
@keyup.enter="newQuery(username)"
>
<button
class="btn search-button"
@click="newQuery(username)"
>
<i class="icon-search" />
</button> </button>
</div> </div>
<div <div v-if="loading" class="text-center loading-icon">
v-if="loading" <i class="icon-spin3 animate-spin"/>
class="text-center loading-icon"
>
<i class="icon-spin3 animate-spin" />
</div> </div>
<div <div v-else class="panel-body">
v-else <FollowCard v-for="user in users" :key="user.id" :user="user"/>
class="panel-body"
>
<FollowCard
v-for="user in users"
:key="user.id"
:user="user"
/>
</div> </div>
</div> </div>
</template> </template>

View File

@ -134,12 +134,12 @@ const UserSettings = {
hide_followers, hide_followers,
show_role show_role
/* eslint-enable camelcase */ /* eslint-enable camelcase */
} }).then((user) => { }}).then((user) => {
if (!user.error) { if (!user.error) {
this.$store.commit('addNewUsers', [user]) this.$store.commit('addNewUsers', [user])
this.$store.commit('setCurrentUser', user) this.$store.commit('setCurrentUser', user)
} }
}) })
}, },
changeVis (visibility) { changeVis (visibility) {
this.newDefaultScope = visibility this.newDefaultScope = visibility
@ -150,12 +150,12 @@ const UserSettings = {
if (file.size > this.$store.state.instance[slot + 'limit']) { if (file.size > this.$store.state.instance[slot + 'limit']) {
const filesize = fileSizeFormatService.fileSizeFormat(file.size) const filesize = fileSizeFormatService.fileSizeFormat(file.size)
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit']) const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
this[slot + 'UploadError'] = this.$t('upload.error.base') + ' ' + this.$t('upload.error.file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit }) this[slot + 'UploadError'] = this.$t('upload.error.base') + ' ' + this.$t('upload.error.file_too_big', {filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit})
return return
} }
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const reader = new FileReader() const reader = new FileReader()
reader.onload = ({ target }) => { reader.onload = ({target}) => {
const img = target.result const img = target.result
this[slot + 'Preview'] = img this[slot + 'Preview'] = img
} }
@ -195,7 +195,7 @@ const UserSettings = {
offset_top = 0 offset_top = 0
offset_left = 0 offset_left = 0
this.bannerUploading = true this.bannerUploading = true
this.$store.state.api.backendInteractor.updateBanner({ params: { banner, offset_top, offset_left, width, height } }).then((data) => { this.$store.state.api.backendInteractor.updateBanner({params: {banner, offset_top, offset_left, width, height}}).then((data) => {
if (!data.error) { if (!data.error) {
let clone = JSON.parse(JSON.stringify(this.$store.state.users.currentUser)) let clone = JSON.parse(JSON.stringify(this.$store.state.users.currentUser))
clone.cover_photo = data.url clone.cover_photo = data.url
@ -221,7 +221,7 @@ const UserSettings = {
cropW = imginfo.width cropW = imginfo.width
cropH = imginfo.width cropH = imginfo.width
this.backgroundUploading = true this.backgroundUploading = true
this.$store.state.api.backendInteractor.updateBg({ params: { img, cropX, cropY, cropW, cropH } }).then((data) => { this.$store.state.api.backendInteractor.updateBg({params: {img, cropX, cropY, cropW, cropH}}).then((data) => {
if (!data.error) { if (!data.error) {
let clone = JSON.parse(JSON.stringify(this.$store.state.users.currentUser)) let clone = JSON.parse(JSON.stringify(this.$store.state.users.currentUser))
clone.background_image = data.url clone.background_image = data.url
@ -237,7 +237,7 @@ const UserSettings = {
importFollows () { importFollows () {
this.followListUploading = true this.followListUploading = true
const followList = this.followList const followList = this.followList
this.$store.state.api.backendInteractor.followImport({ params: followList }) this.$store.state.api.backendInteractor.followImport({params: followList})
.then((status) => { .then((status) => {
if (status) { if (status) {
this.followsImported = true this.followsImported = true
@ -295,11 +295,11 @@ const UserSettings = {
this.deletingAccount = true this.deletingAccount = true
}, },
deleteAccount () { deleteAccount () {
this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput }) this.$store.state.api.backendInteractor.deleteAccount({password: this.deleteAccountConfirmPasswordInput})
.then((res) => { .then((res) => {
if (res.status === 'success') { if (res.status === 'success') {
this.$store.dispatch('logout') this.$store.dispatch('logout')
this.$router.push({ name: 'root' }) this.$router.push({name: 'root'})
} else { } else {
this.deleteAccountError = res.error this.deleteAccountError = res.error
} }

View File

@ -2,23 +2,15 @@
<div class="settings panel panel-default"> <div class="settings panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<div class="title"> <div class="title">
{{ $t('settings.user_settings') }} {{$t('settings.user_settings')}}
</div> </div>
<transition name="fade"> <transition name="fade">
<template v-if="currentSaveStateNotice"> <template v-if="currentSaveStateNotice">
<div <div @click.prevent class="alert error" v-if="currentSaveStateNotice.error">
v-if="currentSaveStateNotice.error"
class="alert error"
@click.prevent
>
{{ $t('settings.saving_err') }} {{ $t('settings.saving_err') }}
</div> </div>
<div <div @click.prevent class="alert transparent" v-if="!currentSaveStateNotice.error">
v-if="!currentSaveStateNotice.error"
class="alert transparent"
@click.prevent
>
{{ $t('settings.saving_ok') }} {{ $t('settings.saving_ok') }}
</div> </div>
</template> </template>
@ -27,258 +19,132 @@
<div class="panel-body profile-edit"> <div class="panel-body profile-edit">
<tab-switcher> <tab-switcher>
<div :label="$t('settings.profile_tab')"> <div :label="$t('settings.profile_tab')">
<div class="setting-item"> <div class="setting-item" >
<h2>{{ $t('settings.name_bio') }}</h2> <h2>{{$t('settings.name_bio')}}</h2>
<p>{{ $t('settings.name') }}</p> <p>{{$t('settings.name')}}</p>
<EmojiInput <EmojiInput
id="username"
v-model="newName"
type="text" type="text"
v-model="newName"
id="username"
classname="name-changer" classname="name-changer"
/> />
<p>{{ $t('settings.bio') }}</p> <p>{{$t('settings.bio')}}</p>
<EmojiInput <EmojiInput
v-model="newBio"
type="textarea" type="textarea"
v-model="newBio"
classname="bio" classname="bio"
/> />
<p> <p>
<input <input type="checkbox" v-model="newLocked" id="account-locked">
id="account-locked" <label for="account-locked">{{$t('settings.lock_account_description')}}</label>
v-model="newLocked"
type="checkbox"
>
<label for="account-locked">{{ $t('settings.lock_account_description') }}</label>
</p> </p>
<div> <div>
<label for="default-vis">{{ $t('settings.default_vis') }}</label> <label for="default-vis">{{$t('settings.default_vis')}}</label>
<div <div id="default-vis" class="visibility-tray">
id="default-vis"
class="visibility-tray"
>
<scope-selector <scope-selector
:show-all="true" :showAll="true"
:user-default="newDefaultScope" :userDefault="newDefaultScope"
:on-scope-change="changeVis" :onScopeChange="changeVis"/>
/>
</div> </div>
</div> </div>
<p> <p>
<input <input type="checkbox" v-model="newNoRichText" id="account-no-rich-text">
id="account-no-rich-text" <label for="account-no-rich-text">{{$t('settings.no_rich_text_description')}}</label>
v-model="newNoRichText"
type="checkbox"
>
<label for="account-no-rich-text">{{ $t('settings.no_rich_text_description') }}</label>
</p> </p>
<p> <p>
<input <input type="checkbox" v-model="hideFollows" id="account-hide-follows">
id="account-hide-follows" <label for="account-hide-follows">{{$t('settings.hide_follows_description')}}</label>
v-model="hideFollows"
type="checkbox"
>
<label for="account-hide-follows">{{ $t('settings.hide_follows_description') }}</label>
</p> </p>
<p> <p>
<input <input type="checkbox" v-model="hideFollowers" id="account-hide-followers">
id="account-hide-followers" <label for="account-hide-followers">{{$t('settings.hide_followers_description')}}</label>
v-model="hideFollowers"
type="checkbox"
>
<label for="account-hide-followers">{{ $t('settings.hide_followers_description') }}</label>
</p> </p>
<p> <p>
<input <input type="checkbox" v-model="showRole" id="account-show-role">
id="account-show-role" <label for="account-show-role" v-if="role === 'admin'">{{$t('settings.show_admin_badge')}}</label>
v-model="showRole" <label for="account-show-role" v-if="role === 'moderator'">{{$t('settings.show_moderator_badge')}}</label>
type="checkbox"
>
<label
v-if="role === 'admin'"
for="account-show-role"
>{{ $t('settings.show_admin_badge') }}</label>
<label
v-if="role === 'moderator'"
for="account-show-role"
>{{ $t('settings.show_moderator_badge') }}</label>
</p> </p>
<button <button :disabled='newName && newName.length === 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button>
:disabled="newName && newName.length === 0"
class="btn btn-default"
@click="updateProfile"
>
{{ $t('general.submit') }}
</button>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.avatar') }}</h2> <h2>{{$t('settings.avatar')}}</h2>
<p class="visibility-notice"> <p class="visibility-notice">{{$t('settings.avatar_size_instruction')}}</p>
{{ $t('settings.avatar_size_instruction') }} <p>{{$t('settings.current_avatar')}}</p>
</p> <img :src="user.profile_image_url_original" class="current-avatar" />
<p>{{ $t('settings.current_avatar') }}</p> <p>{{$t('settings.set_new_avatar')}}</p>
<img <button class="btn" type="button" id="pick-avatar" v-show="pickAvatarBtnVisible">{{$t('settings.upload_a_photo')}}</button>
:src="user.profile_image_url_original" <image-cropper trigger="#pick-avatar" :submitHandler="submitAvatar" @open="pickAvatarBtnVisible=false" @close="pickAvatarBtnVisible=true" />
class="current-avatar"
>
<p>{{ $t('settings.set_new_avatar') }}</p>
<button
v-show="pickAvatarBtnVisible"
id="pick-avatar"
class="btn"
type="button"
>
{{ $t('settings.upload_a_photo') }}
</button>
<image-cropper
trigger="#pick-avatar"
:submit-handler="submitAvatar"
@open="pickAvatarBtnVisible=false"
@close="pickAvatarBtnVisible=true"
/>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.profile_banner') }}</h2> <h2>{{$t('settings.profile_banner')}}</h2>
<p>{{ $t('settings.current_profile_banner') }}</p> <p>{{$t('settings.current_profile_banner')}}</p>
<img <img :src="user.cover_photo" class="banner" />
:src="user.cover_photo" <p>{{$t('settings.set_new_profile_banner')}}</p>
class="banner" <img class="banner" v-bind:src="bannerPreview" v-if="bannerPreview" />
>
<p>{{ $t('settings.set_new_profile_banner') }}</p>
<img
v-if="bannerPreview"
class="banner"
:src="bannerPreview"
>
<div> <div>
<input <input type="file" @change="uploadFile('banner', $event)" />
type="file"
@change="uploadFile('banner', $event)"
>
</div> </div>
<i <i class=" icon-spin4 animate-spin uploading" v-if="bannerUploading"></i>
v-if="bannerUploading" <button class="btn btn-default" v-else-if="bannerPreview" @click="submitBanner">{{$t('general.submit')}}</button>
class=" icon-spin4 animate-spin uploading" <div class='alert error' v-if="bannerUploadError">
/>
<button
v-else-if="bannerPreview"
class="btn btn-default"
@click="submitBanner"
>
{{ $t('general.submit') }}
</button>
<div
v-if="bannerUploadError"
class="alert error"
>
Error: {{ bannerUploadError }} Error: {{ bannerUploadError }}
<i <i class="button-icon icon-cancel" @click="clearUploadError('banner')"></i>
class="button-icon icon-cancel"
@click="clearUploadError('banner')"
/>
</div> </div>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.profile_background') }}</h2> <h2>{{$t('settings.profile_background')}}</h2>
<p>{{ $t('settings.set_new_profile_background') }}</p> <p>{{$t('settings.set_new_profile_background')}}</p>
<img <img class="bg" v-bind:src="backgroundPreview" v-if="backgroundPreview" />
v-if="backgroundPreview"
class="bg"
:src="backgroundPreview"
>
<div> <div>
<input <input type="file" @change="uploadFile('background', $event)" />
type="file"
@change="uploadFile('background', $event)"
>
</div> </div>
<i <i class=" icon-spin4 animate-spin uploading" v-if="backgroundUploading"></i>
v-if="backgroundUploading" <button class="btn btn-default" v-else-if="backgroundPreview" @click="submitBg">{{$t('general.submit')}}</button>
class=" icon-spin4 animate-spin uploading" <div class='alert error' v-if="backgroundUploadError">
/>
<button
v-else-if="backgroundPreview"
class="btn btn-default"
@click="submitBg"
>
{{ $t('general.submit') }}
</button>
<div
v-if="backgroundUploadError"
class="alert error"
>
Error: {{ backgroundUploadError }} Error: {{ backgroundUploadError }}
<i <i class="button-icon icon-cancel" @click="clearUploadError('background')"></i>
class="button-icon icon-cancel"
@click="clearUploadError('background')"
/>
</div> </div>
</div> </div>
</div> </div>
<div :label="$t('settings.security_tab')"> <div :label="$t('settings.security_tab')">
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.change_password') }}</h2> <h2>{{$t('settings.change_password')}}</h2>
<div> <div>
<p>{{ $t('settings.current_password') }}</p> <p>{{$t('settings.current_password')}}</p>
<input <input type="password" v-model="changePasswordInputs[0]">
v-model="changePasswordInputs[0]"
type="password"
>
</div> </div>
<div> <div>
<p>{{ $t('settings.new_password') }}</p> <p>{{$t('settings.new_password')}}</p>
<input <input type="password" v-model="changePasswordInputs[1]">
v-model="changePasswordInputs[1]"
type="password"
>
</div> </div>
<div> <div>
<p>{{ $t('settings.confirm_new_password') }}</p> <p>{{$t('settings.confirm_new_password')}}</p>
<input <input type="password" v-model="changePasswordInputs[2]">
v-model="changePasswordInputs[2]"
type="password"
>
</div> </div>
<button <button class="btn btn-default" @click="changePassword">{{$t('general.submit')}}</button>
class="btn btn-default" <p v-if="changedPassword">{{$t('settings.changed_password')}}</p>
@click="changePassword" <p v-else-if="changePasswordError !== false">{{$t('settings.change_password_error')}}</p>
> <p v-if="changePasswordError">{{changePasswordError}}</p>
{{ $t('general.submit') }}
</button>
<p v-if="changedPassword">
{{ $t('settings.changed_password') }}
</p>
<p v-else-if="changePasswordError !== false">
{{ $t('settings.change_password_error') }}
</p>
<p v-if="changePasswordError">
{{ changePasswordError }}
</p>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.oauth_tokens') }}</h2> <h2>{{$t('settings.oauth_tokens')}}</h2>
<table class="oauth-tokens"> <table class="oauth-tokens">
<thead> <thead>
<tr> <tr>
<th>{{ $t('settings.app_name') }}</th> <th>{{$t('settings.app_name')}}</th>
<th>{{ $t('settings.valid_until') }}</th> <th>{{$t('settings.valid_until')}}</th>
<th /> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr v-for="oauthToken in oauthTokens" :key="oauthToken.id">
v-for="oauthToken in oauthTokens" <td>{{oauthToken.appName}}</td>
:key="oauthToken.id" <td>{{oauthToken.validUntil}}</td>
>
<td>{{ oauthToken.appName }}</td>
<td>{{ oauthToken.validUntil }}</td>
<td class="actions"> <td class="actions">
<button <button class="btn btn-default" @click="revokeToken(oauthToken.id)">
class="btn btn-default" {{$t('settings.revoke_token')}}
@click="revokeToken(oauthToken.id)"
>
{{ $t('settings.revoke_token') }}
</button> </button>
</td> </td>
</tr> </tr>
@ -287,113 +153,56 @@
</div> </div>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.delete_account') }}</h2> <h2>{{$t('settings.delete_account')}}</h2>
<p v-if="!deletingAccount"> <p v-if="!deletingAccount">{{$t('settings.delete_account_description')}}</p>
{{ $t('settings.delete_account_description') }}
</p>
<div v-if="deletingAccount"> <div v-if="deletingAccount">
<p>{{ $t('settings.delete_account_instructions') }}</p> <p>{{$t('settings.delete_account_instructions')}}</p>
<p>{{ $t('login.password') }}</p> <p>{{$t('login.password')}}</p>
<input <input type="password" v-model="deleteAccountConfirmPasswordInput">
v-model="deleteAccountConfirmPasswordInput" <button class="btn btn-default" @click="deleteAccount">{{$t('settings.delete_account')}}</button>
type="password"
>
<button
class="btn btn-default"
@click="deleteAccount"
>
{{ $t('settings.delete_account') }}
</button>
</div> </div>
<p v-if="deleteAccountError !== false"> <p v-if="deleteAccountError !== false">{{$t('settings.delete_account_error')}}</p>
{{ $t('settings.delete_account_error') }} <p v-if="deleteAccountError">{{deleteAccountError}}</p>
</p> <button class="btn btn-default" v-if="!deletingAccount" @click="confirmDelete">{{$t('general.submit')}}</button>
<p v-if="deleteAccountError">
{{ deleteAccountError }}
</p>
<button
v-if="!deletingAccount"
class="btn btn-default"
@click="confirmDelete"
>
{{ $t('general.submit') }}
</button>
</div> </div>
</div> </div>
<div <div :label="$t('settings.data_import_export_tab')" v-if="pleromaBackend">
v-if="pleromaBackend"
:label="$t('settings.data_import_export_tab')"
>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.follow_import') }}</h2> <h2>{{$t('settings.follow_import')}}</h2>
<p>{{ $t('settings.import_followers_from_a_csv_file') }}</p> <p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
<form> <form>
<input <input type="file" ref="followlist" v-on:change="followListChange" />
ref="followlist"
type="file"
@change="followListChange"
>
</form> </form>
<i <i class=" icon-spin4 animate-spin uploading" v-if="followListUploading"></i>
v-if="followListUploading" <button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button>
class=" icon-spin4 animate-spin uploading"
/>
<button
v-else
class="btn btn-default"
@click="importFollows"
>
{{ $t('general.submit') }}
</button>
<div v-if="followsImported"> <div v-if="followsImported">
<i <i class="icon-cross" @click="dismissImported"></i>
class="icon-cross" <p>{{$t('settings.follows_imported')}}</p>
@click="dismissImported"
/>
<p>{{ $t('settings.follows_imported') }}</p>
</div> </div>
<div v-else-if="followImportError"> <div v-else-if="followImportError">
<i <i class="icon-cross" @click="dismissImported"></i>
class="icon-cross" <p>{{$t('settings.follow_import_error')}}</p>
@click="dismissImported"
/>
<p>{{ $t('settings.follow_import_error') }}</p>
</div> </div>
</div> </div>
<div <div class="setting-item" v-if="enableFollowsExport">
v-if="enableFollowsExport" <h2>{{$t('settings.follow_export')}}</h2>
class="setting-item" <button class="btn btn-default" @click="exportFollows">{{$t('settings.follow_export_button')}}</button>
>
<h2>{{ $t('settings.follow_export') }}</h2>
<button
class="btn btn-default"
@click="exportFollows"
>
{{ $t('settings.follow_export_button') }}
</button>
</div> </div>
<div <div class="setting-item" v-else>
v-else <h2>{{$t('settings.follow_export_processing')}}</h2>
class="setting-item"
>
<h2>{{ $t('settings.follow_export_processing') }}</h2>
</div> </div>
</div> </div>
<div :label="$t('settings.blocks_tab')"> <div :label="$t('settings.blocks_tab')">
<block-list :refresh="true"> <block-list :refresh="true">
<template slot="empty"> <template slot="empty">{{$t('settings.no_blocks')}}</template>
{{ $t('settings.no_blocks') }}
</template>
</block-list> </block-list>
</div> </div>
<div :label="$t('settings.mutes_tab')"> <div :label="$t('settings.mutes_tab')">
<mute-list :refresh="true"> <mute-list :refresh="true">
<template slot="empty"> <template slot="empty">{{$t('settings.no_mutes')}}</template>
{{ $t('settings.no_mutes') }}
</template>
</mute-list> </mute-list>
</div> </div>
</tab-switcher> </tab-switcher>

View File

@ -1,11 +1,10 @@
<template> <template>
<video <video class="video"
class="video" @loadeddata="onVideoDataLoad"
:src="attachment.url" :src="attachment.url"
:loop="loopVideo" :loop="loopVideo"
:controls="controls" :controls="controls"
playsinline playsinline
@loadeddata="onVideoDataLoad"
/> />
</template> </template>

View File

@ -36,7 +36,7 @@ const WhoToFollow = {
getWhoToFollow () { getWhoToFollow () {
const credentials = this.$store.state.users.currentUser.credentials const credentials = this.$store.state.users.currentUser.credentials
if (credentials) { if (credentials) {
apiService.suggestions({ credentials: credentials }) apiService.suggestions({credentials: credentials})
.then((reply) => { .then((reply) => {
this.showWhoToFollow(reply) this.showWhoToFollow(reply)
}) })

View File

@ -1,14 +1,10 @@
<template> <template>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{{ $t('who_to_follow.who_to_follow') }} {{$t('who_to_follow.who_to_follow')}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<FollowCard <FollowCard v-for="user in users" :key="user.id" :user="user"/>
v-for="user in users"
:key="user.id"
:user="user"
/>
</div> </div>
</div> </div>
</template> </template>

View File

@ -29,7 +29,7 @@ function getWhoToFollow (panel) {
panel.usersToFollow.forEach(toFollow => { panel.usersToFollow.forEach(toFollow => {
toFollow.name = 'Loading...' toFollow.name = 'Loading...'
}) })
apiService.suggestions({ credentials: credentials }) apiService.suggestions({credentials: credentials})
.then((reply) => { .then((reply) => {
showWhoToFollow(panel, reply) showWhoToFollow(panel, reply)
}) })

View File

@ -3,22 +3,17 @@
<div class="panel panel-default base01-background"> <div class="panel panel-default base01-background">
<div class="panel-heading timeline-heading base02-background base04"> <div class="panel-heading timeline-heading base02-background base04">
<div class="title"> <div class="title">
{{ $t('who_to_follow.who_to_follow') }} {{$t('who_to_follow.who_to_follow')}}
</div> </div>
</div> </div>
<div class="panel-body who-to-follow"> <div class="panel-body who-to-follow">
<span <span v-for="user in usersToFollow">
v-for="user in usersToFollow" <img v-bind:src="user.img" />
:key="user.id" <router-link v-bind:to="userProfileLink(user.id, user.name)">
> {{user.name}}
<img :src="user.img"> </router-link><br />
<router-link :to="userProfileLink(user.id, user.name)">
{{ user.name }}
</router-link><br>
</span> </span>
<img :src="$store.state.instance.logo"> <router-link :to="{ name: 'who-to-follow' }"> <img v-bind:src="$store.state.instance.logo"> <router-link :to="{ name: 'who-to-follow' }">{{$t('who_to_follow.more')}}</router-link>
{{ $t('who_to_follow.more') }}
</router-link>
</div> </div>
</div> </div>
</div> </div>

View File

@ -7,14 +7,14 @@ const defaultEntryPropsGetter = entry => ({ entry })
const defaultKeyGetter = entry => entry.id const defaultKeyGetter = entry => entry.id
const withList = ({ const withList = ({
getEntryProps = defaultEntryPropsGetter, // function to accept entry and index values and return props to be passed into the item component getEntryProps = defaultEntryPropsGetter, // function to accept entry and index values and return props to be passed into the item component
getKey = defaultKeyGetter // funciton to accept entry and index values and return key prop value getKey = defaultKeyGetter // funciton to accept entry and index values and return key prop value
}) => (ItemComponent) => ( }) => (ItemComponent) => (
Vue.component('withList', { Vue.component('withList', {
props: [ props: [
'entries', // array of entry 'entries', // array of entry
'entryProps', // additional props to be passed into each entry 'entryProps', // additional props to be passed into each entry
'entryListeners' // additional event listeners to be passed into each entry 'entryListeners' // additional event listeners to be passed into each entry
], ],
render (createElement) { render (createElement) {
return ( return (

View File

@ -4,16 +4,39 @@ import { getComponentProps } from '../../services/component_utils/component_util
import './with_load_more.scss' import './with_load_more.scss'
const withLoadMore = ({ const withLoadMore = ({
fetch, // function to fetch entries and return a promise fetch, // function to fetch entries and return a promise
select, // function to select data from store select, // function to select data from store
destroy, // function called at "destroyed" lifecycle destroy, // function called at "destroyed" lifecycle
childPropName = 'entries', // name of the prop to be passed into the wrapped component childPropName = 'entries', // name of the prop to be passed into the wrapped component
additionalPropNames = [] // additional prop name list of the wrapper component additionalPropNames = [] // additional prop name list of the wrapper component
}) => (WrappedComponent) => { }) => (WrappedComponent) => {
const originalProps = Object.keys(getComponentProps(WrappedComponent)) const originalProps = Object.keys(getComponentProps(WrappedComponent))
const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames) const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames)
return Vue.component('withLoadMore', { return Vue.component('withLoadMore', {
render (createElement) {
const props = {
props: {
...this.$props,
[childPropName]: this.entries
},
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
return (
<div class="with-load-more">
<WrappedComponent {...props}>
{children}
</WrappedComponent>
<div class="with-load-more-footer">
{this.error && <a onClick={this.fetchEntries} class="alert error">{this.$t('general.generic_error')}</a>}
{!this.error && this.loading && <i class="icon-spin3 animate-spin"/>}
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>}
</div>
</div>
)
},
props, props,
data () { data () {
return { return {
@ -64,29 +87,6 @@ const withLoadMore = ({
this.fetchEntries() this.fetchEntries()
} }
} }
},
render (createElement) {
const props = {
props: {
...this.$props,
[childPropName]: this.entries
},
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
return (
<div class="with-load-more">
<WrappedComponent {...props}>
{children}
</WrappedComponent>
<div class="with-load-more-footer">
{this.error && <a onClick={this.fetchEntries} class="alert error">{this.$t('general.generic_error')}</a>}
{!this.error && this.loading && <i class="icon-spin3 animate-spin"/>}
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>}
</div>
</div>
)
} }
}) })
} }

View File

@ -4,10 +4,10 @@ import { getComponentProps } from '../../services/component_utils/component_util
import './with_subscription.scss' import './with_subscription.scss'
const withSubscription = ({ const withSubscription = ({
fetch, // function to fetch entries and return a promise fetch, // function to fetch entries and return a promise
select, // function to select data from store select, // function to select data from store
childPropName = 'content', // name of the prop to be passed into the wrapped component childPropName = 'content', // name of the prop to be passed into the wrapped component
additionalPropNames = [] // additional prop name list of the wrapper component additionalPropNames = [] // additional prop name list of the wrapper component
}) => (WrappedComponent) => { }) => (WrappedComponent) => {
const originalProps = Object.keys(getComponentProps(WrappedComponent)) const originalProps = Object.keys(getComponentProps(WrappedComponent))
const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames) const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames)
@ -15,8 +15,37 @@ const withSubscription = ({
return Vue.component('withSubscription', { return Vue.component('withSubscription', {
props: [ props: [
...props, ...props,
'refresh' // boolean saying to force-fetch data whenever created 'refresh' // boolean saying to force-fetch data whenever created
], ],
render (createElement) {
if (!this.error && !this.loading) {
const props = {
props: {
...this.$props,
[childPropName]: this.fetchedData
},
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
return (
<div class="with-subscription">
<WrappedComponent {...props}>
{children}
</WrappedComponent>
</div>
)
} else {
return (
<div class="with-subscription-loading">
{this.error
? <a onClick={this.fetchData} class="alert error">{this.$t('general.generic_error')}</a>
: <i class="icon-spin3 animate-spin"/>
}
</div>
)
}
},
data () { data () {
return { return {
loading: false, loading: false,
@ -48,35 +77,6 @@ const withSubscription = ({
}) })
} }
} }
},
render (createElement) {
if (!this.error && !this.loading) {
const props = {
props: {
...this.$props,
[childPropName]: this.fetchedData
},
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
return (
<div class="with-subscription">
<WrappedComponent {...props}>
{children}
</WrappedComponent>
</div>
)
} else {
return (
<div class="with-subscription-loading">
{this.error
? <a onClick={this.fetchData} class="alert error">{this.$t('general.generic_error')}</a>
: <i class="icon-spin3 animate-spin"/>
}
</div>
)
}
} }
}) })
} }

View File

@ -13,10 +13,10 @@ const api = {
setBackendInteractor (state, backendInteractor) { setBackendInteractor (state, backendInteractor) {
state.backendInteractor = backendInteractor state.backendInteractor = backendInteractor
}, },
addFetcher (state, { timeline, fetcher }) { addFetcher (state, {timeline, fetcher}) {
state.fetchers[timeline] = fetcher state.fetchers[timeline] = fetcher
}, },
removeFetcher (state, { timeline }) { removeFetcher (state, {timeline}) {
delete state.fetchers[timeline] delete state.fetchers[timeline]
}, },
setWsToken (state, token) { setWsToken (state, token) {
@ -33,7 +33,7 @@ const api = {
} }
}, },
actions: { actions: {
startFetching (store, { timeline = 'friends', tag = false, userId = false }) { startFetching (store, {timeline = 'friends', tag = false, userId = false}) {
// Don't start fetching if we already are. // Don't start fetching if we already are.
if (store.state.fetchers[timeline]) return if (store.state.fetchers[timeline]) return
@ -43,7 +43,7 @@ const api = {
stopFetching (store, timeline) { stopFetching (store, timeline) {
const fetcher = store.state.fetchers[timeline] const fetcher = store.state.fetchers[timeline]
window.clearInterval(fetcher) window.clearInterval(fetcher)
store.commit('removeFetcher', { timeline }) store.commit('removeFetcher', {timeline})
}, },
setWsToken (store, token) { setWsToken (store, token) {
store.commit('setWsToken', token) store.commit('setWsToken', token)
@ -52,7 +52,7 @@ const api = {
// Set up websocket connection // Set up websocket connection
if (!store.state.chatDisabled) { if (!store.state.chatDisabled) {
const token = store.state.wsToken const token = store.state.wsToken
const socket = new Socket('/socket', { params: { token } }) const socket = new Socket('/socket', {params: {token}})
socket.connect() socket.connect()
store.dispatch('initializeChat', socket) store.dispatch('initializeChat', socket)
} }

View File

@ -1,7 +1,7 @@
const chat = { const chat = {
state: { state: {
messages: [], messages: [],
channel: { state: '' }, channel: {state: ''},
socket: null socket: null
}, },
mutations: { mutations: {
@ -29,7 +29,7 @@ const chat = {
channel.on('new_msg', (msg) => { channel.on('new_msg', (msg) => {
store.commit('addMessage', msg) store.commit('addMessage', msg)
}) })
channel.on('messages', ({ messages }) => { channel.on('messages', ({messages}) => {
store.commit('setMessages', messages) store.commit('setMessages', messages)
}) })
channel.join() channel.join()

View File

@ -54,10 +54,10 @@ const config = {
}, },
actions: { actions: {
setHighlight ({ commit, dispatch }, { user, color, type }) { setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', { user, color, type }) commit('setHighlight', {user, color, type})
}, },
setOption ({ commit, dispatch }, { name, value }) { setOption ({ commit, dispatch }, { name, value }) {
commit('setOption', { name, value }) commit('setOption', {name, value})
switch (name) { switch (name) {
case 'theme': case 'theme':
setPreset(value, commit) setPreset(value, commit)

View File

@ -9,3 +9,4 @@ export function humanizeErrors (errors) {
return [...errs, message] return [...errs, message]
}, []) }, [])
} }

View File

@ -68,7 +68,7 @@ const instance = {
}, },
actions: { actions: {
setInstanceOption ({ commit, dispatch }, { name, value }) { setInstanceOption ({ commit, dispatch }, { name, value }) {
commit('setInstanceOption', { name, value }) commit('setInstanceOption', {name, value})
switch (name) { switch (name) {
case 'name': case 'name':
dispatch('setPageTitle') dispatch('setPageTitle')

View File

@ -3,12 +3,12 @@ const oauthTokens = {
tokens: [] tokens: []
}, },
actions: { actions: {
fetchTokens ({ rootState, commit }) { fetchTokens ({rootState, commit}) {
rootState.api.backendInteractor.fetchOAuthTokens().then((tokens) => { rootState.api.backendInteractor.fetchOAuthTokens().then((tokens) => {
commit('swapTokens', tokens) commit('swapTokens', tokens)
}) })
}, },
revokeToken ({ rootState, commit, state }, id) { revokeToken ({rootState, commit, state}, id) {
rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => { rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => {
if (response.status === 201) { if (response.status === 201) {
commit('swapTokens', state.tokens.filter(token => token.id !== id)) commit('swapTokens', state.tokens.filter(token => token.id !== id))

View File

@ -78,13 +78,13 @@ const mergeOrAdd = (arr, obj, item) => {
merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user')) merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user'))
// Reactivity fix. // Reactivity fix.
oldItem.attachments.splice(oldItem.attachments.length) oldItem.attachments.splice(oldItem.attachments.length)
return { item: oldItem, new: false } return {item: oldItem, new: false}
} else { } else {
// This is a new item, prepare it // This is a new item, prepare it
prepareStatus(item) prepareStatus(item)
arr.push(item) arr.push(item)
set(obj, item.id, item) set(obj, item.id, item)
return { item, new: true } return {item, new: true}
} }
} }
@ -237,12 +237,12 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
const uri = deletion.uri const uri = deletion.uri
// Remove possible notification // Remove possible notification
const status = find(allStatuses, { uri }) const status = find(allStatuses, {uri})
if (!status) { if (!status) {
return return
} }
remove(state.notifications.data, ({ action: { id } }) => id === status.id) remove(state.notifications.data, ({action: {id}}) => id === status.id)
remove(allStatuses, { uri }) remove(allStatuses, { uri })
if (timeline) { if (timeline) {

View File

@ -224,8 +224,8 @@ const users = {
.then((friends) => { .then((friends) => {
commit('addFriends', { id: user.id, friends }) commit('addFriends', { id: user.id, friends })
resolve(friends) resolve(friends)
}).catch(e => { }).catch(() => {
reject(e) reject()
}) })
}) })
}, },
@ -279,8 +279,8 @@ const users = {
const notificationsObject = store.rootState.statuses.notifications.idStore const notificationsObject = store.rootState.statuses.notifications.idStore
const relevantNotifications = Object.entries(notificationsObject) const relevantNotifications = Object.entries(notificationsObject)
.filter(([k, val]) => notificationIds.includes(k)) .filter(([k, val]) => notificationIds.includes(k))
.map(([k, val]) => val) .map(([k, val]) => val)
// Reconnect users to notifications // Reconnect users to notifications
each(relevantNotifications, (notification) => { each(relevantNotifications, (notification) => {
@ -322,7 +322,7 @@ const users = {
} }
}, },
async getCaptcha (store) { async getCaptcha (store) {
return store.rootState.api.backendInteractor.getCaptcha() return await store.rootState.api.backendInteractor.getCaptcha()
}, },
logout (store) { logout (store) {
@ -376,19 +376,19 @@ const users = {
// Authentication failed // Authentication failed
commit('endLogin') commit('endLogin')
if (response.status === 401) { if (response.status === 401) {
reject(new Error('Wrong username or password')) reject('Wrong username or password')
} else { } else {
reject(new Error('An error occurred, please try again')) reject('An error occurred, please try again')
} }
} }
commit('endLogin') commit('endLogin')
resolve() resolve()
}) })
.catch((error) => { .catch((error) => {
console.log(error) console.log(error)
commit('endLogin') commit('endLogin')
reject(new Error('Failed to connect to server, try again')) reject('Failed to connect to server, try again')
}) })
}) })
} }
} }

View File

@ -1,9 +1,4 @@
/* eslint-env browser */ /* eslint-env browser */
import { each, map } from 'lodash'
import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
import 'whatwg-fetch'
import { StatusCodeError } from '../errors/errors'
const LOGIN_URL = '/api/account/verify_credentials.json' const LOGIN_URL = '/api/account/verify_credentials.json'
const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing'
const MENTIONS_URL = '/api/statuses/mentions.json' const MENTIONS_URL = '/api/statuses/mentions.json'
@ -51,6 +46,11 @@ const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
const MASTODON_POST_STATUS_URL = '/api/v1/statuses' const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media' const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
import { each, map } from 'lodash'
import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
import 'whatwg-fetch'
import { StatusCodeError } from '../errors/errors'
const oldfetch = window.fetch const oldfetch = window.fetch
let fetch = (url, options) => { let fetch = (url, options) => {
@ -80,7 +80,7 @@ const promisedRequest = (url, options) => {
// cropX // cropX
// cropY // cropY
// img (base 64 encodend data url) // img (base 64 encodend data url)
const updateAvatar = ({ credentials, params }) => { const updateAvatar = ({credentials, params}) => {
let url = AVATAR_UPDATE_URL let url = AVATAR_UPDATE_URL
const form = new FormData() const form = new FormData()
@ -98,7 +98,7 @@ const updateAvatar = ({ credentials, params }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const updateBg = ({ credentials, params }) => { const updateBg = ({credentials, params}) => {
let url = BG_UPDATE_URL let url = BG_UPDATE_URL
const form = new FormData() const form = new FormData()
@ -122,7 +122,7 @@ const updateBg = ({ credentials, params }) => {
// offset_left // offset_left
// offset_top // offset_top
// banner (base 64 encodend data url) // banner (base 64 encodend data url)
const updateBanner = ({ credentials, params }) => { const updateBanner = ({credentials, params}) => {
let url = BANNER_UPDATE_URL let url = BANNER_UPDATE_URL
const form = new FormData() const form = new FormData()
@ -145,7 +145,7 @@ const updateBanner = ({ credentials, params }) => {
// url // url
// location // location
// description // description
const updateProfile = ({ credentials, params }) => { const updateProfile = ({credentials, params}) => {
// Always include these fields, because they might be empty or false // Always include these fields, because they might be empty or false
const fields = ['description', 'locked', 'no_rich_text', 'hide_follows', 'hide_followers', 'show_role'] const fields = ['description', 'locked', 'no_rich_text', 'hide_follows', 'hide_followers', 'show_role']
let url = PROFILE_UPDATE_URL let url = PROFILE_UPDATE_URL
@ -201,7 +201,7 @@ const authHeaders = (accessToken) => {
} }
} }
const externalProfile = ({ profileUrl, credentials }) => { const externalProfile = ({profileUrl, credentials}) => {
let url = `${EXTERNAL_PROFILE_URL}?profileurl=${profileUrl}` let url = `${EXTERNAL_PROFILE_URL}?profileurl=${profileUrl}`
return fetch(url, { return fetch(url, {
headers: authHeaders(credentials), headers: authHeaders(credentials),
@ -209,7 +209,7 @@ const externalProfile = ({ profileUrl, credentials }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const followUser = ({ id, credentials }) => { const followUser = ({id, credentials}) => {
let url = MASTODON_FOLLOW_URL(id) let url = MASTODON_FOLLOW_URL(id)
return fetch(url, { return fetch(url, {
headers: authHeaders(credentials), headers: authHeaders(credentials),
@ -217,7 +217,7 @@ const followUser = ({ id, credentials }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const unfollowUser = ({ id, credentials }) => { const unfollowUser = ({id, credentials}) => {
let url = MASTODON_UNFOLLOW_URL(id) let url = MASTODON_UNFOLLOW_URL(id)
return fetch(url, { return fetch(url, {
headers: authHeaders(credentials), headers: authHeaders(credentials),
@ -225,21 +225,21 @@ const unfollowUser = ({ id, credentials }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const blockUser = ({ id, credentials }) => { const blockUser = ({id, credentials}) => {
return fetch(MASTODON_BLOCK_USER_URL(id), { return fetch(MASTODON_BLOCK_USER_URL(id), {
headers: authHeaders(credentials), headers: authHeaders(credentials),
method: 'POST' method: 'POST'
}).then((data) => data.json()) }).then((data) => data.json())
} }
const unblockUser = ({ id, credentials }) => { const unblockUser = ({id, credentials}) => {
return fetch(MASTODON_UNBLOCK_USER_URL(id), { return fetch(MASTODON_UNBLOCK_USER_URL(id), {
headers: authHeaders(credentials), headers: authHeaders(credentials),
method: 'POST' method: 'POST'
}).then((data) => data.json()) }).then((data) => data.json())
} }
const approveUser = ({ id, credentials }) => { const approveUser = ({id, credentials}) => {
let url = `${APPROVE_USER_URL}?user_id=${id}` let url = `${APPROVE_USER_URL}?user_id=${id}`
return fetch(url, { return fetch(url, {
headers: authHeaders(credentials), headers: authHeaders(credentials),
@ -247,7 +247,7 @@ const approveUser = ({ id, credentials }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const denyUser = ({ id, credentials }) => { const denyUser = ({id, credentials}) => {
let url = `${DENY_USER_URL}?user_id=${id}` let url = `${DENY_USER_URL}?user_id=${id}`
return fetch(url, { return fetch(url, {
headers: authHeaders(credentials), headers: authHeaders(credentials),
@ -255,13 +255,13 @@ const denyUser = ({ id, credentials }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const fetchUser = ({ id, credentials }) => { const fetchUser = ({id, credentials}) => {
let url = `${MASTODON_USER_URL}/${id}` let url = `${MASTODON_USER_URL}/${id}`
return promisedRequest(url, { headers: authHeaders(credentials) }) return promisedRequest(url, { headers: authHeaders(credentials) })
.then((data) => parseUser(data)) .then((data) => parseUser(data))
} }
const fetchUserRelationship = ({ id, credentials }) => { const fetchUserRelationship = ({id, credentials}) => {
let url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}` let url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}`
return fetch(url, { headers: authHeaders(credentials) }) return fetch(url, { headers: authHeaders(credentials) })
.then((response) => { .then((response) => {
@ -275,7 +275,7 @@ const fetchUserRelationship = ({ id, credentials }) => {
}) })
} }
const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => { const fetchFriends = ({id, maxId, sinceId, limit = 20, credentials}) => {
let url = MASTODON_FOLLOWING_URL(id) let url = MASTODON_FOLLOWING_URL(id)
const args = [ const args = [
maxId && `max_id=${maxId}`, maxId && `max_id=${maxId}`,
@ -289,14 +289,14 @@ const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => {
.then((data) => data.map(parseUser)) .then((data) => data.map(parseUser))
} }
const exportFriends = ({ id, credentials }) => { const exportFriends = ({id, credentials}) => {
let url = MASTODON_FOLLOWING_URL(id) + `?all=true` let url = MASTODON_FOLLOWING_URL(id) + `?all=true`
return fetch(url, { headers: authHeaders(credentials) }) return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json()) .then((data) => data.json())
.then((data) => data.map(parseUser)) .then((data) => data.map(parseUser))
} }
const fetchFollowers = ({ id, maxId, sinceId, limit = 20, credentials }) => { const fetchFollowers = ({id, maxId, sinceId, limit = 20, credentials}) => {
let url = MASTODON_FOLLOWERS_URL(id) let url = MASTODON_FOLLOWERS_URL(id)
const args = [ const args = [
maxId && `max_id=${maxId}`, maxId && `max_id=${maxId}`,
@ -310,20 +310,20 @@ const fetchFollowers = ({ id, maxId, sinceId, limit = 20, credentials }) => {
.then((data) => data.map(parseUser)) .then((data) => data.map(parseUser))
} }
const fetchAllFollowing = ({ username, credentials }) => { const fetchAllFollowing = ({username, credentials}) => {
const url = `${ALL_FOLLOWING_URL}/${username}.json` const url = `${ALL_FOLLOWING_URL}/${username}.json`
return fetch(url, { headers: authHeaders(credentials) }) return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json()) .then((data) => data.json())
.then((data) => data.map(parseUser)) .then((data) => data.map(parseUser))
} }
const fetchFollowRequests = ({ credentials }) => { const fetchFollowRequests = ({credentials}) => {
const url = FOLLOW_REQUESTS_URL const url = FOLLOW_REQUESTS_URL
return fetch(url, { headers: authHeaders(credentials) }) return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json()) .then((data) => data.json())
} }
const fetchConversation = ({ id, credentials }) => { const fetchConversation = ({id, credentials}) => {
let urlContext = MASTODON_STATUS_CONTEXT_URL(id) let urlContext = MASTODON_STATUS_CONTEXT_URL(id)
return fetch(urlContext, { headers: authHeaders(credentials) }) return fetch(urlContext, { headers: authHeaders(credentials) })
.then((data) => { .then((data) => {
@ -333,13 +333,13 @@ const fetchConversation = ({ id, credentials }) => {
throw new Error('Error fetching timeline', data) throw new Error('Error fetching timeline', data)
}) })
.then((data) => data.json()) .then((data) => data.json())
.then(({ ancestors, descendants }) => ({ .then(({ancestors, descendants}) => ({
ancestors: ancestors.map(parseStatus), ancestors: ancestors.map(parseStatus),
descendants: descendants.map(parseStatus) descendants: descendants.map(parseStatus)
})) }))
} }
const fetchStatus = ({ id, credentials }) => { const fetchStatus = ({id, credentials}) => {
let url = MASTODON_STATUS_URL(id) let url = MASTODON_STATUS_URL(id)
return fetch(url, { headers: authHeaders(credentials) }) return fetch(url, { headers: authHeaders(credentials) })
.then((data) => { .then((data) => {
@ -352,7 +352,7 @@ const fetchStatus = ({ id, credentials }) => {
.then((data) => parseStatus(data)) .then((data) => parseStatus(data))
} }
const fetchTimeline = ({ timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false }) => { const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => {
const timelineUrls = { const timelineUrls = {
public: MASTODON_PUBLIC_TIMELINE, public: MASTODON_PUBLIC_TIMELINE,
friends: MASTODON_USER_HOME_TIMELINE_URL, friends: MASTODON_USER_HOME_TIMELINE_URL,
@ -487,7 +487,7 @@ const unretweet = ({ id, credentials }) => {
.then((data) => parseStatus(data)) .then((data) => parseStatus(data))
} }
const postStatus = ({ credentials, status, spoilerText, visibility, sensitive, mediaIds = [], inReplyToStatusId, contentType }) => { const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds = [], inReplyToStatusId, contentType}) => {
const form = new FormData() const form = new FormData()
form.append('status', status) form.append('status', status)
@ -527,7 +527,7 @@ const deleteStatus = ({ id, credentials }) => {
}) })
} }
const uploadMedia = ({ formData, credentials }) => { const uploadMedia = ({formData, credentials}) => {
return fetch(MASTODON_MEDIA_UPLOAD_URL, { return fetch(MASTODON_MEDIA_UPLOAD_URL, {
body: formData, body: formData,
method: 'POST', method: 'POST',
@ -537,7 +537,7 @@ const uploadMedia = ({ formData, credentials }) => {
.then((data) => parseAttachment(data)) .then((data) => parseAttachment(data))
} }
const followImport = ({ params, credentials }) => { const followImport = ({params, credentials}) => {
return fetch(FOLLOW_IMPORT_URL, { return fetch(FOLLOW_IMPORT_URL, {
body: params, body: params,
method: 'POST', method: 'POST',
@ -546,7 +546,7 @@ const followImport = ({ params, credentials }) => {
.then((response) => response.ok) .then((response) => response.ok)
} }
const deleteAccount = ({ credentials, password }) => { const deleteAccount = ({credentials, password}) => {
const form = new FormData() const form = new FormData()
form.append('password', password) form.append('password', password)
@ -559,7 +559,7 @@ const deleteAccount = ({ credentials, password }) => {
.then((response) => response.json()) .then((response) => response.json())
} }
const changePassword = ({ credentials, password, newPassword, newPasswordConfirmation }) => { const changePassword = ({credentials, password, newPassword, newPasswordConfirmation}) => {
const form = new FormData() const form = new FormData()
form.append('password', password) form.append('password', password)
@ -574,31 +574,31 @@ const changePassword = ({ credentials, password, newPassword, newPasswordConfirm
.then((response) => response.json()) .then((response) => response.json())
} }
const fetchMutes = ({ credentials }) => { const fetchMutes = ({credentials}) => {
return promisedRequest(MASTODON_USER_MUTES_URL, { headers: authHeaders(credentials) }) return promisedRequest(MASTODON_USER_MUTES_URL, { headers: authHeaders(credentials) })
.then((users) => users.map(parseUser)) .then((users) => users.map(parseUser))
} }
const muteUser = ({ id, credentials }) => { const muteUser = ({id, credentials}) => {
return promisedRequest(MASTODON_MUTE_USER_URL(id), { return promisedRequest(MASTODON_MUTE_USER_URL(id), {
headers: authHeaders(credentials), headers: authHeaders(credentials),
method: 'POST' method: 'POST'
}) })
} }
const unmuteUser = ({ id, credentials }) => { const unmuteUser = ({id, credentials}) => {
return promisedRequest(MASTODON_UNMUTE_USER_URL(id), { return promisedRequest(MASTODON_UNMUTE_USER_URL(id), {
headers: authHeaders(credentials), headers: authHeaders(credentials),
method: 'POST' method: 'POST'
}) })
} }
const fetchBlocks = ({ credentials }) => { const fetchBlocks = ({credentials}) => {
return promisedRequest(MASTODON_USER_BLOCKS_URL, { headers: authHeaders(credentials) }) return promisedRequest(MASTODON_USER_BLOCKS_URL, { headers: authHeaders(credentials) })
.then((users) => users.map(parseUser)) .then((users) => users.map(parseUser))
} }
const fetchOAuthTokens = ({ credentials }) => { const fetchOAuthTokens = ({credentials}) => {
const url = '/api/oauth_tokens.json' const url = '/api/oauth_tokens.json'
return fetch(url, { return fetch(url, {
@ -611,7 +611,7 @@ const fetchOAuthTokens = ({ credentials }) => {
}) })
} }
const revokeOAuthToken = ({ id, credentials }) => { const revokeOAuthToken = ({id, credentials}) => {
const url = `/api/oauth_tokens/${id}` const url = `/api/oauth_tokens/${id}`
return fetch(url, { return fetch(url, {
@ -620,13 +620,13 @@ const revokeOAuthToken = ({ id, credentials }) => {
}) })
} }
const suggestions = ({ credentials }) => { const suggestions = ({credentials}) => {
return fetch(SUGGESTIONS_URL, { return fetch(SUGGESTIONS_URL, {
headers: authHeaders(credentials) headers: authHeaders(credentials)
}).then((data) => data.json()) }).then((data) => data.json())
} }
const markNotificationsAsSeen = ({ id, credentials }) => { const markNotificationsAsSeen = ({id, credentials}) => {
const body = new FormData() const body = new FormData()
body.append('latest_id', id) body.append('latest_id', id)

Some files were not shown because too many files have changed in this diff Show More