aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.js4
-rw-r--r--src/App.scss25
-rw-r--r--src/App.vue1
-rw-r--r--src/boot/after_store.js6
-rw-r--r--src/boot/routes.js2
-rw-r--r--src/components/attachment/attachment.vue1
-rw-r--r--src/components/basic_user_card/basic_user_card.js4
-rw-r--r--src/components/basic_user_card/basic_user_card.vue33
-rw-r--r--src/components/media_modal/media_modal.vue15
-rw-r--r--src/components/mobile_post_status_modal/mobile_post_status_modal.js91
-rw-r--r--src/components/mobile_post_status_modal/mobile_post_status_modal.vue76
-rw-r--r--src/components/notification/notification.js4
-rw-r--r--src/components/notification/notification.vue4
-rw-r--r--src/components/notifications/notifications.js3
-rw-r--r--src/components/notifications/notifications.scss4
-rw-r--r--src/components/post_status_form/post_status_form.js6
-rw-r--r--src/components/post_status_form/post_status_form.vue7
-rw-r--r--src/components/settings/settings.js3
-rw-r--r--src/components/settings/settings.vue14
-rw-r--r--src/components/side_drawer/side_drawer.js4
-rw-r--r--src/components/side_drawer/side_drawer.vue26
-rw-r--r--src/components/status/status.js4
-rw-r--r--src/components/status/status.vue7
-rw-r--r--src/components/timeline/timeline.js4
-rw-r--r--src/components/user_card/user_card.js (renamed from src/components/user_card_content/user_card_content.js)25
-rw-r--r--src/components/user_card/user_card.vue (renamed from src/components/user_card_content/user_card_content.vue)94
-rw-r--r--src/components/user_panel/user_panel.js4
-rw-r--r--src/components/user_panel/user_panel.vue12
-rw-r--r--src/components/user_profile/user_profile.js4
-rw-r--r--src/components/user_profile/user_profile.vue11
-rw-r--r--src/i18n/cs.json15
-rw-r--r--src/i18n/en.json5
-rw-r--r--src/i18n/eo.json1
-rw-r--r--src/i18n/es.json1
-rw-r--r--src/i18n/ja.json1
-rw-r--r--src/i18n/oc.json1
-rw-r--r--src/i18n/pt.json1
-rw-r--r--src/modules/chat.js10
-rw-r--r--src/modules/instance.js1
-rw-r--r--src/modules/statuses.js38
-rw-r--r--src/modules/users.js6
41 files changed, 388 insertions, 190 deletions
diff --git a/src/App.js b/src/App.js
index 214e0f48..5c27a3df 100644
--- a/src/App.js
+++ b/src/App.js
@@ -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 MediaModal from './components/media_modal/media_modal.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'
export default {
@@ -22,7 +23,8 @@ export default {
WhoToFollowPanel,
ChatPanel,
MediaModal,
- SideDrawer
+ SideDrawer,
+ MobilePostStatusModal
},
data: () => ({
mobileActivePanel: 'timeline',
diff --git a/src/App.scss b/src/App.scss
index a0d1a804..598735d9 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -671,6 +671,31 @@ nav {
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 {
font-size: 1.2em;
}
diff --git a/src/App.vue b/src/App.vue
index acbbeb75..4fff3d1d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -50,6 +50,7 @@
<media-modal></media-modal>
</div>
<chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel>
+ <MobilePostStatusModal />
</div>
</template>
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 53ecc083..cd88c188 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -89,10 +89,8 @@ const afterStoreSetup = ({ store, i18n }) => {
copyInstanceOption('noAttachmentLinks')
copyInstanceOption('showFeaturesPanel')
- if ((config.chatDisabled)) {
+ if (config.chatDisabled) {
store.dispatch('disableChat')
- } else {
- store.dispatch('initializeSocket')
}
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: 'gopherAvailable', value: features.includes('gopher') })
+ store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })
+
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
const suggestions = metadata.suggestions
diff --git a/src/boot/routes.js b/src/boot/routes.js
index cfbcb1fe..7e54a98b 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -13,7 +13,6 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue'
import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
import UserSearch from 'components/user_search/user_search.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 ChatPanel from 'components/chat_panel/chat_panel.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: 'user-settings', path: '/user-settings', component: UserSettings },
{ name: 'notifications', path: '/:username/notifications', component: Notifications },
- { name: 'new-status', path: '/:username/new-status', component: UserPanel },
{ name: 'login', path: '/login', component: LoginForm },
{ name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) },
{ name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 76affe2d..c58bebd3 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -160,6 +160,7 @@
.hider {
position: absolute;
+ right: 0;
white-space: nowrap;
margin: 10px;
padding: 5px;
diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js
index a8441446..87085a28 100644
--- a/src/components/basic_user_card/basic_user_card.js
+++ b/src/components/basic_user_card/basic_user_card.js
@@ -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 generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@@ -12,7 +12,7 @@ const BasicUserCard = {
}
},
components: {
- UserCardContent,
+ UserCard,
UserAvatar
},
methods: {
diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue
index 77fb0aa0..9b80c72b 100644
--- a/src/components/basic_user_card/basic_user_card.vue
+++ b/src/components/basic_user_card/basic_user_card.vue
@@ -1,18 +1,18 @@
<template>
- <div class="user-card">
+ <div class="basic-user-card">
<router-link :to="userProfileLink(user)">
<UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
</router-link>
- <div class="user-card-expanded-content" v-if="userExpanded">
- <user-card-content :user="user" :switcher="false"></user-card-content>
+ <div class="basic-user-card-expanded-content" v-if="userExpanded">
+ <UserCard :user="user" :rounded="true" :bordered="true"/>
</div>
- <div class="user-card-collapsed-content" v-else>
- <div :title="user.name" class="user-card-user-name">
+ <div class="basic-user-card-collapsed-content" v-else>
+ <div :title="user.name" class="basic-user-card-user-name">
<span v-if="user.name_html" v-html="user.name_html"></span>
<span v-else>{{ user.name }}</span>
</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}}
</router-link>
</div>
@@ -26,15 +26,15 @@
<style lang="scss">
@import '../../_variables.scss';
-.user-card {
+.basic-user-card {
display: flex;
flex: 1 0;
+ margin: 0;
padding-top: 0.6em;
padding-right: 1em;
padding-bottom: 0.6em;
padding-left: 1em;
border-bottom: 1px solid;
- margin: 0;
border-bottom-color: $fallback--border;
border-bottom-color: var(--border, $fallback--border);
@@ -57,23 +57,6 @@
&-expanded-content {
flex: 1;
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>
diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue
index 427bf12b..7f666603 100644
--- a/src/components/media_modal/media_modal.vue
+++ b/src/components/media_modal/media_modal.vue
@@ -1,5 +1,5 @@
<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>
<VideoAttachment
class="modal-image"
@@ -32,18 +32,7 @@
<style lang="scss">
@import '../../_variables.scss';
-.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);
-
+.media-modal-view {
&:hover {
.modal-view-button-arrow {
opacity: 0.75;
diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.js b/src/components/mobile_post_status_modal/mobile_post_status_modal.js
new file mode 100644
index 00000000..2f24dd08
--- /dev/null
+++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.js
@@ -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
diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
new file mode 100644
index 00000000..0a451c28
--- /dev/null
+++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
@@ -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>
diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js
index 7d9807de..fe5b7018 100644
--- a/src/components/notification/notification.js
+++ b/src/components/notification/notification.js
@@ -1,6 +1,6 @@
import Status from '../status/status.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 generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@@ -13,7 +13,7 @@ const Notification = {
},
props: [ 'notification' ],
components: {
- Status, UserAvatar, UserCardContent
+ Status, UserAvatar, UserCard
},
methods: {
toggleUserExpanded () {
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index 87925cfc..5e9cef97 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -5,9 +5,7 @@
<UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/>
</a>
<div class='notification-right'>
- <div class="usercard notification-usercard" v-if="userExpanded">
- <user-card-content :user="notification.action.user" :switcher="false"></user-card-content>
- </div>
+ <UserCard :user="notification.action.user" :rounded="true" :bordered="true" v-if="userExpanded"/>
<span class="notification-details">
<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>
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 5e95631a..9fc5e38a 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -11,7 +11,8 @@ const Notifications = {
const store = this.$store
const credentials = store.state.users.currentUser.credentials
- notificationsFetcher.startFetching({ store, credentials })
+ const fetcherId = notificationsFetcher.startFetching({ store, credentials })
+ this.$store.commit('setNotificationFetcher', { fetcherId })
},
data () {
return {
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index 2240c10a..c0b458cc 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -45,10 +45,6 @@
}
}
- .notification-usercard {
- margin: 0;
- }
-
.non-mention {
display: flex;
flex: 1;
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index c28c51bf..1f0df35a 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -171,6 +171,9 @@ const PostStatusForm = {
},
formattingOptionsEnabled () {
return this.$store.state.instance.formattingOptionsEnabled
+ },
+ postFormats () {
+ return this.$store.state.instance.postFormats || []
}
},
methods: {
@@ -219,6 +222,9 @@ const PostStatusForm = {
this.highlighted = 0
}
},
+ onKeydown (e) {
+ e.stopPropagation()
+ },
setCaret ({target: {selectionStart}}) {
this.caret = selectionStart
},
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 40d24b97..3d1df91b 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -20,6 +20,7 @@
ref="textarea"
@click="setCaret"
@keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
+ @keydown="onKeydown"
@keydown.down="cycleForward"
@keydown.up="cycleBackward"
@keydown.shift.tab="cycleBackward"
@@ -38,9 +39,9 @@
<span class="text-format" v-if="formattingOptionsEnabled">
<label for="post-content-type" class="select">
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
- <option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option>
- <option value="text/html">HTML</option>
- <option value="text/markdown">Markdown</option>
+ <option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
+ {{$t(`post_status.content_type["${postFormat}"]`)}}
+ </option>
</select>
<i class="icon-down-open"></i>
</label>
diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js
index 6e2dff7b..979457a5 100644
--- a/src/components/settings/settings.js
+++ b/src/components/settings/settings.js
@@ -93,6 +93,9 @@ const settings = {
currentSaveStateNotice () {
return this.$store.state.interface.settings.currentSaveStateNotice
},
+ postFormats () {
+ return this.$store.state.instance.postFormats || []
+ },
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }
},
watch: {
diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue
index 5041b3a3..d2346747 100644
--- a/src/components/settings/settings.vue
+++ b/src/components/settings/settings.vue
@@ -105,17 +105,9 @@
{{$t('settings.post_status_content_type')}}
<label for="postContentType" class="select">
<select id="postContentType" v-model="postContentTypeLocal">
- <option value="text/plain">
- {{$t('settings.status_content_type_plain')}}
- {{postContentTypeDefault == 'text/plain' ? $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 v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
+ {{$t(`post_status.content_type["${postFormat}"]`)}}
+ {{postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : ''}}
</option>
</select>
<i class="icon-down-open"/>
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index b5c49059..ad3738d1 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -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'
// TODO: separate touch gesture stuff into their own utils if more components want them
@@ -12,7 +12,7 @@ const SideDrawer = {
closed: true,
touchCoord: [0, 0]
}),
- components: { UserCardContent },
+ components: { UserCard },
computed: {
currentUser () {
return this.$store.state.users.currentUser
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 6996380d..95ee21b4 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -8,19 +8,14 @@
@touchmove="touchMove"
>
<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>
<img :src="logo"/>
<span>{{sitename}}</span>
</div>
</div>
<ul>
- <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">
+ <li v-if="!currentUser" @click="toggleDrawer">
<router-link :to="{ name: 'login' }">
{{ $t("login.login") }}
</router-link>
@@ -119,14 +114,14 @@
}
.side-drawer-container-open {
- transition-delay: 0.0s;
- transition-property: left;
+ transition: 0.35s;
+ transition-property: background-color;
+ background-color: rgba(0, 0, 0, 0.5);
}
.side-drawer-container-closed {
left: -100%;
- transition-delay: 0.5s;
- transition-property: left;
+ background-color: rgba(0, 0, 0, 0);
}
.side-drawer-click-outside {
@@ -181,15 +176,6 @@
display: flex;
padding: 0;
margin: 0;
-
- .profile-panel-background {
- border-radius: 0;
- .panel-heading {
- background: transparent;
- flex-direction: column;
- align-items: stretch;
- }
- }
}
.side-drawer ul {
diff --git a/src/components/status/status.js b/src/components/status/status.js
index fbbca6c4..9e18fe15 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -3,7 +3,7 @@ import FavoriteButton from '../favorite_button/favorite_button.vue'
import RetweetButton from '../retweet_button/retweet_button.vue'
import DeleteButton from '../delete_button/delete_button.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 Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue'
@@ -259,7 +259,7 @@ const Status = {
RetweetButton,
DeleteButton,
PostStatusForm,
- UserCardContent,
+ UserCard,
UserAvatar,
Gallery,
LinkPreview
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 4dd20362..1f6d0325 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -31,9 +31,7 @@
</router-link>
</div>
<div class="status-body">
- <div class="usercard" v-if="userExpanded">
- <user-card-content :user="status.user" :switcher="false"></user-card-content>
- </div>
+ <UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard" v-if="userExpanded"/>
<div v-if="!noHeading" class="media-heading">
<div class="heading-name-row">
<div class="name-and-account-name">
@@ -248,8 +246,7 @@ $status-margin: 0.75em;
padding: 0;
}
- .usercard {
- margin: 0;
+ .status-usercard {
margin-bottom: $status-margin;
}
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 655bfb3f..c45f8947 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -132,7 +132,9 @@ const Timeline = {
}
if (count > 0) {
// 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.unfocused && this.$store.state.config.pauseOnUnfocused)
) {
diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card/user_card.js
index 7a7b89d4..80d15a27 100644
--- a/src/components/user_card_content/user_card_content.js
+++ b/src/components/user_card/user_card.js
@@ -4,7 +4,7 @@ import { requestFollow, requestUnfollow } from '../../services/follow_manipulate
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
export default {
- props: [ 'user', 'switcher', 'selected', 'hideBio' ],
+ props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered' ],
data () {
return {
followRequestInProgress: false,
@@ -16,7 +16,14 @@ export default {
}
},
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
? this.$store.state.config.customTheme.colors.bg // v2
: this.$store.state.config.colors.bg // v1
@@ -93,22 +100,30 @@ export default {
},
methods: {
followUser () {
+ const store = this.$store
this.followRequestInProgress = true
- requestFollow(this.user, this.$store).then(({sent}) => {
+ requestFollow(this.user, store).then(({sent}) => {
this.followRequestInProgress = false
this.followRequestSent = sent
})
},
unfollowUser () {
+ const store = this.$store
this.followRequestInProgress = true
- requestUnfollow(this.user, this.$store).then(() => {
+ requestUnfollow(this.user, store).then(() => {
this.followRequestInProgress = false
+ store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
})
},
blockUser () {
const store = this.$store
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 () {
const store = this.$store
diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card/user_card.vue
index 702c3385..690e1bde 100644
--- a/src/components/user_card_content/user_card_content.vue
+++ b/src/components/user_card/user_card.vue
@@ -1,6 +1,6 @@
<template>
-<div id="heading" class="profile-panel-background" :style="headingStyle">
- <div class="panel-heading text-center">
+<div class="user-card" :class="classes" :style="style">
+ <div class="panel-heading">
<div class='user-info'>
<div class='container'>
<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-else>{{user.name}}</div>
<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>
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
<i class="icon-link-ext usersettings"></i>
@@ -108,7 +108,7 @@
</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 class="user-count" v-on:click.prevent="setProfileView('statuses')">
<h5>{{ $t('user_card.statuses') }}</h5>
@@ -123,40 +123,75 @@
<span>{{user.followers_count}}</span>
</div>
</div>
- <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p>
- <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</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="user-card-bio">{{ user.description }}</p>
</div>
</div>
</template>
-<script src="./user_card_content.js"></script>
+<script src="./user_card.js"></script>
<style lang="scss">
@import '../../_variables.scss';
-.profile-panel-background {
+.user-card {
background-size: cover;
- border-radius: $fallback--panelRadius;
- border-radius: var(--panelRadius, $fallback--panelRadius);
overflow: hidden;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-
.panel-heading {
padding: .5em 0;
text-align: center;
box-shadow: none;
+ background: transparent;
+ flex-direction: column;
+ align-items: stretch;
}
-}
-.profile-panel-body {
- 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), var(--bg, $fallback--bg) 80%);
+ .panel-body {
+ 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), var(--bg, $fallback--bg) 80%);
+ }
+
+ p {
+ margin-bottom: 0;
+ }
- .profile-bio {
+ &-bio {
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;
}
}
-
-.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>
diff --git a/src/components/user_panel/user_panel.js b/src/components/user_panel/user_panel.js
index 15804b88..d4478290 100644
--- a/src/components/user_panel/user_panel.js
+++ b/src/components/user_panel/user_panel.js
@@ -1,6 +1,6 @@
import LoginForm from '../login_form/login_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 = {
computed: {
@@ -9,7 +9,7 @@ const UserPanel = {
components: {
LoginForm,
PostStatusForm,
- UserCardContent
+ UserCard
}
}
diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue
index 2d5cb500..8310f30e 100644
--- a/src/components/user_panel/user_panel.vue
+++ b/src/components/user_panel/user_panel.vue
@@ -1,7 +1,7 @@
<template>
<div class="user-panel">
<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">
<post-status-form v-if='user'></post-status-form>
</div>
@@ -11,13 +11,3 @@
</template>
<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>
diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js
index cdf1cee9..54126514 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -1,6 +1,6 @@
import { compose } from 'vue-compose'
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 Timeline from '../timeline/timeline.vue'
import withLoadMore from '../../hocs/with_load_more/with_load_more'
@@ -147,7 +147,7 @@ const UserProfile = {
}
},
components: {
- UserCardContent,
+ UserCard,
Timeline,
FollowerList,
FriendList
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 8090efa5..7d4a8b1f 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -1,11 +1,7 @@
<template>
<div>
<div v-if="user.id" class="user-profile panel panel-default">
- <user-card-content
- :user="user"
- :switcher="true"
- :selected="timeline.viewing"
- />
+ <UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
<Timeline
:label="$t('user_card.statuses')"
@@ -64,11 +60,6 @@
flex: 2;
flex-basis: 500px;
- .profile-panel-background .panel-heading {
- background: transparent;
- flex-direction: column;
- align-items: stretch;
- }
.userlist-placeholder {
display: flex;
justify-content: center;
diff --git a/src/i18n/cs.json b/src/i18n/cs.json
index 6326032c..51e9d342 100644
--- a/src/i18n/cs.json
+++ b/src/i18n/cs.json
@@ -71,7 +71,9 @@
"account_not_locked_warning_link": "uzamčen",
"attachments_sensitive": "Označovat přílohy jako citlivé",
"content_type": {
- "plain_text": "Prostý text"
+ "plain_text": "Prostý text",
+ "text/html": "HTML",
+ "text/markdown": "Markdown"
},
"content_warning": "Předmět (volitelný)",
"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",
"username_placeholder": "např. lain",
"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": {
"username_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)",
"replies_in_timeline": "Odpovědi v časové ose",
"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_self": "Zobrazit pouze odpovědi směřované na mě",
"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_noop": "Nekopírovat",
"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",
"streaming": "Povolit automatické streamování nových příspěvků při rolování nahoru",
"text": "Text",
@@ -339,7 +340,7 @@
"button": "Tlačítko",
"text": "Spousta dalšího {0} a {1}",
"mono": "obsahu",
- "input": "Just landed in L.A.",
+ "input": "Právě jsem přistál v L.A.",
"faint_link": "pomocný manuál",
"fine_print": "Přečtěte si náš {0} a nenaučte se nic užitečného!",
"header_faint": "Tohle je v pohodě",
@@ -361,7 +362,7 @@
"no_statuses": "Žádné příspěvky"
},
"status": {
- "reply_to": "Odpovědět uživateli",
+ "reply_to": "Odpověď uživateli",
"replies_list": "Odpovědi:"
},
@@ -413,7 +414,7 @@
"upload":{
"error": {
"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"
},
"file_size_units": {
diff --git a/src/i18n/en.json b/src/i18n/en.json
index c5a4a90d..01fe2fba 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -71,7 +71,9 @@
"account_not_locked_warning_link": "locked",
"attachments_sensitive": "Mark attachments as sensitive",
"content_type": {
- "plain_text": "Plain text"
+ "text/plain": "Plain text",
+ "text/html": "HTML",
+ "text/markdown": "Markdown"
},
"content_warning": "Subject (optional)",
"default": "Just landed in L.A.",
@@ -221,7 +223,6 @@
"subject_line_mastodon": "Like mastodon: copy as is",
"subject_line_noop": "Do not copy",
"post_status_content_type": "Post status content type",
- "status_content_type_plain": "Plain text",
"stop_gifs": "Play-on-hover GIFs",
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
"text": "Text",
diff --git a/src/i18n/eo.json b/src/i18n/eo.json
index 2438b4d5..34851a44 100644
--- a/src/i18n/eo.json
+++ b/src/i18n/eo.json
@@ -221,7 +221,6 @@
"subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe",
"subject_line_noop": "Ne kopii",
"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",
"streaming": "Ŝalti memfaran fluigon de novaj afiŝoj ĉe la supro de la paĝo",
"text": "Teksto",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index 167e8c42..fe96dd08 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -202,7 +202,6 @@
"subject_line_mastodon": "Tipo mastodon: copiar como es",
"subject_line_noop": "No copiar",
"post_status_content_type": "Formato de publicación",
- "status_content_type_plain": "Texto plano",
"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",
"text": "Texto",
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index b51fa7fd..f39a5a7c 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -202,7 +202,6 @@
"subject_line_mastodon": "マストドンふう: そのままコピー",
"subject_line_noop": "コピーしない",
"post_status_content_type": "とうこうのコンテントタイプ",
- "status_content_type_plain": "プレーンテキスト",
"stop_gifs": "カーソルをかさねたとき、GIFをうごかす",
"streaming": "うえまでスクロールしたとき、じどうてきにストリーミングする",
"text": "もじ",
diff --git a/src/i18n/oc.json b/src/i18n/oc.json
index ef32f83b..fd5ccc97 100644
--- a/src/i18n/oc.json
+++ b/src/i18n/oc.json
@@ -221,7 +221,6 @@
"subject_line_mastodon": "Coma mastodon : copiar tal coma es",
"subject_line_noop": "Copiar pas",
"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",
"streaming": "Activar lo cargament automatic dels novèls estatus en anar amont",
"text": "Tèxt",
diff --git a/src/i18n/pt.json b/src/i18n/pt.json
index 39ff6c63..cbc2c9a3 100644
--- a/src/i18n/pt.json
+++ b/src/i18n/pt.json
@@ -221,7 +221,6 @@
"subject_line_mastodon": "Como o Mastodon: copiar como está",
"subject_line_noop": "Não copiar",
"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",
"streaming": "Habilitar o fluxo automático de postagens quando ao topo da página",
"text": "Texto",
diff --git a/src/modules/chat.js b/src/modules/chat.js
index 383ac75c..2804e577 100644
--- a/src/modules/chat.js
+++ b/src/modules/chat.js
@@ -1,12 +1,16 @@
const chat = {
state: {
messages: [],
- channel: {state: ''}
+ channel: {state: ''},
+ socket: null
},
mutations: {
setChannel (state, channel) {
state.channel = channel
},
+ setSocket (state, socket) {
+ state.socket = socket
+ },
addMessage (state, message) {
state.messages.push(message)
state.messages = state.messages.slice(-19, 20)
@@ -16,8 +20,12 @@ const chat = {
}
},
actions: {
+ disconnectFromChat (store) {
+ store.state.socket.disconnect()
+ },
initializeChat (store, socket) {
const channel = socket.channel('chat:public')
+ store.commit('setSocket', socket)
channel.on('new_msg', (msg) => {
store.commit('addMessage', msg)
})
diff --git a/src/modules/instance.js b/src/modules/instance.js
index c31d02b9..24c52f9c 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -37,6 +37,7 @@ const defaultState = {
emoji: [],
customEmoji: [],
restrictedNicknames: [],
+ postFormats: [],
// Feature-set, apparently, not everything here is reported...
mediaProxyAvailable: false,
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 96a3549d..6b512fa3 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -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 parse from '../services/status_parser/status_parser.js'
@@ -19,7 +19,7 @@ const emptyTl = (userId = 0) => ({
flushMarker: 0
})
-export const defaultState = {
+export const defaultState = () => ({
allStatuses: [],
allStatusesObject: {},
maxId: 0,
@@ -30,7 +30,8 @@ export const defaultState = {
data: [],
idStore: {},
loading: false,
- error: false
+ error: false,
+ fetcherId: null
},
favorites: new Set(),
error: false,
@@ -45,7 +46,7 @@ export const defaultState = {
tag: emptyTl(),
dms: emptyTl()
}
-}
+})
export const prepareStatus = (status) => {
// 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 = {
addNewStatuses,
addNewNotifications,
+ removeStatus,
showNewStatuses (state, { timeline }) {
const oldTimeline = (state.timelines[timeline])
oldTimeline.newStatusCount = 0
oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)
oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id
+ oldTimeline.minId = oldTimeline.minVisibleId
oldTimeline.visibleStatusesObject = {}
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 }) {
state.timelines[timeline] = emptyTl(state.timelines[timeline].userId)
},
@@ -374,7 +396,7 @@ export const mutations = {
}
const statuses = {
- state: defaultState,
+ state: defaultState(),
actions: {
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })
@@ -394,6 +416,12 @@ const statuses = {
setNotificationsSilence ({ rootState, commit }, { 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) {
commit('setDeleted', { status })
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
diff --git a/src/modules/users.js b/src/modules/users.js
index 093af497..26884750 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -292,9 +292,12 @@ const users = {
logout (store) {
store.commit('clearCurrentUser')
+ store.dispatch('disconnectFromChat')
store.commit('setToken', false)
store.dispatch('stopFetching', 'friends')
store.commit('setBackendInteractor', backendInteractorService())
+ store.dispatch('stopFetchingNotifications')
+ store.commit('resetStatuses')
},
loginUser (store, accessToken) {
return new Promise((resolve, reject) => {
@@ -319,6 +322,9 @@ const users = {
if (user.token) {
store.dispatch('setWsToken', user.token)
+
+ // Initialize the chat socket.
+ store.dispatch('initializeSocket')
}
// Start getting fresh posts.