Merge branch 'develop' into 'master'
Update master with bugfixes (and other changes) See merge request pleroma/pleroma-fe!673
This commit is contained in:
commit
7c26435e66
@ -8,6 +8,7 @@ import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_pan
|
|||||||
import ChatPanel from './components/chat_panel/chat_panel.vue'
|
import ChatPanel from './components/chat_panel/chat_panel.vue'
|
||||||
import MediaModal from './components/media_modal/media_modal.vue'
|
import MediaModal from './components/media_modal/media_modal.vue'
|
||||||
import SideDrawer from './components/side_drawer/side_drawer.vue'
|
import SideDrawer from './components/side_drawer/side_drawer.vue'
|
||||||
|
import MobilePostStatusModal from './components/mobile_post_status_modal/mobile_post_status_modal.vue'
|
||||||
import { unseenNotificationsFromStore } from './services/notification_utils/notification_utils'
|
import { unseenNotificationsFromStore } from './services/notification_utils/notification_utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -22,7 +23,8 @@ export default {
|
|||||||
WhoToFollowPanel,
|
WhoToFollowPanel,
|
||||||
ChatPanel,
|
ChatPanel,
|
||||||
MediaModal,
|
MediaModal,
|
||||||
SideDrawer
|
SideDrawer,
|
||||||
|
MobilePostStatusModal
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
mobileActivePanel: 'timeline',
|
mobileActivePanel: 'timeline',
|
||||||
|
25
src/App.scss
25
src/App.scss
@ -671,6 +671,31 @@ nav {
|
|||||||
border-radius: var(--inputRadius, $fallback--inputRadius);
|
border-radius: var(--inputRadius, $fallback--inputRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes modal-background-fadein {
|
||||||
|
from {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-view {
|
||||||
|
z-index: 1000;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
animation-name: modal-background-fadein;
|
||||||
|
}
|
||||||
|
|
||||||
.button-icon {
|
.button-icon {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
<media-modal></media-modal>
|
<media-modal></media-modal>
|
||||||
</div>
|
</div>
|
||||||
<chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel>
|
<chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel>
|
||||||
|
<MobilePostStatusModal />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -89,10 +89,8 @@ const afterStoreSetup = ({ store, i18n }) => {
|
|||||||
copyInstanceOption('noAttachmentLinks')
|
copyInstanceOption('noAttachmentLinks')
|
||||||
copyInstanceOption('showFeaturesPanel')
|
copyInstanceOption('showFeaturesPanel')
|
||||||
|
|
||||||
if ((config.chatDisabled)) {
|
if (config.chatDisabled) {
|
||||||
store.dispatch('disableChat')
|
store.dispatch('disableChat')
|
||||||
} else {
|
|
||||||
store.dispatch('initializeSocket')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return store.dispatch('setTheme', config['theme'])
|
return store.dispatch('setTheme', config['theme'])
|
||||||
@ -169,6 +167,8 @@ const afterStoreSetup = ({ store, i18n }) => {
|
|||||||
store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') })
|
store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') })
|
||||||
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
|
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
|
||||||
|
|
||||||
|
store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })
|
||||||
|
|
||||||
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
|
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
|
||||||
|
|
||||||
const suggestions = metadata.suggestions
|
const suggestions = metadata.suggestions
|
||||||
|
@ -13,7 +13,6 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue'
|
|||||||
import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
|
import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
|
||||||
import UserSearch from 'components/user_search/user_search.vue'
|
import UserSearch from 'components/user_search/user_search.vue'
|
||||||
import Notifications from 'components/notifications/notifications.vue'
|
import Notifications from 'components/notifications/notifications.vue'
|
||||||
import UserPanel from 'components/user_panel/user_panel.vue'
|
|
||||||
import LoginForm from 'components/login_form/login_form.vue'
|
import LoginForm from 'components/login_form/login_form.vue'
|
||||||
import ChatPanel from 'components/chat_panel/chat_panel.vue'
|
import ChatPanel from 'components/chat_panel/chat_panel.vue'
|
||||||
import WhoToFollow from 'components/who_to_follow/who_to_follow.vue'
|
import WhoToFollow from 'components/who_to_follow/who_to_follow.vue'
|
||||||
@ -43,7 +42,6 @@ export default (store) => {
|
|||||||
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests },
|
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests },
|
||||||
{ name: 'user-settings', path: '/user-settings', component: UserSettings },
|
{ name: 'user-settings', path: '/user-settings', component: UserSettings },
|
||||||
{ name: 'notifications', path: '/:username/notifications', component: Notifications },
|
{ name: 'notifications', path: '/:username/notifications', component: Notifications },
|
||||||
{ name: 'new-status', path: '/:username/new-status', component: UserPanel },
|
|
||||||
{ name: 'login', path: '/login', component: LoginForm },
|
{ name: 'login', path: '/login', component: LoginForm },
|
||||||
{ name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) },
|
{ name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) },
|
||||||
{ name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
|
{ name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
|
||||||
|
@ -160,6 +160,7 @@
|
|||||||
|
|
||||||
.hider {
|
.hider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ const BasicUserCard = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
UserCardContent,
|
UserCard,
|
||||||
UserAvatar
|
UserAvatar
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user-card">
|
<div class="basic-user-card">
|
||||||
<router-link :to="userProfileLink(user)">
|
<router-link :to="userProfileLink(user)">
|
||||||
<UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
|
<UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="user-card-expanded-content" v-if="userExpanded">
|
<div class="basic-user-card-expanded-content" v-if="userExpanded">
|
||||||
<user-card-content :user="user" :switcher="false"></user-card-content>
|
<UserCard :user="user" :rounded="true" :bordered="true"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-card-collapsed-content" v-else>
|
<div class="basic-user-card-collapsed-content" v-else>
|
||||||
<div :title="user.name" class="user-card-user-name">
|
<div :title="user.name" class="basic-user-card-user-name">
|
||||||
<span v-if="user.name_html" v-html="user.name_html"></span>
|
<span v-if="user.name_html" v-html="user.name_html"></span>
|
||||||
<span v-else>{{ user.name }}</span>
|
<span v-else>{{ user.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<router-link class="user-card-screen-name" :to="userProfileLink(user)">
|
<router-link class="basic-user-card-screen-name" :to="userProfileLink(user)">
|
||||||
@{{user.screen_name}}
|
@{{user.screen_name}}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
@ -26,15 +26,15 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.user-card {
|
.basic-user-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 0;
|
flex: 1 0;
|
||||||
|
margin: 0;
|
||||||
padding-top: 0.6em;
|
padding-top: 0.6em;
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
padding-bottom: 0.6em;
|
padding-bottom: 0.6em;
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
margin: 0;
|
|
||||||
border-bottom-color: $fallback--border;
|
border-bottom-color: $fallback--border;
|
||||||
border-bottom-color: var(--border, $fallback--border);
|
border-bottom-color: var(--border, $fallback--border);
|
||||||
|
|
||||||
@ -57,23 +57,6 @@
|
|||||||
&-expanded-content {
|
&-expanded-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-left: 0.7em;
|
margin-left: 0.7em;
|
||||||
border-radius: $fallback--panelRadius;
|
|
||||||
border-radius: var(--panelRadius, $fallback--panelRadius);
|
|
||||||
border-style: solid;
|
|
||||||
border-color: $fallback--border;
|
|
||||||
border-color: var(--border, $fallback--border);
|
|
||||||
border-width: 1px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.panel-heading {
|
|
||||||
background: transparent;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="modal-view" v-if="showing" @click.prevent="hide">
|
<div class="modal-view media-modal-view" v-if="showing" @click.prevent="hide">
|
||||||
<img class="modal-image" v-if="type === 'image'" :src="currentMedia.url"></img>
|
<img class="modal-image" v-if="type === 'image'" :src="currentMedia.url"></img>
|
||||||
<VideoAttachment
|
<VideoAttachment
|
||||||
class="modal-image"
|
class="modal-image"
|
||||||
@ -32,18 +32,7 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.modal-view {
|
.media-modal-view {
|
||||||
z-index: 1000;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.modal-view-button-arrow {
|
.modal-view-button-arrow {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
|
import { throttle } from 'lodash'
|
||||||
|
|
||||||
|
const MobilePostStatusModal = {
|
||||||
|
components: {
|
||||||
|
PostStatusForm
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
hidden: false,
|
||||||
|
postFormOpen: false,
|
||||||
|
scrollingDown: false,
|
||||||
|
inputActive: false,
|
||||||
|
oldScrollPos: 0,
|
||||||
|
amountScrolled: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('scroll', this.handleScroll)
|
||||||
|
window.addEventListener('resize', this.handleOSK)
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('scroll', this.handleScroll)
|
||||||
|
window.removeEventListener('resize', this.handleOSK)
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentUser () {
|
||||||
|
return this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
isHidden () {
|
||||||
|
return this.hidden || this.inputActive
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openPostForm () {
|
||||||
|
this.postFormOpen = true
|
||||||
|
this.hidden = true
|
||||||
|
|
||||||
|
const el = this.$el.querySelector('textarea')
|
||||||
|
this.$nextTick(function () {
|
||||||
|
el.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closePostForm () {
|
||||||
|
this.postFormOpen = false
|
||||||
|
this.hidden = false
|
||||||
|
},
|
||||||
|
handleOSK () {
|
||||||
|
// This is a big hack: we're guessing from changed window sizes if the
|
||||||
|
// on-screen keyboard is active or not. This is only really important
|
||||||
|
// for phones in portrait mode and it's more important to show the button
|
||||||
|
// in normal scenarios on all phones, than it is to hide it when the
|
||||||
|
// keyboard is active.
|
||||||
|
// Guesswork based on https://www.mydevice.io/#compare-devices
|
||||||
|
|
||||||
|
// for example, iphone 4 and android phones from the same time period
|
||||||
|
const smallPhone = window.innerWidth < 350
|
||||||
|
const smallPhoneKbOpen = smallPhone && window.innerHeight < 345
|
||||||
|
|
||||||
|
const biggerPhone = !smallPhone && window.innerWidth < 450
|
||||||
|
const biggerPhoneKbOpen = biggerPhone && window.innerHeight < 560
|
||||||
|
if (smallPhoneKbOpen || biggerPhoneKbOpen) {
|
||||||
|
this.inputActive = true
|
||||||
|
} else {
|
||||||
|
this.inputActive = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleScroll: throttle(function () {
|
||||||
|
const scrollAmount = window.scrollY - this.oldScrollPos
|
||||||
|
const scrollingDown = scrollAmount > 0
|
||||||
|
|
||||||
|
if (scrollingDown !== this.scrollingDown) {
|
||||||
|
this.amountScrolled = 0
|
||||||
|
this.scrollingDown = scrollingDown
|
||||||
|
if (!scrollingDown) {
|
||||||
|
this.hidden = false
|
||||||
|
}
|
||||||
|
} else if (scrollingDown) {
|
||||||
|
this.amountScrolled += scrollAmount
|
||||||
|
if (this.amountScrolled > 100 && !this.hidden) {
|
||||||
|
this.hidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.oldScrollPos = window.scrollY
|
||||||
|
this.scrollingDown = scrollingDown
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MobilePostStatusModal
|
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="currentUser">
|
||||||
|
<div
|
||||||
|
class="post-form-modal-view modal-view"
|
||||||
|
v-show="postFormOpen"
|
||||||
|
@click="closePostForm"
|
||||||
|
>
|
||||||
|
<div class="post-form-modal-panel panel" @click.stop="">
|
||||||
|
<div class="panel-heading">{{$t('post_status.new_status')}}</div>
|
||||||
|
<PostStatusForm class="panel-body" @posted="closePostForm"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="new-status-button"
|
||||||
|
:class="{ 'hidden': isHidden }"
|
||||||
|
@click="openPostForm"
|
||||||
|
>
|
||||||
|
<i class="icon-edit" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./mobile_post_status_modal.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.post-form-modal-view {
|
||||||
|
max-height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-form-modal-panel {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 25% 0 4em 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-status-button {
|
||||||
|
width: 5em;
|
||||||
|
height: 5em;
|
||||||
|
border-radius: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1.5em;
|
||||||
|
right: 1.5em;
|
||||||
|
// TODO: this needs its own color, it has to stand out enough and link color
|
||||||
|
// is not very optimal for this particular use.
|
||||||
|
background-color: $fallback--fg;
|
||||||
|
background-color: var(--btn, $fallback--fg);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
transition: 0.35s transform;
|
||||||
|
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
transform: translateY(150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: $fallback--text;
|
||||||
|
color: var(--text, $fallback--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 801px) {
|
||||||
|
.new-status-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -1,6 +1,6 @@
|
|||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ const Notification = {
|
|||||||
},
|
},
|
||||||
props: [ 'notification' ],
|
props: [ 'notification' ],
|
||||||
components: {
|
components: {
|
||||||
Status, UserAvatar, UserCardContent
|
Status, UserAvatar, UserCard
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleUserExpanded () {
|
toggleUserExpanded () {
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
<UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/>
|
<UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/>
|
||||||
</a>
|
</a>
|
||||||
<div class='notification-right'>
|
<div class='notification-right'>
|
||||||
<div class="usercard notification-usercard" v-if="userExpanded">
|
<UserCard :user="notification.action.user" :rounded="true" :bordered="true" v-if="userExpanded"/>
|
||||||
<user-card-content :user="notification.action.user" :switcher="false"></user-card-content>
|
|
||||||
</div>
|
|
||||||
<span class="notification-details">
|
<span class="notification-details">
|
||||||
<div class="name-and-action">
|
<div class="name-and-action">
|
||||||
<span class="username" v-if="!!notification.action.user.name_html" :title="'@'+notification.action.user.screen_name" v-html="notification.action.user.name_html"></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>
|
||||||
|
@ -11,7 +11,8 @@ const Notifications = {
|
|||||||
const store = this.$store
|
const store = this.$store
|
||||||
const credentials = store.state.users.currentUser.credentials
|
const credentials = store.state.users.currentUser.credentials
|
||||||
|
|
||||||
notificationsFetcher.startFetching({ store, credentials })
|
const fetcherId = notificationsFetcher.startFetching({ store, credentials })
|
||||||
|
this.$store.commit('setNotificationFetcher', { fetcherId })
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -45,10 +45,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-usercard {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.non-mention {
|
.non-mention {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -171,6 +171,9 @@ const PostStatusForm = {
|
|||||||
},
|
},
|
||||||
formattingOptionsEnabled () {
|
formattingOptionsEnabled () {
|
||||||
return this.$store.state.instance.formattingOptionsEnabled
|
return this.$store.state.instance.formattingOptionsEnabled
|
||||||
|
},
|
||||||
|
postFormats () {
|
||||||
|
return this.$store.state.instance.postFormats || []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -219,6 +222,9 @@ const PostStatusForm = {
|
|||||||
this.highlighted = 0
|
this.highlighted = 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onKeydown (e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
},
|
||||||
setCaret ({target: {selectionStart}}) {
|
setCaret ({target: {selectionStart}}) {
|
||||||
this.caret = selectionStart
|
this.caret = selectionStart
|
||||||
},
|
},
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
ref="textarea"
|
ref="textarea"
|
||||||
@click="setCaret"
|
@click="setCaret"
|
||||||
@keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
|
@keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
|
||||||
|
@keydown="onKeydown"
|
||||||
@keydown.down="cycleForward"
|
@keydown.down="cycleForward"
|
||||||
@keydown.up="cycleBackward"
|
@keydown.up="cycleBackward"
|
||||||
@keydown.shift.tab="cycleBackward"
|
@keydown.shift.tab="cycleBackward"
|
||||||
@ -38,9 +39,9 @@
|
|||||||
<span class="text-format" v-if="formattingOptionsEnabled">
|
<span class="text-format" v-if="formattingOptionsEnabled">
|
||||||
<label for="post-content-type" class="select">
|
<label for="post-content-type" class="select">
|
||||||
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
|
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
|
||||||
<option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option>
|
<option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
|
||||||
<option value="text/html">HTML</option>
|
{{$t(`post_status.content_type["${postFormat}"]`)}}
|
||||||
<option value="text/markdown">Markdown</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<i class="icon-down-open"></i>
|
<i class="icon-down-open"></i>
|
||||||
</label>
|
</label>
|
||||||
|
@ -93,6 +93,9 @@ const settings = {
|
|||||||
currentSaveStateNotice () {
|
currentSaveStateNotice () {
|
||||||
return this.$store.state.interface.settings.currentSaveStateNotice
|
return this.$store.state.interface.settings.currentSaveStateNotice
|
||||||
},
|
},
|
||||||
|
postFormats () {
|
||||||
|
return this.$store.state.instance.postFormats || []
|
||||||
|
},
|
||||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }
|
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -105,17 +105,9 @@
|
|||||||
{{$t('settings.post_status_content_type')}}
|
{{$t('settings.post_status_content_type')}}
|
||||||
<label for="postContentType" class="select">
|
<label for="postContentType" class="select">
|
||||||
<select id="postContentType" v-model="postContentTypeLocal">
|
<select id="postContentType" v-model="postContentTypeLocal">
|
||||||
<option value="text/plain">
|
<option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
|
||||||
{{$t('settings.status_content_type_plain')}}
|
{{$t(`post_status.content_type["${postFormat}"]`)}}
|
||||||
{{postContentTypeDefault == 'text/plain' ? $t('settings.instance_default_simple') : ''}}
|
{{postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : ''}}
|
||||||
</option>
|
|
||||||
<option value="text/html">
|
|
||||||
HTML
|
|
||||||
{{postContentTypeDefault == 'text/html' ? $t('settings.instance_default_simple') : ''}}
|
|
||||||
</option>
|
|
||||||
<option value="text/markdown">
|
|
||||||
Markdown
|
|
||||||
{{postContentTypeDefault == 'text/markdown' ? $t('settings.instance_default_simple') : ''}}
|
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<i class="icon-down-open"/>
|
<i class="icon-down-open"/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
|
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
|
||||||
|
|
||||||
// TODO: separate touch gesture stuff into their own utils if more components want them
|
// TODO: separate touch gesture stuff into their own utils if more components want them
|
||||||
@ -12,7 +12,7 @@ const SideDrawer = {
|
|||||||
closed: true,
|
closed: true,
|
||||||
touchCoord: [0, 0]
|
touchCoord: [0, 0]
|
||||||
}),
|
}),
|
||||||
components: { UserCardContent },
|
components: { UserCard },
|
||||||
computed: {
|
computed: {
|
||||||
currentUser () {
|
currentUser () {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
|
@ -8,19 +8,14 @@
|
|||||||
@touchmove="touchMove"
|
@touchmove="touchMove"
|
||||||
>
|
>
|
||||||
<div class="side-drawer-heading" @click="toggleDrawer">
|
<div class="side-drawer-heading" @click="toggleDrawer">
|
||||||
<user-card-content :user="currentUser" :switcher="false" :hideBio="true" v-if="currentUser"/>
|
<UserCard :user="currentUser" :hideBio="true" v-if="currentUser"/>
|
||||||
<div class="side-drawer-logo-wrapper" v-else>
|
<div class="side-drawer-logo-wrapper" v-else>
|
||||||
<img :src="logo"/>
|
<img :src="logo"/>
|
||||||
<span>{{sitename}}</span>
|
<span>{{sitename}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-if="currentUser" @click="toggleDrawer">
|
<li v-if="!currentUser" @click="toggleDrawer">
|
||||||
<router-link :to="{ name: 'new-status', params: { username: currentUser.screen_name } }">
|
|
||||||
{{ $t("post_status.new_status") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li v-else @click="toggleDrawer">
|
|
||||||
<router-link :to="{ name: 'login' }">
|
<router-link :to="{ name: 'login' }">
|
||||||
{{ $t("login.login") }}
|
{{ $t("login.login") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
@ -119,14 +114,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.side-drawer-container-open {
|
.side-drawer-container-open {
|
||||||
transition-delay: 0.0s;
|
transition: 0.35s;
|
||||||
transition-property: left;
|
transition-property: background-color;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-drawer-container-closed {
|
.side-drawer-container-closed {
|
||||||
left: -100%;
|
left: -100%;
|
||||||
transition-delay: 0.5s;
|
background-color: rgba(0, 0, 0, 0);
|
||||||
transition-property: left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-drawer-click-outside {
|
.side-drawer-click-outside {
|
||||||
@ -181,15 +176,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.profile-panel-background {
|
|
||||||
border-radius: 0;
|
|
||||||
.panel-heading {
|
|
||||||
background: transparent;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-drawer ul {
|
.side-drawer ul {
|
||||||
|
@ -3,7 +3,7 @@ import FavoriteButton from '../favorite_button/favorite_button.vue'
|
|||||||
import RetweetButton from '../retweet_button/retweet_button.vue'
|
import RetweetButton from '../retweet_button/retweet_button.vue'
|
||||||
import DeleteButton from '../delete_button/delete_button.vue'
|
import DeleteButton from '../delete_button/delete_button.vue'
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import Gallery from '../gallery/gallery.vue'
|
import Gallery from '../gallery/gallery.vue'
|
||||||
import LinkPreview from '../link-preview/link-preview.vue'
|
import LinkPreview from '../link-preview/link-preview.vue'
|
||||||
@ -259,7 +259,7 @@ const Status = {
|
|||||||
RetweetButton,
|
RetweetButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
PostStatusForm,
|
PostStatusForm,
|
||||||
UserCardContent,
|
UserCard,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
Gallery,
|
Gallery,
|
||||||
LinkPreview
|
LinkPreview
|
||||||
|
@ -31,9 +31,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-body">
|
<div class="status-body">
|
||||||
<div class="usercard" v-if="userExpanded">
|
<UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard" v-if="userExpanded"/>
|
||||||
<user-card-content :user="status.user" :switcher="false"></user-card-content>
|
|
||||||
</div>
|
|
||||||
<div v-if="!noHeading" class="media-heading">
|
<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">
|
||||||
@ -248,8 +246,7 @@ $status-margin: 0.75em;
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.usercard {
|
.status-usercard {
|
||||||
margin: 0;
|
|
||||||
margin-bottom: $status-margin;
|
margin-bottom: $status-margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,9 @@ const Timeline = {
|
|||||||
}
|
}
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
// only 'stream' them when you're scrolled to the top
|
// only 'stream' them when you're scrolled to the top
|
||||||
if (window.pageYOffset < 15 &&
|
const doc = document.documentElement
|
||||||
|
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
|
||||||
|
if (top < 15 &&
|
||||||
!this.paused &&
|
!this.paused &&
|
||||||
!(this.unfocused && this.$store.state.config.pauseOnUnfocused)
|
!(this.unfocused && this.$store.state.config.pauseOnUnfocused)
|
||||||
) {
|
) {
|
||||||
|
@ -4,7 +4,7 @@ import { requestFollow, requestUnfollow } from '../../services/follow_manipulate
|
|||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [ 'user', 'switcher', 'selected', 'hideBio' ],
|
props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered' ],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
followRequestInProgress: false,
|
followRequestInProgress: false,
|
||||||
@ -16,7 +16,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headingStyle () {
|
classes () {
|
||||||
|
return [{
|
||||||
|
'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-bordered': this.bordered === true // set border for all sides
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
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
|
||||||
@ -93,22 +100,30 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
followUser () {
|
followUser () {
|
||||||
|
const store = this.$store
|
||||||
this.followRequestInProgress = true
|
this.followRequestInProgress = true
|
||||||
requestFollow(this.user, this.$store).then(({sent}) => {
|
requestFollow(this.user, store).then(({sent}) => {
|
||||||
this.followRequestInProgress = false
|
this.followRequestInProgress = false
|
||||||
this.followRequestSent = sent
|
this.followRequestSent = sent
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
unfollowUser () {
|
unfollowUser () {
|
||||||
|
const store = this.$store
|
||||||
this.followRequestInProgress = true
|
this.followRequestInProgress = true
|
||||||
requestUnfollow(this.user, this.$store).then(() => {
|
requestUnfollow(this.user, store).then(() => {
|
||||||
this.followRequestInProgress = false
|
this.followRequestInProgress = false
|
||||||
|
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
blockUser () {
|
blockUser () {
|
||||||
const store = this.$store
|
const store = this.$store
|
||||||
store.state.api.backendInteractor.blockUser(this.user.id)
|
store.state.api.backendInteractor.blockUser(this.user.id)
|
||||||
.then((blockedUser) => store.commit('addNewUsers', [blockedUser]))
|
.then((blockedUser) => {
|
||||||
|
store.commit('addNewUsers', [blockedUser])
|
||||||
|
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
|
||||||
|
store.commit('removeStatus', { timeline: 'public', userId: this.user.id })
|
||||||
|
store.commit('removeStatus', { timeline: 'publicAndExternal', userId: this.user.id })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
unblockUser () {
|
unblockUser () {
|
||||||
const store = this.$store
|
const store = this.$store
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="heading" class="profile-panel-background" :style="headingStyle">
|
<div class="user-card" :class="classes" :style="style">
|
||||||
<div class="panel-heading text-center">
|
<div class="panel-heading">
|
||||||
<div class='user-info'>
|
<div class='user-info'>
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
<router-link :to="userProfileLink(user)">
|
<router-link :to="userProfileLink(user)">
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div>
|
<div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div>
|
||||||
<div :title="user.name" class='user-name' v-else>{{user.name}}</div>
|
<div :title="user.name" class='user-name' v-else>{{user.name}}</div>
|
||||||
<router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
|
<router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
|
||||||
<i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i>
|
<i class="button-icon icon-pencil usersettings" :title="$t('tool_tip.user_settings')"></i>
|
||||||
</router-link>
|
</router-link>
|
||||||
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
|
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
|
||||||
<i class="icon-link-ext usersettings"></i>
|
<i class="icon-link-ext usersettings"></i>
|
||||||
@ -108,7 +108,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body profile-panel-body" v-if="!hideBio">
|
<div class="panel-body" v-if="!hideBio">
|
||||||
<div v-if="!hideUserStatsLocal && switcher" class="user-counts">
|
<div v-if="!hideUserStatsLocal && switcher" class="user-counts">
|
||||||
<div class="user-count" v-on:click.prevent="setProfileView('statuses')">
|
<div class="user-count" v-on:click.prevent="setProfileView('statuses')">
|
||||||
<h5>{{ $t('user_card.statuses') }}</h5>
|
<h5>{{ $t('user_card.statuses') }}</h5>
|
||||||
@ -123,40 +123,75 @@
|
|||||||
<span>{{user.followers_count}}</span>
|
<span>{{user.followers_count}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p>
|
<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="profile-bio">{{ user.description }}</p>
|
<p v-else-if="!hideBio" class="user-card-bio">{{ user.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./user_card_content.js"></script>
|
<script src="./user_card.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.profile-panel-background {
|
.user-card {
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: $fallback--panelRadius;
|
|
||||||
border-radius: var(--panelRadius, $fallback--panelRadius);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
|
|
||||||
.panel-heading {
|
.panel-heading {
|
||||||
padding: .5em 0;
|
padding: .5em 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
background: transparent;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.profile-panel-body {
|
.panel-body {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
|
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
|
||||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%);
|
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%);
|
||||||
|
}
|
||||||
|
|
||||||
.profile-bio {
|
p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-bio {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
object-fit: contain;
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 400px;
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
|
||||||
|
&-rounded-t {
|
||||||
|
border-top-left-radius: $fallback--panelRadius;
|
||||||
|
border-top-left-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
|
border-top-right-radius: $fallback--panelRadius;
|
||||||
|
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-rounded {
|
||||||
|
border-radius: $fallback--panelRadius;
|
||||||
|
border-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-bordered {
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: $fallback--border;
|
||||||
|
border-color: var(--border, $fallback--border);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,25 +428,4 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.usercard {
|
|
||||||
width: fill-available;
|
|
||||||
border-radius: $fallback--panelRadius;
|
|
||||||
border-radius: var(--panelRadius, $fallback--panelRadius);
|
|
||||||
border-style: solid;
|
|
||||||
border-color: $fallback--border;
|
|
||||||
border-color: var(--border, $fallback--border);
|
|
||||||
border-width: 1px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.panel-heading {
|
|
||||||
background: transparent;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
@ -1,6 +1,6 @@
|
|||||||
import LoginForm from '../login_form/login_form.vue'
|
import LoginForm from '../login_form/login_form.vue'
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
|
|
||||||
const UserPanel = {
|
const UserPanel = {
|
||||||
computed: {
|
computed: {
|
||||||
@ -9,7 +9,7 @@ const UserPanel = {
|
|||||||
components: {
|
components: {
|
||||||
LoginForm,
|
LoginForm,
|
||||||
PostStatusForm,
|
PostStatusForm,
|
||||||
UserCardContent
|
UserCard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user-panel">
|
<div class="user-panel">
|
||||||
<div v-if='user' class="panel panel-default" style="overflow: visible;">
|
<div v-if='user' class="panel panel-default" style="overflow: visible;">
|
||||||
<user-card-content :user="user" :switcher="false" :hideBio="true"></user-card-content>
|
<UserCard :user="user" :hideBio="true" rounded="top"/>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
<post-status-form v-if='user'></post-status-form>
|
<post-status-form v-if='user'></post-status-form>
|
||||||
</div>
|
</div>
|
||||||
@ -11,13 +11,3 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./user_panel.js"></script>
|
<script src="./user_panel.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.user-panel {
|
|
||||||
.profile-panel-background .panel-heading {
|
|
||||||
background: transparent;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { compose } from 'vue-compose'
|
import { compose } from 'vue-compose'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import FollowCard from '../follow_card/follow_card.vue'
|
import FollowCard from '../follow_card/follow_card.vue'
|
||||||
import Timeline from '../timeline/timeline.vue'
|
import Timeline from '../timeline/timeline.vue'
|
||||||
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
||||||
@ -147,7 +147,7 @@ const UserProfile = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
UserCardContent,
|
UserCard,
|
||||||
Timeline,
|
Timeline,
|
||||||
FollowerList,
|
FollowerList,
|
||||||
FriendList
|
FriendList
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="user.id" class="user-profile panel panel-default">
|
<div v-if="user.id" class="user-profile panel panel-default">
|
||||||
<user-card-content
|
<UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
|
||||||
:user="user"
|
|
||||||
:switcher="true"
|
|
||||||
:selected="timeline.viewing"
|
|
||||||
/>
|
|
||||||
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
|
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
|
||||||
<Timeline
|
<Timeline
|
||||||
:label="$t('user_card.statuses')"
|
:label="$t('user_card.statuses')"
|
||||||
@ -64,11 +60,6 @@
|
|||||||
flex: 2;
|
flex: 2;
|
||||||
flex-basis: 500px;
|
flex-basis: 500px;
|
||||||
|
|
||||||
.profile-panel-background .panel-heading {
|
|
||||||
background: transparent;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
.userlist-placeholder {
|
.userlist-placeholder {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -71,7 +71,9 @@
|
|||||||
"account_not_locked_warning_link": "uzamčen",
|
"account_not_locked_warning_link": "uzamčen",
|
||||||
"attachments_sensitive": "Označovat přílohy jako citlivé",
|
"attachments_sensitive": "Označovat přílohy jako citlivé",
|
||||||
"content_type": {
|
"content_type": {
|
||||||
"plain_text": "Prostý text"
|
"plain_text": "Prostý text",
|
||||||
|
"text/html": "HTML",
|
||||||
|
"text/markdown": "Markdown"
|
||||||
},
|
},
|
||||||
"content_warning": "Předmět (volitelný)",
|
"content_warning": "Předmět (volitelný)",
|
||||||
"default": "Právě jsem přistál v L.A.",
|
"default": "Právě jsem přistál v L.A.",
|
||||||
@ -95,7 +97,7 @@
|
|||||||
"new_captcha": "Kliknutím na obrázek získáte novou CAPTCHA",
|
"new_captcha": "Kliknutím na obrázek získáte novou CAPTCHA",
|
||||||
"username_placeholder": "např. lain",
|
"username_placeholder": "např. lain",
|
||||||
"fullname_placeholder": "např. Lain Iwakura",
|
"fullname_placeholder": "např. Lain Iwakura",
|
||||||
"bio_placeholder": "např.\nNazdar, jsem Lain\nJsem anime dívka a žiji v příměstském Japonsku. Možná mě znáte z Wired.",
|
"bio_placeholder": "např.\nNazdar, jsem Lain\nJsem anime dívka žijící v příměstském Japonsku. Možná mě znáte z Wired.",
|
||||||
"validations": {
|
"validations": {
|
||||||
"username_required": "nemůže být prázdné",
|
"username_required": "nemůže být prázdné",
|
||||||
"fullname_required": "nemůže být prázdné",
|
"fullname_required": "nemůže být prázdné",
|
||||||
@ -204,7 +206,7 @@
|
|||||||
"radii_help": "Nastavit zakulacení rohů rozhraní (v pixelech)",
|
"radii_help": "Nastavit zakulacení rohů rozhraní (v pixelech)",
|
||||||
"replies_in_timeline": "Odpovědi v časové ose",
|
"replies_in_timeline": "Odpovědi v časové ose",
|
||||||
"reply_link_preview": "Povolit náhledy odkazu pro odpověď při přejetí myši",
|
"reply_link_preview": "Povolit náhledy odkazu pro odpověď při přejetí myši",
|
||||||
"reply_visibility_all": "Zobrazit všechny odpovědiShow all replies",
|
"reply_visibility_all": "Zobrazit všechny odpovědi",
|
||||||
"reply_visibility_following": "Zobrazit pouze odpovědi směřované na mě nebo uživatele, které sleduji",
|
"reply_visibility_following": "Zobrazit pouze odpovědi směřované na mě nebo uživatele, které sleduji",
|
||||||
"reply_visibility_self": "Zobrazit pouze odpovědi směřované na mě",
|
"reply_visibility_self": "Zobrazit pouze odpovědi směřované na mě",
|
||||||
"saving_err": "Chyba při ukládání nastavení",
|
"saving_err": "Chyba při ukládání nastavení",
|
||||||
@ -221,7 +223,6 @@
|
|||||||
"subject_line_mastodon": "Jako u Mastodonu: zkopírovat tak, jak je",
|
"subject_line_mastodon": "Jako u Mastodonu: zkopírovat tak, jak je",
|
||||||
"subject_line_noop": "Nekopírovat",
|
"subject_line_noop": "Nekopírovat",
|
||||||
"post_status_content_type": "Publikovat typ obsahu příspěvku",
|
"post_status_content_type": "Publikovat typ obsahu příspěvku",
|
||||||
"status_content_type_plain": "Prostý text",
|
|
||||||
"stop_gifs": "Přehrávat GIFy při přejetí myši",
|
"stop_gifs": "Přehrávat GIFy při přejetí myši",
|
||||||
"streaming": "Povolit automatické streamování nových příspěvků při rolování nahoru",
|
"streaming": "Povolit automatické streamování nových příspěvků při rolování nahoru",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
@ -339,7 +340,7 @@
|
|||||||
"button": "Tlačítko",
|
"button": "Tlačítko",
|
||||||
"text": "Spousta dalšího {0} a {1}",
|
"text": "Spousta dalšího {0} a {1}",
|
||||||
"mono": "obsahu",
|
"mono": "obsahu",
|
||||||
"input": "Just landed in L.A.",
|
"input": "Právě jsem přistál v L.A.",
|
||||||
"faint_link": "pomocný manuál",
|
"faint_link": "pomocný manuál",
|
||||||
"fine_print": "Přečtěte si náš {0} a nenaučte se nic užitečného!",
|
"fine_print": "Přečtěte si náš {0} a nenaučte se nic užitečného!",
|
||||||
"header_faint": "Tohle je v pohodě",
|
"header_faint": "Tohle je v pohodě",
|
||||||
@ -361,7 +362,7 @@
|
|||||||
"no_statuses": "Žádné příspěvky"
|
"no_statuses": "Žádné příspěvky"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"reply_to": "Odpovědět uživateli",
|
"reply_to": "Odpověď uživateli",
|
||||||
"replies_list": "Odpovědi:"
|
"replies_list": "Odpovědi:"
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -413,7 +414,7 @@
|
|||||||
"upload":{
|
"upload":{
|
||||||
"error": {
|
"error": {
|
||||||
"base": "Nahrávání selhalo.",
|
"base": "Nahrávání selhalo.",
|
||||||
"file_too_big": "Soubor je úříliš velký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
|
"file_too_big": "Soubor je příliš velký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
|
||||||
"default": "Zkuste to znovu později"
|
"default": "Zkuste to znovu později"
|
||||||
},
|
},
|
||||||
"file_size_units": {
|
"file_size_units": {
|
||||||
|
@ -71,7 +71,9 @@
|
|||||||
"account_not_locked_warning_link": "locked",
|
"account_not_locked_warning_link": "locked",
|
||||||
"attachments_sensitive": "Mark attachments as sensitive",
|
"attachments_sensitive": "Mark attachments as sensitive",
|
||||||
"content_type": {
|
"content_type": {
|
||||||
"plain_text": "Plain text"
|
"text/plain": "Plain text",
|
||||||
|
"text/html": "HTML",
|
||||||
|
"text/markdown": "Markdown"
|
||||||
},
|
},
|
||||||
"content_warning": "Subject (optional)",
|
"content_warning": "Subject (optional)",
|
||||||
"default": "Just landed in L.A.",
|
"default": "Just landed in L.A.",
|
||||||
@ -221,7 +223,6 @@
|
|||||||
"subject_line_mastodon": "Like mastodon: copy as is",
|
"subject_line_mastodon": "Like mastodon: copy as is",
|
||||||
"subject_line_noop": "Do not copy",
|
"subject_line_noop": "Do not copy",
|
||||||
"post_status_content_type": "Post status content type",
|
"post_status_content_type": "Post status content type",
|
||||||
"status_content_type_plain": "Plain text",
|
|
||||||
"stop_gifs": "Play-on-hover GIFs",
|
"stop_gifs": "Play-on-hover GIFs",
|
||||||
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
|
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
|
@ -221,7 +221,6 @@
|
|||||||
"subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe",
|
"subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe",
|
||||||
"subject_line_noop": "Ne kopii",
|
"subject_line_noop": "Ne kopii",
|
||||||
"post_status_content_type": "Afiŝi specon de la enhavo de la stato",
|
"post_status_content_type": "Afiŝi specon de la enhavo de la stato",
|
||||||
"status_content_type_plain": "Plata teksto",
|
|
||||||
"stop_gifs": "Movi GIF-bildojn dum musa ŝvebo",
|
"stop_gifs": "Movi GIF-bildojn dum musa ŝvebo",
|
||||||
"streaming": "Ŝalti memfaran fluigon de novaj afiŝoj ĉe la supro de la paĝo",
|
"streaming": "Ŝalti memfaran fluigon de novaj afiŝoj ĉe la supro de la paĝo",
|
||||||
"text": "Teksto",
|
"text": "Teksto",
|
||||||
|
@ -202,7 +202,6 @@
|
|||||||
"subject_line_mastodon": "Tipo mastodon: copiar como es",
|
"subject_line_mastodon": "Tipo mastodon: copiar como es",
|
||||||
"subject_line_noop": "No copiar",
|
"subject_line_noop": "No copiar",
|
||||||
"post_status_content_type": "Formato de publicación",
|
"post_status_content_type": "Formato de publicación",
|
||||||
"status_content_type_plain": "Texto plano",
|
|
||||||
"stop_gifs": "Iniciar GIFs al pasar el ratón",
|
"stop_gifs": "Iniciar GIFs al pasar el ratón",
|
||||||
"streaming": "Habilite la transmisión automática de nuevas publicaciones cuando se desplaza hacia la parte superior",
|
"streaming": "Habilite la transmisión automática de nuevas publicaciones cuando se desplaza hacia la parte superior",
|
||||||
"text": "Texto",
|
"text": "Texto",
|
||||||
|
@ -202,7 +202,6 @@
|
|||||||
"subject_line_mastodon": "マストドンふう: そのままコピー",
|
"subject_line_mastodon": "マストドンふう: そのままコピー",
|
||||||
"subject_line_noop": "コピーしない",
|
"subject_line_noop": "コピーしない",
|
||||||
"post_status_content_type": "とうこうのコンテントタイプ",
|
"post_status_content_type": "とうこうのコンテントタイプ",
|
||||||
"status_content_type_plain": "プレーンテキスト",
|
|
||||||
"stop_gifs": "カーソルをかさねたとき、GIFをうごかす",
|
"stop_gifs": "カーソルをかさねたとき、GIFをうごかす",
|
||||||
"streaming": "うえまでスクロールしたとき、じどうてきにストリーミングする",
|
"streaming": "うえまでスクロールしたとき、じどうてきにストリーミングする",
|
||||||
"text": "もじ",
|
"text": "もじ",
|
||||||
|
@ -221,7 +221,6 @@
|
|||||||
"subject_line_mastodon": "Coma mastodon : copiar tal coma es",
|
"subject_line_mastodon": "Coma mastodon : copiar tal coma es",
|
||||||
"subject_line_noop": "Copiar pas",
|
"subject_line_noop": "Copiar pas",
|
||||||
"post_status_content_type": "Publicar lo tipe de contengut dels estatuts",
|
"post_status_content_type": "Publicar lo tipe de contengut dels estatuts",
|
||||||
"status_content_type_plain": "Tèxte brut",
|
|
||||||
"stop_gifs": "Lançar los GIFs al subrevòl",
|
"stop_gifs": "Lançar los GIFs al subrevòl",
|
||||||
"streaming": "Activar lo cargament automatic dels novèls estatus en anar amont",
|
"streaming": "Activar lo cargament automatic dels novèls estatus en anar amont",
|
||||||
"text": "Tèxt",
|
"text": "Tèxt",
|
||||||
|
@ -221,7 +221,6 @@
|
|||||||
"subject_line_mastodon": "Como o Mastodon: copiar como está",
|
"subject_line_mastodon": "Como o Mastodon: copiar como está",
|
||||||
"subject_line_noop": "Não copiar",
|
"subject_line_noop": "Não copiar",
|
||||||
"post_status_content_type": "Postar tipo de conteúdo do status",
|
"post_status_content_type": "Postar tipo de conteúdo do status",
|
||||||
"status_content_type_plain": "Texto puro",
|
|
||||||
"stop_gifs": "Reproduzir GIFs ao passar o cursor em cima",
|
"stop_gifs": "Reproduzir GIFs ao passar o cursor em cima",
|
||||||
"streaming": "Habilitar o fluxo automático de postagens quando ao topo da página",
|
"streaming": "Habilitar o fluxo automático de postagens quando ao topo da página",
|
||||||
"text": "Texto",
|
"text": "Texto",
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
const chat = {
|
const chat = {
|
||||||
state: {
|
state: {
|
||||||
messages: [],
|
messages: [],
|
||||||
channel: {state: ''}
|
channel: {state: ''},
|
||||||
|
socket: null
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setChannel (state, channel) {
|
setChannel (state, channel) {
|
||||||
state.channel = channel
|
state.channel = channel
|
||||||
},
|
},
|
||||||
|
setSocket (state, socket) {
|
||||||
|
state.socket = socket
|
||||||
|
},
|
||||||
addMessage (state, message) {
|
addMessage (state, message) {
|
||||||
state.messages.push(message)
|
state.messages.push(message)
|
||||||
state.messages = state.messages.slice(-19, 20)
|
state.messages = state.messages.slice(-19, 20)
|
||||||
@ -16,8 +20,12 @@ const chat = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
disconnectFromChat (store) {
|
||||||
|
store.state.socket.disconnect()
|
||||||
|
},
|
||||||
initializeChat (store, socket) {
|
initializeChat (store, socket) {
|
||||||
const channel = socket.channel('chat:public')
|
const channel = socket.channel('chat:public')
|
||||||
|
store.commit('setSocket', socket)
|
||||||
channel.on('new_msg', (msg) => {
|
channel.on('new_msg', (msg) => {
|
||||||
store.commit('addMessage', msg)
|
store.commit('addMessage', msg)
|
||||||
})
|
})
|
||||||
|
@ -37,6 +37,7 @@ const defaultState = {
|
|||||||
emoji: [],
|
emoji: [],
|
||||||
customEmoji: [],
|
customEmoji: [],
|
||||||
restrictedNicknames: [],
|
restrictedNicknames: [],
|
||||||
|
postFormats: [],
|
||||||
|
|
||||||
// Feature-set, apparently, not everything here is reported...
|
// Feature-set, apparently, not everything here is reported...
|
||||||
mediaProxyAvailable: false,
|
mediaProxyAvailable: false,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash'
|
import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray } from 'lodash'
|
||||||
import apiService from '../services/api/api.service.js'
|
import apiService from '../services/api/api.service.js'
|
||||||
// import parse from '../services/status_parser/status_parser.js'
|
// import parse from '../services/status_parser/status_parser.js'
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ const emptyTl = (userId = 0) => ({
|
|||||||
flushMarker: 0
|
flushMarker: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
export const defaultState = {
|
export const defaultState = () => ({
|
||||||
allStatuses: [],
|
allStatuses: [],
|
||||||
allStatusesObject: {},
|
allStatusesObject: {},
|
||||||
maxId: 0,
|
maxId: 0,
|
||||||
@ -30,7 +30,8 @@ export const defaultState = {
|
|||||||
data: [],
|
data: [],
|
||||||
idStore: {},
|
idStore: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false
|
error: false,
|
||||||
|
fetcherId: null
|
||||||
},
|
},
|
||||||
favorites: new Set(),
|
favorites: new Set(),
|
||||||
error: false,
|
error: false,
|
||||||
@ -45,7 +46,7 @@ export const defaultState = {
|
|||||||
tag: emptyTl(),
|
tag: emptyTl(),
|
||||||
dms: emptyTl()
|
dms: emptyTl()
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
export const prepareStatus = (status) => {
|
export const prepareStatus = (status) => {
|
||||||
// Set deleted flag
|
// Set deleted flag
|
||||||
@ -312,18 +313,39 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const removeStatus = (state, { timeline, userId }) => {
|
||||||
|
const timelineObject = state.timelines[timeline]
|
||||||
|
if (userId) {
|
||||||
|
remove(timelineObject.statuses, { user: { id: userId } })
|
||||||
|
remove(timelineObject.visibleStatuses, { user: { id: userId } })
|
||||||
|
timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0
|
||||||
|
timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
addNewStatuses,
|
addNewStatuses,
|
||||||
addNewNotifications,
|
addNewNotifications,
|
||||||
|
removeStatus,
|
||||||
showNewStatuses (state, { timeline }) {
|
showNewStatuses (state, { timeline }) {
|
||||||
const oldTimeline = (state.timelines[timeline])
|
const oldTimeline = (state.timelines[timeline])
|
||||||
|
|
||||||
oldTimeline.newStatusCount = 0
|
oldTimeline.newStatusCount = 0
|
||||||
oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)
|
oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)
|
||||||
oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id
|
oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id
|
||||||
|
oldTimeline.minId = oldTimeline.minVisibleId
|
||||||
oldTimeline.visibleStatusesObject = {}
|
oldTimeline.visibleStatusesObject = {}
|
||||||
each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status })
|
each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status })
|
||||||
},
|
},
|
||||||
|
setNotificationFetcher (state, { fetcherId }) {
|
||||||
|
state.notifications.fetcherId = fetcherId
|
||||||
|
},
|
||||||
|
resetStatuses (state) {
|
||||||
|
const emptyState = defaultState()
|
||||||
|
Object.entries(emptyState).forEach(([key, value]) => {
|
||||||
|
state[key] = value
|
||||||
|
})
|
||||||
|
},
|
||||||
clearTimeline (state, { timeline }) {
|
clearTimeline (state, { timeline }) {
|
||||||
state.timelines[timeline] = emptyTl(state.timelines[timeline].userId)
|
state.timelines[timeline] = emptyTl(state.timelines[timeline].userId)
|
||||||
},
|
},
|
||||||
@ -374,7 +396,7 @@ export const mutations = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const statuses = {
|
const statuses = {
|
||||||
state: defaultState,
|
state: defaultState(),
|
||||||
actions: {
|
actions: {
|
||||||
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {
|
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {
|
||||||
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })
|
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })
|
||||||
@ -394,6 +416,12 @@ const statuses = {
|
|||||||
setNotificationsSilence ({ rootState, commit }, { value }) {
|
setNotificationsSilence ({ rootState, commit }, { value }) {
|
||||||
commit('setNotificationsSilence', { value })
|
commit('setNotificationsSilence', { value })
|
||||||
},
|
},
|
||||||
|
stopFetchingNotifications ({ rootState, commit }) {
|
||||||
|
if (rootState.statuses.notifications.fetcherId) {
|
||||||
|
window.clearInterval(rootState.statuses.notifications.fetcherId)
|
||||||
|
}
|
||||||
|
commit('setNotificationFetcher', { fetcherId: null })
|
||||||
|
},
|
||||||
deleteStatus ({ rootState, commit }, status) {
|
deleteStatus ({ rootState, commit }, status) {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||||
|
@ -292,9 +292,12 @@ const users = {
|
|||||||
|
|
||||||
logout (store) {
|
logout (store) {
|
||||||
store.commit('clearCurrentUser')
|
store.commit('clearCurrentUser')
|
||||||
|
store.dispatch('disconnectFromChat')
|
||||||
store.commit('setToken', false)
|
store.commit('setToken', false)
|
||||||
store.dispatch('stopFetching', 'friends')
|
store.dispatch('stopFetching', 'friends')
|
||||||
store.commit('setBackendInteractor', backendInteractorService())
|
store.commit('setBackendInteractor', backendInteractorService())
|
||||||
|
store.dispatch('stopFetchingNotifications')
|
||||||
|
store.commit('resetStatuses')
|
||||||
},
|
},
|
||||||
loginUser (store, accessToken) {
|
loginUser (store, accessToken) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -319,6 +322,9 @@ const users = {
|
|||||||
|
|
||||||
if (user.token) {
|
if (user.token) {
|
||||||
store.dispatch('setWsToken', user.token)
|
store.dispatch('setWsToken', user.token)
|
||||||
|
|
||||||
|
// Initialize the chat socket.
|
||||||
|
store.dispatch('initializeSocket')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start getting fresh posts.
|
// Start getting fresh posts.
|
||||||
|
0
static/font/LICENSE.txt
Normal file → Executable file
0
static/font/LICENSE.txt
Normal file → Executable file
0
static/font/README.txt
Normal file → Executable file
0
static/font/README.txt
Normal file → Executable file
6
static/font/config.json
Normal file → Executable file
6
static/font/config.json
Normal file → Executable file
@ -233,6 +233,12 @@
|
|||||||
"css": "play-circled",
|
"css": "play-circled",
|
||||||
"code": 61764,
|
"code": 61764,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "d35a1d35efeb784d1dc9ac18b9b6c2b6",
|
||||||
|
"css": "pencil",
|
||||||
|
"code": 59416,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
0
static/font/css/animation.css
Normal file → Executable file
0
static/font/css/animation.css
Normal file → Executable file
1
static/font/css/fontello-codes.css
vendored
Normal file → Executable file
1
static/font/css/fontello-codes.css
vendored
Normal file → Executable file
@ -23,6 +23,7 @@
|
|||||||
.icon-plus:before { content: '\e815'; } /* '' */
|
.icon-plus:before { content: '\e815'; } /* '' */
|
||||||
.icon-adjust:before { content: '\e816'; } /* '' */
|
.icon-adjust:before { content: '\e816'; } /* '' */
|
||||||
.icon-edit:before { content: '\e817'; } /* '' */
|
.icon-edit:before { content: '\e817'; } /* '' */
|
||||||
|
.icon-pencil:before { content: '\e818'; } /* '' */
|
||||||
.icon-spin3:before { content: '\e832'; } /* '' */
|
.icon-spin3:before { content: '\e832'; } /* '' */
|
||||||
.icon-spin4:before { content: '\e834'; } /* '' */
|
.icon-spin4:before { content: '\e834'; } /* '' */
|
||||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||||
|
13
static/font/css/fontello-embedded.css
vendored
Normal file → Executable file
13
static/font/css/fontello-embedded.css
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
1
static/font/css/fontello-ie7-codes.css
vendored
Normal file → Executable file
1
static/font/css/fontello-ie7-codes.css
vendored
Normal file → Executable file
@ -23,6 +23,7 @@
|
|||||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
1
static/font/css/fontello-ie7.css
vendored
Normal file → Executable file
1
static/font/css/fontello-ie7.css
vendored
Normal file → Executable file
@ -34,6 +34,7 @@
|
|||||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
15
static/font/css/fontello.css
vendored
Normal file → Executable file
15
static/font/css/fontello.css
vendored
Normal file → Executable file
@ -1,11 +1,11 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('../font/fontello.eot?94672585');
|
src: url('../font/fontello.eot?40679575');
|
||||||
src: url('../font/fontello.eot?94672585#iefix') format('embedded-opentype'),
|
src: url('../font/fontello.eot?40679575#iefix') format('embedded-opentype'),
|
||||||
url('../font/fontello.woff2?94672585') format('woff2'),
|
url('../font/fontello.woff2?40679575') format('woff2'),
|
||||||
url('../font/fontello.woff?94672585') format('woff'),
|
url('../font/fontello.woff?40679575') format('woff'),
|
||||||
url('../font/fontello.ttf?94672585') format('truetype'),
|
url('../font/fontello.ttf?40679575') format('truetype'),
|
||||||
url('../font/fontello.svg?94672585#fontello') format('svg');
|
url('../font/fontello.svg?40679575#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@ -15,7 +15,7 @@
|
|||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('../font/fontello.svg?94672585#fontello') format('svg');
|
src: url('../font/fontello.svg?40679575#fontello') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@ -79,6 +79,7 @@
|
|||||||
.icon-plus:before { content: '\e815'; } /* '' */
|
.icon-plus:before { content: '\e815'; } /* '' */
|
||||||
.icon-adjust:before { content: '\e816'; } /* '' */
|
.icon-adjust:before { content: '\e816'; } /* '' */
|
||||||
.icon-edit:before { content: '\e817'; } /* '' */
|
.icon-edit:before { content: '\e817'; } /* '' */
|
||||||
|
.icon-pencil:before { content: '\e818'; } /* '' */
|
||||||
.icon-spin3:before { content: '\e832'; } /* '' */
|
.icon-spin3:before { content: '\e832'; } /* '' */
|
||||||
.icon-spin4:before { content: '\e834'; } /* '' */
|
.icon-spin4:before { content: '\e834'; } /* '' */
|
||||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||||
|
17
static/font/demo.html
Normal file → Executable file
17
static/font/demo.html
Normal file → Executable file
@ -229,11 +229,11 @@ body {
|
|||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('./font/fontello.eot?28736547');
|
src: url('./font/fontello.eot?50378338');
|
||||||
src: url('./font/fontello.eot?28736547#iefix') format('embedded-opentype'),
|
src: url('./font/fontello.eot?50378338#iefix') format('embedded-opentype'),
|
||||||
url('./font/fontello.woff?28736547') format('woff'),
|
url('./font/fontello.woff?50378338') format('woff'),
|
||||||
url('./font/fontello.ttf?28736547') format('truetype'),
|
url('./font/fontello.ttf?50378338') format('truetype'),
|
||||||
url('./font/fontello.svg?28736547#fontello') format('svg');
|
url('./font/fontello.svg?50378338#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@ -334,24 +334,25 @@ body {
|
|||||||
<div class="the-icons span3" title="Code: 0xe817"><i class="demo-icon icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe817</span></div>
|
<div class="the-icons span3" title="Code: 0xe817"><i class="demo-icon icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe817</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe818"><i class="demo-icon icon-pencil"></i> <span class="i-name">icon-pencil</span><span class="i-code">0xe818</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
BIN
static/font/font/fontello.eot
Normal file → Executable file
BIN
static/font/font/fontello.eot
Normal file → Executable file
Binary file not shown.
2
static/font/font/fontello.svg
Normal file → Executable file
2
static/font/font/fontello.svg
Normal file → Executable file
@ -54,6 +54,8 @@
|
|||||||
|
|
||||||
<glyph glyph-name="edit" unicode="" d="M496 196l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
<glyph glyph-name="edit" unicode="" d="M496 196l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="pencil" unicode="" d="M203 0l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
|
<glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
<glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
BIN
static/font/font/fontello.ttf
Normal file → Executable file
BIN
static/font/font/fontello.ttf
Normal file → Executable file
Binary file not shown.
BIN
static/font/font/fontello.woff
Normal file → Executable file
BIN
static/font/font/fontello.woff
Normal file → Executable file
Binary file not shown.
BIN
static/font/font/fontello.woff2
Normal file → Executable file
BIN
static/font/font/fontello.woff2
Normal file → Executable file
Binary file not shown.
@ -24,7 +24,7 @@ describe('routes', () => {
|
|||||||
|
|
||||||
const matchedComponents = router.getMatchedComponents()
|
const matchedComponents = router.getMatchedComponents()
|
||||||
|
|
||||||
expect(matchedComponents[0].components.hasOwnProperty('UserCardContent')).to.eql(true)
|
expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('user\'s profile at /users', () => {
|
it('user\'s profile at /users', () => {
|
||||||
@ -32,6 +32,6 @@ describe('routes', () => {
|
|||||||
|
|
||||||
const matchedComponents = router.getMatchedComponents()
|
const matchedComponents = router.getMatchedComponents()
|
||||||
|
|
||||||
expect(matchedComponents[0].components.hasOwnProperty('UserCardContent')).to.eql(true)
|
expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { cloneDeep } from 'lodash'
|
|
||||||
import { defaultState, mutations, prepareStatus } from '../../../../src/modules/statuses.js'
|
import { defaultState, mutations, prepareStatus } from '../../../../src/modules/statuses.js'
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
@ -15,244 +14,264 @@ const makeMockStatus = ({id, text, type = 'status'}) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Statuses.prepareStatus', () => {
|
describe('Statuses module', () => {
|
||||||
it('sets deleted flag to false', () => {
|
describe('prepareStatus', () => {
|
||||||
const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'})
|
it('sets deleted flag to false', () => {
|
||||||
expect(prepareStatus(aStatus).deleted).to.eq(false)
|
const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'})
|
||||||
})
|
expect(prepareStatus(aStatus).deleted).to.eq(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('The Statuses module', () => {
|
|
||||||
it('adds the status to allStatuses and to the given timeline', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' })
|
|
||||||
|
|
||||||
expect(state.allStatuses).to.eql([status])
|
|
||||||
expect(state.timelines.public.statuses).to.eql([status])
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([])
|
|
||||||
expect(state.timelines.public.newStatusCount).to.equal(1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('counts the status as new if it has not been seen on this timeline', () => {
|
describe('addNewStatuses', () => {
|
||||||
const state = cloneDeep(defaultState)
|
it('adds the status to allStatuses and to the given timeline', () => {
|
||||||
const status = makeMockStatus({id: '1'})
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' })
|
mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' })
|
||||||
mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' })
|
|
||||||
|
|
||||||
expect(state.allStatuses).to.eql([status])
|
expect(state.allStatuses).to.eql([status])
|
||||||
expect(state.timelines.public.statuses).to.eql([status])
|
expect(state.timelines.public.statuses).to.eql([status])
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([])
|
expect(state.timelines.public.visibleStatuses).to.eql([])
|
||||||
expect(state.timelines.public.newStatusCount).to.equal(1)
|
expect(state.timelines.public.newStatusCount).to.equal(1)
|
||||||
|
})
|
||||||
|
|
||||||
expect(state.allStatuses).to.eql([status])
|
it('counts the status as new if it has not been seen on this timeline', () => {
|
||||||
expect(state.timelines.friends.statuses).to.eql([status])
|
const state = defaultState()
|
||||||
expect(state.timelines.friends.visibleStatuses).to.eql([])
|
const status = makeMockStatus({id: '1'})
|
||||||
expect(state.timelines.friends.newStatusCount).to.equal(1)
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' })
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' })
|
||||||
|
|
||||||
|
expect(state.allStatuses).to.eql([status])
|
||||||
|
expect(state.timelines.public.statuses).to.eql([status])
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.eql([])
|
||||||
|
expect(state.timelines.public.newStatusCount).to.equal(1)
|
||||||
|
|
||||||
|
expect(state.allStatuses).to.eql([status])
|
||||||
|
expect(state.timelines.friends.statuses).to.eql([status])
|
||||||
|
expect(state.timelines.friends.visibleStatuses).to.eql([])
|
||||||
|
expect(state.timelines.friends.newStatusCount).to.equal(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add the statuses to allStatuses if no timeline is given', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status] })
|
||||||
|
|
||||||
|
expect(state.allStatuses).to.eql([status])
|
||||||
|
expect(state.timelines.public.statuses).to.eql([])
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.eql([])
|
||||||
|
expect(state.timelines.public.newStatusCount).to.equal(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('adds the status to allStatuses and to the given timeline, directly visible', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
|
expect(state.allStatuses).to.eql([status])
|
||||||
|
expect(state.timelines.public.statuses).to.eql([status])
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.eql([status])
|
||||||
|
expect(state.timelines.public.newStatusCount).to.equal(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removes statuses by tag on deletion', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
const otherStatus = makeMockStatus({id: '3'})
|
||||||
|
status.uri = 'xxx'
|
||||||
|
const deletion = makeMockStatus({id: '2', type: 'deletion'})
|
||||||
|
deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.'
|
||||||
|
deletion.uri = 'xxx'
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status, otherStatus], showImmediately: true, timeline: 'public' })
|
||||||
|
mutations.addNewStatuses(state, { statuses: [deletion], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
|
expect(state.allStatuses).to.eql([otherStatus])
|
||||||
|
expect(state.timelines.public.statuses).to.eql([otherStatus])
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.eql([otherStatus])
|
||||||
|
expect(state.timelines.public.maxId).to.eql('3')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not update the maxId when the noIdUpdate flag is set', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
const secondStatus = makeMockStatus({id: '2'})
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
expect(state.timelines.public.maxId).to.eql('1')
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true })
|
||||||
|
expect(state.timelines.public.statuses).to.eql([secondStatus, status])
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status])
|
||||||
|
expect(state.timelines.public.maxId).to.eql('1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const nonVisibleStatus = makeMockStatus({id: '1'})
|
||||||
|
const status = makeMockStatus({id: '3'})
|
||||||
|
const statusTwo = makeMockStatus({id: '2'})
|
||||||
|
const statusThree = makeMockStatus({id: '4'})
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' })
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
|
expect(state.timelines.public.minVisibleId).to.equal('2')
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
|
expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus])
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits retweets from their status and links them', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
const retweet = makeMockStatus({id: '2', type: 'retweet'})
|
||||||
|
const modStatus = makeMockStatus({id: '1', text: 'something else'})
|
||||||
|
|
||||||
|
retweet.retweeted_status = status
|
||||||
|
|
||||||
|
// It adds both statuses, but only the retweet to visible.
|
||||||
|
mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true })
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
||||||
|
expect(state.timelines.public.statuses).to.have.length(1)
|
||||||
|
expect(state.allStatuses).to.have.length(2)
|
||||||
|
expect(state.allStatuses[0].id).to.equal('1')
|
||||||
|
expect(state.allStatuses[1].id).to.equal('2')
|
||||||
|
|
||||||
|
// It refers to the modified status.
|
||||||
|
mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' })
|
||||||
|
expect(state.allStatuses).to.have.length(2)
|
||||||
|
expect(state.allStatuses[0].id).to.equal('1')
|
||||||
|
expect(state.allStatuses[0].text).to.equal(modStatus.text)
|
||||||
|
expect(state.allStatuses[1].id).to.equal('2')
|
||||||
|
expect(retweet.retweeted_status.text).to.eql(modStatus.text)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('replaces existing statuses with the same id', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
const modStatus = makeMockStatus({id: '1', text: 'something else'})
|
||||||
|
|
||||||
|
// Add original status
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
||||||
|
expect(state.allStatuses).to.have.length(1)
|
||||||
|
|
||||||
|
// Add new version of status
|
||||||
|
mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' })
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
||||||
|
expect(state.allStatuses).to.have.length(1)
|
||||||
|
expect(state.allStatuses[0].text).to.eql(modStatus.text)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('replaces existing statuses with the same id, coming from a retweet', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
const modStatus = makeMockStatus({id: '1', text: 'something else'})
|
||||||
|
const retweet = makeMockStatus({id: '2', type: 'retweet'})
|
||||||
|
retweet.retweeted_status = modStatus
|
||||||
|
|
||||||
|
// Add original status
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
||||||
|
expect(state.allStatuses).to.have.length(1)
|
||||||
|
|
||||||
|
// Add new version of status
|
||||||
|
mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' })
|
||||||
|
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
||||||
|
// Don't add the retweet itself if the tweet is visible
|
||||||
|
expect(state.timelines.public.statuses).to.have.length(1)
|
||||||
|
expect(state.allStatuses).to.have.length(2)
|
||||||
|
expect(state.allStatuses[0].text).to.eql(modStatus.text)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles favorite actions', () => {
|
||||||
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({id: '1'})
|
||||||
|
|
||||||
|
const favorite = {
|
||||||
|
id: '2',
|
||||||
|
type: 'favorite',
|
||||||
|
in_reply_to_status_id: '1', // The API uses strings here...
|
||||||
|
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
|
||||||
|
text: 'a favorited something by b',
|
||||||
|
user: { id: '99' }
|
||||||
|
}
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
|
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
|
||||||
|
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
|
||||||
|
expect(state.timelines.public.maxId).to.eq(favorite.id)
|
||||||
|
|
||||||
|
// Adding it again does nothing
|
||||||
|
mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
|
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
|
||||||
|
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
|
||||||
|
expect(state.timelines.public.maxId).to.eq(favorite.id)
|
||||||
|
|
||||||
|
// If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request.
|
||||||
|
const user = {
|
||||||
|
id: '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
const ownFavorite = {
|
||||||
|
id: '3',
|
||||||
|
type: 'favorite',
|
||||||
|
in_reply_to_status_id: '1', // The API uses strings here...
|
||||||
|
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
|
||||||
|
text: 'a favorited something by b',
|
||||||
|
user
|
||||||
|
}
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user })
|
||||||
|
|
||||||
|
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
|
||||||
|
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
|
||||||
|
expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('add the statuses to allStatuses if no timeline is given', () => {
|
describe('showNewStatuses', () => {
|
||||||
const state = cloneDeep(defaultState)
|
it('resets the minId to the min of the visible statuses when adding new to visible statuses', () => {
|
||||||
const status = makeMockStatus({id: '1'})
|
const state = defaultState()
|
||||||
|
const status = makeMockStatus({ id: '10' })
|
||||||
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
|
const newStatus = makeMockStatus({ id: '20' })
|
||||||
|
mutations.addNewStatuses(state, { statuses: [newStatus], showImmediately: false, timeline: 'public' })
|
||||||
|
state.timelines.public.minId = '5'
|
||||||
|
mutations.showNewStatuses(state, { timeline: 'public' })
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status] })
|
expect(state.timelines.public.visibleStatuses.length).to.eql(2)
|
||||||
|
expect(state.timelines.public.minVisibleId).to.eql('10')
|
||||||
expect(state.allStatuses).to.eql([status])
|
expect(state.timelines.public.minId).to.eql('10')
|
||||||
expect(state.timelines.public.statuses).to.eql([])
|
})
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([])
|
|
||||||
expect(state.timelines.public.newStatusCount).to.equal(0)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('adds the status to allStatuses and to the given timeline, directly visible', () => {
|
describe('clearTimeline', () => {
|
||||||
const state = cloneDeep(defaultState)
|
it('keeps userId when clearing user timeline', () => {
|
||||||
const status = makeMockStatus({id: '1'})
|
const state = defaultState()
|
||||||
|
state.timelines.user.userId = 123
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
mutations.clearTimeline(state, { timeline: 'user' })
|
||||||
|
|
||||||
expect(state.allStatuses).to.eql([status])
|
expect(state.timelines.user.userId).to.eql(123)
|
||||||
expect(state.timelines.public.statuses).to.eql([status])
|
})
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([status])
|
|
||||||
expect(state.timelines.public.newStatusCount).to.equal(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('removes statuses by tag on deletion', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
const otherStatus = makeMockStatus({id: '3'})
|
|
||||||
status.uri = 'xxx'
|
|
||||||
const deletion = makeMockStatus({id: '2', type: 'deletion'})
|
|
||||||
deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.'
|
|
||||||
deletion.uri = 'xxx'
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status, otherStatus], showImmediately: true, timeline: 'public' })
|
|
||||||
mutations.addNewStatuses(state, { statuses: [deletion], showImmediately: true, timeline: 'public' })
|
|
||||||
|
|
||||||
expect(state.allStatuses).to.eql([otherStatus])
|
|
||||||
expect(state.timelines.public.statuses).to.eql([otherStatus])
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([otherStatus])
|
|
||||||
expect(state.timelines.public.maxId).to.eql('3')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not update the maxId when the noIdUpdate flag is set', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
const secondStatus = makeMockStatus({id: '2'})
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
|
||||||
expect(state.timelines.public.maxId).to.eql('1')
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true })
|
|
||||||
expect(state.timelines.public.statuses).to.eql([secondStatus, status])
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status])
|
|
||||||
expect(state.timelines.public.maxId).to.eql('1')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const nonVisibleStatus = makeMockStatus({id: '1'})
|
|
||||||
const status = makeMockStatus({id: '3'})
|
|
||||||
const statusTwo = makeMockStatus({id: '2'})
|
|
||||||
const statusThree = makeMockStatus({id: '4'})
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' })
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
|
||||||
mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' })
|
|
||||||
|
|
||||||
expect(state.timelines.public.minVisibleId).to.equal('2')
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' })
|
|
||||||
|
|
||||||
expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus])
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('splits retweets from their status and links them', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
const retweet = makeMockStatus({id: '2', type: 'retweet'})
|
|
||||||
const modStatus = makeMockStatus({id: '1', text: 'something else'})
|
|
||||||
|
|
||||||
retweet.retweeted_status = status
|
|
||||||
|
|
||||||
// It adds both statuses, but only the retweet to visible.
|
|
||||||
mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true })
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
|
||||||
expect(state.timelines.public.statuses).to.have.length(1)
|
|
||||||
expect(state.allStatuses).to.have.length(2)
|
|
||||||
expect(state.allStatuses[0].id).to.equal('1')
|
|
||||||
expect(state.allStatuses[1].id).to.equal('2')
|
|
||||||
|
|
||||||
// It refers to the modified status.
|
|
||||||
mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' })
|
|
||||||
expect(state.allStatuses).to.have.length(2)
|
|
||||||
expect(state.allStatuses[0].id).to.equal('1')
|
|
||||||
expect(state.allStatuses[0].text).to.equal(modStatus.text)
|
|
||||||
expect(state.allStatuses[1].id).to.equal('2')
|
|
||||||
expect(retweet.retweeted_status.text).to.eql(modStatus.text)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('replaces existing statuses with the same id', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
const modStatus = makeMockStatus({id: '1', text: 'something else'})
|
|
||||||
|
|
||||||
// Add original status
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
|
||||||
expect(state.allStatuses).to.have.length(1)
|
|
||||||
|
|
||||||
// Add new version of status
|
|
||||||
mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' })
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
|
||||||
expect(state.allStatuses).to.have.length(1)
|
|
||||||
expect(state.allStatuses[0].text).to.eql(modStatus.text)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('replaces existing statuses with the same id, coming from a retweet', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
const modStatus = makeMockStatus({id: '1', text: 'something else'})
|
|
||||||
const retweet = makeMockStatus({id: '2', type: 'retweet'})
|
|
||||||
retweet.retweeted_status = modStatus
|
|
||||||
|
|
||||||
// Add original status
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
|
||||||
expect(state.allStatuses).to.have.length(1)
|
|
||||||
|
|
||||||
// Add new version of status
|
|
||||||
mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' })
|
|
||||||
expect(state.timelines.public.visibleStatuses).to.have.length(1)
|
|
||||||
// Don't add the retweet itself if the tweet is visible
|
|
||||||
expect(state.timelines.public.statuses).to.have.length(1)
|
|
||||||
expect(state.allStatuses).to.have.length(2)
|
|
||||||
expect(state.allStatuses[0].text).to.eql(modStatus.text)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('handles favorite actions', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
const status = makeMockStatus({id: '1'})
|
|
||||||
|
|
||||||
const favorite = {
|
|
||||||
id: '2',
|
|
||||||
type: 'favorite',
|
|
||||||
in_reply_to_status_id: '1', // The API uses strings here...
|
|
||||||
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
|
|
||||||
text: 'a favorited something by b',
|
|
||||||
user: { id: '99' }
|
|
||||||
}
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
|
||||||
mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' })
|
|
||||||
|
|
||||||
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
|
|
||||||
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
|
|
||||||
expect(state.timelines.public.maxId).to.eq(favorite.id)
|
|
||||||
|
|
||||||
// Adding it again does nothing
|
|
||||||
mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' })
|
|
||||||
|
|
||||||
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
|
|
||||||
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
|
|
||||||
expect(state.timelines.public.maxId).to.eq(favorite.id)
|
|
||||||
|
|
||||||
// If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request.
|
|
||||||
const user = {
|
|
||||||
id: '1'
|
|
||||||
}
|
|
||||||
|
|
||||||
const ownFavorite = {
|
|
||||||
id: '3',
|
|
||||||
type: 'favorite',
|
|
||||||
in_reply_to_status_id: '1', // The API uses strings here...
|
|
||||||
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
|
|
||||||
text: 'a favorited something by b',
|
|
||||||
user
|
|
||||||
}
|
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user })
|
|
||||||
|
|
||||||
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
|
|
||||||
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
|
|
||||||
expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('keeps userId when clearing user timeline', () => {
|
|
||||||
const state = cloneDeep(defaultState)
|
|
||||||
state.timelines.user.userId = 123
|
|
||||||
|
|
||||||
mutations.clearTimeline(state, { timeline: 'user' })
|
|
||||||
|
|
||||||
expect(state.timelines.user.userId).to.eql(123)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('notifications', () => {
|
describe('notifications', () => {
|
||||||
it('removes a notification when the notice gets removed', () => {
|
it('removes a notification when the notice gets removed', () => {
|
||||||
const user = { id: '1' }
|
const user = { id: '1' }
|
||||||
const state = cloneDeep(defaultState)
|
const state = defaultState()
|
||||||
const status = makeMockStatus({id: '1'})
|
const status = makeMockStatus({id: '1'})
|
||||||
const otherStatus = makeMockStatus({id: '3'})
|
const otherStatus = makeMockStatus({id: '3'})
|
||||||
const mentionedStatus = makeMockStatus({id: '2'})
|
const mentionedStatus = makeMockStatus({id: '2'})
|
||||||
|
Loading…
Reference in New Issue
Block a user