aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/attachment/attachment.vue3
-rw-r--r--src/components/basic_user_card/basic_user_card.vue47
-rw-r--r--src/components/block_card/block_card.vue16
-rw-r--r--src/components/follow_card/follow_card.js45
-rw-r--r--src/components/follow_card/follow_card.vue53
-rw-r--r--src/components/follow_list/follow_list.js68
-rw-r--r--src/components/follow_list/follow_list.vue33
-rw-r--r--src/components/follow_request_card/follow_request_card.js20
-rw-r--r--src/components/follow_request_card/follow_request_card.vue29
-rw-r--r--src/components/follow_requests/follow_requests.js13
-rw-r--r--src/components/follow_requests/follow_requests.vue2
-rw-r--r--src/components/gallery/gallery.vue4
-rw-r--r--src/components/image_cropper/image_cropper.js8
-rw-r--r--src/components/link-preview/link-preview.vue5
-rw-r--r--src/components/nav_panel/nav_panel.js13
-rw-r--r--src/components/nav_panel/nav_panel.vue4
-rw-r--r--src/components/notification/notification.vue6
-rw-r--r--src/components/notifications/notifications.scss2
-rw-r--r--src/components/post_status_form/post_status_form.js3
-rw-r--r--src/components/post_status_form/post_status_form.vue10
-rw-r--r--src/components/settings/settings.js3
-rw-r--r--src/components/settings/settings.vue16
-rw-r--r--src/components/side_drawer/side_drawer.js3
-rw-r--r--src/components/side_drawer/side_drawer.vue4
-rw-r--r--src/components/status/status.js2
-rw-r--r--src/components/status/status.vue267
-rw-r--r--src/components/timeline/timeline.js6
-rw-r--r--src/components/user_card/user_card.js64
-rw-r--r--src/components/user_card/user_card.vue159
-rw-r--r--src/components/user_card_content/user_card_content.js14
-rw-r--r--src/components/user_card_content/user_card_content.vue28
-rw-r--r--src/components/user_profile/user_profile.js51
-rw-r--r--src/components/user_profile/user_profile.vue15
-rw-r--r--src/components/user_search/user_search.js4
-rw-r--r--src/components/user_search/user_search.vue2
-rw-r--r--src/components/user_settings/user_settings.js4
-rw-r--r--src/components/who_to_follow/who_to_follow.js4
-rw-r--r--src/components/who_to_follow/who_to_follow.vue2
38 files changed, 495 insertions, 537 deletions
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 7e972026..c58bebd3 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -88,7 +88,7 @@
.attachment {
position: relative;
- margin: 0.5em 0.5em 0em 0em;
+ margin-top: 0.5em;
align-self: flex-start;
line-height: 0;
@@ -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.vue b/src/components/basic_user_card/basic_user_card.vue
index 4ede15e9..77fb0aa0 100644
--- a/src/components/basic_user_card/basic_user_card.vue
+++ b/src/components/basic_user_card/basic_user_card.vue
@@ -1,26 +1,22 @@
<template>
<div class="user-card">
<router-link :to="userProfileLink(user)">
- <UserAvatar class="avatar" :compact="true" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
+ <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>
<div class="user-card-collapsed-content" v-else>
- <div class="user-card-primary-area">
- <div :title="user.name" class="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-screen-name' :to="userProfileLink(user)">
- @{{user.screen_name}}
- </router-link>
- </div>
+ <div :title="user.name" class="user-card-user-name">
+ <span v-if="user.name_html" v-html="user.name_html"></span>
+ <span v-else>{{ user.name }}</span>
</div>
- <div class="user-card-secondary-area">
- <slot name="secondary-area"></slot>
+ <div>
+ <router-link class="user-card-screen-name" :to="userProfileLink(user)">
+ @{{user.screen_name}}
+ </router-link>
</div>
+ <slot></slot>
</div>
</div>
</template>
@@ -46,30 +42,21 @@
margin-left: 0.7em;
text-align: left;
flex: 1;
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
+ min-width: 0;
}
- &-primary-area {
- flex: 1;
- .user-name {
- img {
- object-fit: contain;
- height: 16px;
- width: 16px;
- vertical-align: middle;
- }
+ &-user-name {
+ img {
+ object-fit: contain;
+ height: 16px;
+ width: 16px;
+ vertical-align: middle;
}
}
- &-secondary-area {
- flex: none;
- }
-
&-expanded-content {
flex: 1;
- margin: 0.2em 0 0 0.7em;
+ margin-left: 0.7em;
border-radius: $fallback--panelRadius;
border-radius: var(--panelRadius, $fallback--panelRadius);
border-style: solid;
diff --git a/src/components/block_card/block_card.vue b/src/components/block_card/block_card.vue
index ed7fe30b..8eb56e25 100644
--- a/src/components/block_card/block_card.vue
+++ b/src/components/block_card/block_card.vue
@@ -1,6 +1,6 @@
<template>
<basic-user-card :user="user">
- <template slot="secondary-area">
+ <div class="block-card-content-container">
<button class="btn btn-default" @click="unblockUser" :disabled="progress" v-if="blocked">
<template v-if="progress">
{{ $t('user_card.unblock_progress') }}
@@ -17,8 +17,18 @@
{{ $t('user_card.block') }}
</template>
</button>
- </template>
+ </div>
</basic-user-card>
</template>
-<script src="./block_card.js"></script> \ No newline at end of file
+<script src="./block_card.js"></script>
+
+<style lang="scss">
+.block-card-content-container {
+ margin-top: 0.5em;
+ text-align: right;
+ button {
+ width: 10em;
+ }
+}
+</style>
diff --git a/src/components/follow_card/follow_card.js b/src/components/follow_card/follow_card.js
new file mode 100644
index 00000000..425c9c3e
--- /dev/null
+++ b/src/components/follow_card/follow_card.js
@@ -0,0 +1,45 @@
+import BasicUserCard from '../basic_user_card/basic_user_card.vue'
+import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
+
+const FollowCard = {
+ props: [
+ 'user',
+ 'noFollowsYou'
+ ],
+ data () {
+ return {
+ inProgress: false,
+ requestSent: false,
+ updated: false
+ }
+ },
+ components: {
+ BasicUserCard
+ },
+ computed: {
+ isMe () { return this.$store.state.users.currentUser.id === this.user.id },
+ following () { return this.updated ? this.updated.following : this.user.following },
+ showFollow () {
+ return !this.following || this.updated && !this.updated.following
+ }
+ },
+ methods: {
+ followUser () {
+ this.inProgress = true
+ requestFollow(this.user, this.$store).then(({ sent, updated }) => {
+ this.inProgress = false
+ this.requestSent = sent
+ this.updated = updated
+ })
+ },
+ unfollowUser () {
+ this.inProgress = true
+ requestUnfollow(this.user, this.$store).then(({ updated }) => {
+ this.inProgress = false
+ this.updated = updated
+ })
+ }
+ }
+}
+
+export default FollowCard
diff --git a/src/components/follow_card/follow_card.vue b/src/components/follow_card/follow_card.vue
new file mode 100644
index 00000000..6cb064eb
--- /dev/null
+++ b/src/components/follow_card/follow_card.vue
@@ -0,0 +1,53 @@
+<template>
+ <basic-user-card :user="user">
+ <div class="follow-card-content-container">
+ <span class="faint" v-if="!noFollowsYou && user.follows_you">
+ {{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }}
+ </span>
+ <button
+ v-if="showFollow"
+ class="btn btn-default"
+ @click="followUser"
+ :disabled="inProgress"
+ :title="requestSent ? $t('user_card.follow_again') : ''"
+ >
+ <template v-if="inProgress">
+ {{ $t('user_card.follow_progress') }}
+ </template>
+ <template v-else-if="requestSent">
+ {{ $t('user_card.follow_sent') }}
+ </template>
+ <template v-else>
+ {{ $t('user_card.follow') }}
+ </template>
+ </button>
+ <button v-if="following" class="btn btn-default pressed" @click="unfollowUser" :disabled="inProgress">
+ <template v-if="inProgress">
+ {{ $t('user_card.follow_progress') }}
+ </template>
+ <template v-else>
+ {{ $t('user_card.follow_unfollow') }}
+ </template>
+ </button>
+ </div>
+ </basic-user-card>
+</template>
+
+<script src="./follow_card.js"></script>
+
+<style lang="scss">
+.follow-card-content-container {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ line-height: 1.5em;
+
+ .btn {
+ margin-top: 0.5em;
+ margin-left: auto;
+ width: 10em;
+ }
+}
+</style>
diff --git a/src/components/follow_list/follow_list.js b/src/components/follow_list/follow_list.js
deleted file mode 100644
index 9777c87e..00000000
--- a/src/components/follow_list/follow_list.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import UserCard from '../user_card/user_card.vue'
-
-const FollowList = {
- data () {
- return {
- loading: false,
- bottomedOut: false,
- error: false
- }
- },
- props: ['userId', 'showFollowers'],
- created () {
- window.addEventListener('scroll', this.scrollLoad)
- if (this.entries.length === 0) {
- this.fetchEntries()
- }
- },
- destroyed () {
- window.removeEventListener('scroll', this.scrollLoad)
- this.$store.dispatch('clearFriendsAndFollowers', this.userId)
- },
- computed: {
- user () {
- return this.$store.getters.userById(this.userId)
- },
- entries () {
- return this.showFollowers ? this.user.followers : this.user.friends
- },
- showFollowsYou () {
- return !this.showFollowers || (this.showFollowers && this.userId !== this.$store.state.users.currentUser.id)
- }
- },
- methods: {
- fetchEntries () {
- if (!this.loading) {
- const command = this.showFollowers ? 'addFollowers' : 'addFriends'
- this.loading = true
- this.$store.dispatch(command, this.userId).then(entries => {
- this.error = false
- this.loading = false
- this.bottomedOut = entries.length === 0
- }).catch(() => {
- this.error = true
- this.loading = false
- })
- }
- },
- scrollLoad (e) {
- const bodyBRect = document.body.getBoundingClientRect()
- const height = Math.max(bodyBRect.height, -(bodyBRect.y))
- if (this.loading === false &&
- this.bottomedOut === false &&
- this.$el.offsetHeight > 0 &&
- (window.innerHeight + window.pageYOffset) >= (height - 750)
- ) {
- this.fetchEntries()
- }
- }
- },
- watch: {
- 'user': 'fetchEntries'
- },
- components: {
- UserCard
- }
-}
-
-export default FollowList
diff --git a/src/components/follow_list/follow_list.vue b/src/components/follow_list/follow_list.vue
deleted file mode 100644
index 27102edf..00000000
--- a/src/components/follow_list/follow_list.vue
+++ /dev/null
@@ -1,33 +0,0 @@
-<template>
- <div class="follow-list">
- <user-card
- v-for="entry in entries"
- :key="entry.id" :user="entry"
- :noFollowsYou="!showFollowsYou"
- />
- <div class="text-center panel-footer">
- <a v-if="error" @click="fetchEntries" class="alert error">
- {{$t('general.generic_error')}}
- </a>
- <i v-else-if="loading" class="icon-spin3 animate-spin"/>
- <span v-else-if="bottomedOut"></span>
- <a v-else @click="fetchEntries">{{$t('general.more')}}</a>
- </div>
- </div>
-</template>
-
-<script src="./follow_list.js"></script>
-
-<style lang="scss">
-
-.follow-list {
- .panel-footer {
- padding: 10px;
- }
-
- .error {
- font-size: 14px;
- }
-}
-
-</style>
diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js
new file mode 100644
index 00000000..1a00a1c1
--- /dev/null
+++ b/src/components/follow_request_card/follow_request_card.js
@@ -0,0 +1,20 @@
+import BasicUserCard from '../basic_user_card/basic_user_card.vue'
+
+const FollowRequestCard = {
+ props: ['user'],
+ components: {
+ BasicUserCard
+ },
+ methods: {
+ approveUser () {
+ this.$store.state.api.backendInteractor.approveUser(this.user.id)
+ this.$store.dispatch('removeFollowRequest', this.user)
+ },
+ denyUser () {
+ this.$store.state.api.backendInteractor.denyUser(this.user.id)
+ this.$store.dispatch('removeFollowRequest', this.user)
+ }
+ }
+}
+
+export default FollowRequestCard
diff --git a/src/components/follow_request_card/follow_request_card.vue b/src/components/follow_request_card/follow_request_card.vue
new file mode 100644
index 00000000..4a3bbba4
--- /dev/null
+++ b/src/components/follow_request_card/follow_request_card.vue
@@ -0,0 +1,29 @@
+<template>
+ <basic-user-card :user="user">
+ <div class="follow-request-card-content-container">
+ <button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button>
+ <button class="btn btn-default" @click="denyUser">{{ $t('user_card.deny') }}</button>
+ </div>
+ </basic-user-card>
+</template>
+
+<script src="./follow_request_card.js"></script>
+
+<style lang="scss">
+.follow-request-card-content-container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ button {
+ margin-top: 0.5em;
+ margin-right: 0.5em;
+ flex: 1 1;
+ max-width: 12em;
+ min-width: 8em;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+}
+</style>
diff --git a/src/components/follow_requests/follow_requests.js b/src/components/follow_requests/follow_requests.js
index 11a228aa..704a76c6 100644
--- a/src/components/follow_requests/follow_requests.js
+++ b/src/components/follow_requests/follow_requests.js
@@ -1,22 +1,13 @@
-import UserCard from '../user_card/user_card.vue'
+import FollowRequestCard from '../follow_request_card/follow_request_card.vue'
const FollowRequests = {
components: {
- UserCard
- },
- created () {
- this.updateRequests()
+ FollowRequestCard
},
computed: {
requests () {
return this.$store.state.api.followRequests
}
- },
- methods: {
- updateRequests () {
- this.$store.state.api.backendInteractor.fetchFollowRequests()
- .then((requests) => { this.$store.commit('setFollowRequests', requests) })
- }
}
}
diff --git a/src/components/follow_requests/follow_requests.vue b/src/components/follow_requests/follow_requests.vue
index 87dc4194..b83c2d68 100644
--- a/src/components/follow_requests/follow_requests.vue
+++ b/src/components/follow_requests/follow_requests.vue
@@ -4,7 +4,7 @@
{{$t('nav.friend_requests')}}
</div>
<div class="panel-body">
- <user-card v-for="request in requests" :key="request.id" :user="request" :showFollows="false" :showApproval="true"></user-card>
+ <FollowRequestCard v-for="request in requests" :key="request.id" :user="request"/>
</div>
</div>
</template>
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index 3f90caa9..ea525c95 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -27,7 +27,6 @@
align-content: stretch;
flex-grow: 1;
margin-top: 0.5em;
- margin-bottom: 0.25em;
.attachments, .attachment {
margin: 0 0.5em 0 0;
@@ -36,6 +35,9 @@
box-sizing: border-box;
// to make failed images a bit more noticeable on chromium
min-width: 2em;
+ &:last-child {
+ margin: 0;
+ }
}
.image-attachment {
diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js
index 990c0370..49d51846 100644
--- a/src/components/image_cropper/image_cropper.js
+++ b/src/components/image_cropper/image_cropper.js
@@ -67,7 +67,7 @@ const ImageCropper = {
submit () {
this.submitting = true
this.avatarUploadError = null
- this.submitHandler(this.cropper, this.filename)
+ this.submitHandler(this.cropper, this.file)
.then(() => this.destroy())
.catch((err) => {
this.submitError = err
@@ -88,14 +88,14 @@ const ImageCropper = {
readFile () {
const fileInput = this.$refs.input
if (fileInput.files != null && fileInput.files[0] != null) {
+ this.file = fileInput.files[0]
let reader = new window.FileReader()
reader.onload = (e) => {
this.dataUrl = e.target.result
this.$emit('open')
}
- reader.readAsDataURL(fileInput.files[0])
- this.filename = fileInput.files[0].name || 'unknown'
- this.$emit('changed', fileInput.files[0], reader)
+ reader.readAsDataURL(this.file)
+ this.$emit('changed', this.file, reader)
}
},
clearError () {
diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue
index e4a247c5..64b1a58b 100644
--- a/src/components/link-preview/link-preview.vue
+++ b/src/components/link-preview/link-preview.vue
@@ -23,10 +23,7 @@
flex-direction: row;
cursor: pointer;
overflow: hidden;
-
- // TODO: clean up the random margins in attachments, this makes preview line
- // up with attachments...
- margin-right: 0.5em;
+ margin-top: 0.5em;
.card-image {
flex-shrink: 0;
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index ea5d7ea4..aa3f7605 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -1,10 +1,23 @@
+import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
+
const NavPanel = {
+ created () {
+ if (this.currentUser && this.currentUser.locked) {
+ const store = this.$store
+ const credentials = store.state.users.currentUser.credentials
+
+ followRequestFetcher.startFetching({ store, credentials })
+ }
+ },
computed: {
currentUser () {
return this.$store.state.users.currentUser
},
chat () {
return this.$store.state.chat.channel
+ },
+ followRequestCount () {
+ return this.$store.state.api.followRequests.length
}
}
}
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 1a269adf..7a7212fb 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -20,8 +20,8 @@
<li v-if='currentUser && currentUser.locked'>
<router-link :to="{ name: 'friend-requests' }">
{{ $t("nav.friend_requests")}}
- <span v-if='currentUser.follow_request_count > 0' class="badge follow-request-count">
- {{currentUser.follow_request_count}}
+ <span v-if='followRequestCount > 0' class="badge follow-request-count">
+ {{followRequestCount}}
</span>
</router-link>
</li>
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index a0a55cba..87925cfc 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -25,7 +25,11 @@
<small>{{$t('notifications.followed_you')}}</small>
</span>
</div>
- <small class="timeago"><router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }"><timeago :since="notification.action.created_at" :auto-update="240"></timeago></router-link></small>
+ <div class="timeago">
+ <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" class="faint-link">
+ <timeago :since="notification.action.created_at" :auto-update="240"></timeago>
+ </router-link>
+ </div>
</span>
<div class="follow-text" v-if="notification.type === 'follow'">
<router-link :to="userProfileLink(notification.action.user)">
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index b3364afc..2240c10a 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -126,7 +126,7 @@
}
.timeago {
- font-size: 12px;
+ margin-right: .2em;
}
.icon-retweet.lit {
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index c28c51bf..23a2c7e2 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: {
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 5085570b..0ddde4ea 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -30,15 +30,17 @@
@drop="fileDrop"
@dragover.prevent="fileDrag"
@input="resize"
- @paste="paste">
+ @paste="paste"
+ :disabled="posting"
+ >
</textarea>
<div class="visibility-tray">
<span class="text-format" v-if="formattingOptionsEnabled">
<label for="post-content-type" class="select">
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
- <option 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 16814f65..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"/>
@@ -150,7 +142,7 @@
<label for="preloadImage">{{$t('settings.preload_images')}}</label>
</li>
<li>
- <input type="checkbox" id="useOneClickNsfw" v-model="useOneClickNsfw">
+ <input :disabled="!hideNsfwLocal" type="checkbox" id="useOneClickNsfw" v-model="useOneClickNsfw">
<label for="useOneClickNsfw">{{$t('settings.use_one_click_nsfw')}}</label>
</li>
</ul>
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index 40ffa1dd..b5c49059 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -32,6 +32,9 @@ const SideDrawer = {
},
sitename () {
return this.$store.state.instance.name
+ },
+ followRequestCount () {
+ return this.$store.state.api.followRequests.length
}
},
methods: {
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 8eca7b8c..6996380d 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -45,8 +45,8 @@
<li v-if="currentUser && currentUser.locked" @click="toggleDrawer">
<router-link to='/friend-requests'>
{{ $t("nav.friend_requests") }}
- <span v-if='currentUser.follow_request_count > 0' class="badge follow-request-count">
- {{currentUser.follow_request_count}}
+ <span v-if='followRequestCount > 0' class="badge follow-request-count">
+ {{followRequestCount}}
</span>
</router-link>
diff --git a/src/components/status/status.js b/src/components/status/status.js
index fab2fe62..fbbca6c4 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -23,7 +23,7 @@ const Status = {
'highlight',
'compact',
'replies',
- 'noReplyLinks',
+ 'isPreview',
'noHeading',
'inlineExpanded'
],
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 3fc5b486..4dd20362 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,6 +1,6 @@
<template>
<div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
- <template v-if="muted && !noReplyLinks">
+ <template v-if="muted && !isPreview">
<div class="media status container muted">
<small>
<router-link :to="userProfileLink">
@@ -13,7 +13,7 @@
</template>
<template v-else>
<div v-if="retweet && !noHeading" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
- <UserAvatar v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/>
+ <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/>
<div class="media-body faint">
<span class="user-name">
<router-link v-if="retweeterHtml" :to="retweeterProfileLink" v-html="retweeterHtml"/>
@@ -31,57 +31,69 @@
</router-link>
</div>
<div class="status-body">
- <div class="usercard media-body" v-if="userExpanded">
+ <div class="usercard" v-if="userExpanded">
<user-card-content :user="status.user" :switcher="false"></user-card-content>
</div>
- <div v-if="!noHeading" class="media-body container media-heading">
- <div class="media-heading-left">
- <div class="name-and-links">
+ <div v-if="!noHeading" class="media-heading">
+ <div class="heading-name-row">
+ <div class="name-and-account-name">
<h4 class="user-name" v-if="status.user.name_html" v-html="status.user.name_html"></h4>
<h4 class="user-name" v-else>{{status.user.name}}</h4>
- <span class="links">
- <router-link :to="userProfileLink">
- {{status.user.screen_name}}
- </router-link>
- <span v-if="isReply" class="faint reply-info">
- <i class="icon-right-open"></i>
- <router-link :to="replyProfileLink">
- {{replyToName}}
- </router-link>
- </span>
- <a v-if="isReply && !noReplyLinks" href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)" :aria-label="$t('tool_tip.reply')">
- <i class="button-icon icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i>
+ <router-link class="account-name" :to="userProfileLink">
+ {{status.user.screen_name}}
+ </router-link>
+ </div>
+
+ <span class="heading-right">
+ <router-link class="timeago faint-link" :to="{ name: 'conversation', params: { id: status.id } }">
+ <timeago :since="status.created_at" :auto-update="60"></timeago>
+ </router-link>
+ <div class="button-icon visibility-icon" v-if="status.visibility">
+ <i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i>
+ </div>
+ <a :href="status.external_url" target="_blank" v-if="!status.is_local && !isPreview" class="source_url" title="Source">
+ <i class="button-icon icon-link-ext-alt"></i>
+ </a>
+ <template v-if="expandable && !isPreview">
+ <a href="#" @click.prevent="toggleExpanded" title="Expand">
+ <i class="button-icon icon-plus-squared"></i>
</a>
+ </template>
+ <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="button-icon icon-eye-off"></i></a>
+ </span>
+ </div>
+
+ <div class="heading-reply-row">
+ <div v-if="isReply" class="reply-to-and-accountname">
+ <a class="reply-to"
+ href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"
+ :aria-label="$t('tool_tip.reply')"
+ @mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)"
+ @mouseleave.prevent.stop="replyLeave()"
+ >
+ <i class="button-icon icon-reply" v-if="!isPreview"></i>
+ <span class="faint-link reply-to-text">{{$t('status.reply_to')}}</span>
+ </a>
+ <router-link :to="replyProfileLink">
+ {{replyToName}}
+ </router-link>
+ <span class="faint replies-separator" v-if="replies && replies.length">
+ -
</span>
</div>
- <h4 class="replies" v-if="inConversation && !noReplyLinks">
- <small v-if="replies.length">Replies:</small>
- <small class="reply-link" v-bind:key="reply.id" v-for="reply in replies">
- <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}&nbsp;</a>
- </small>
- </h4>
- </div>
- <div class="media-heading-right">
- <router-link class="timeago" :to="{ name: 'conversation', params: { id: status.id } }">
- <timeago :since="status.created_at" :auto-update="60"></timeago>
- </router-link>
- <div class="button-icon visibility-icon" v-if="status.visibility">
- <i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i>
+ <div class="replies" v-if="inConversation && !isPreview">
+ <span class="faint" v-if="replies && replies.length">{{$t('status.replies_list')}}</span>
+ <span class="reply-link faint" v-if="replies" v-for="reply in replies">
+ <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}</a>
+ </span>
</div>
- <a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url" title="Source">
- <i class="button-icon icon-link-ext-alt"></i>
- </a>
- <template v-if="expandable">
- <a href="#" @click.prevent="toggleExpanded" title="Expand">
- <i class="button-icon icon-plus-squared"></i>
- </a>
- </template>
- <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="button-icon icon-eye-off"></i></a>
</div>
+
+
</div>
<div v-if="showPreview" class="status-preview-container">
- <status class="status-preview" v-if="preview" :noReplyLinks="true" :statusoid="preview" :compact=true></status>
+ <status class="status-preview" v-if="preview" :isPreview="true" :statusoid="preview" :compact=true></status>
<div class="status-preview status-preview-loading" v-else>
<i class="icon-spin4 animate-spin"></i>
</div>
@@ -123,7 +135,7 @@
<link-preview :card="status.card" :size="attachmentSize" :nsfw="nsfwClickthrough" />
</div>
- <div v-if="!noHeading && !noReplyLinks" class='status-actions media-body'>
+ <div v-if="!noHeading && !isPreview" class='status-actions media-body'>
<div v-if="loggedIn">
<a href="#" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')">
<i class="button-icon icon-reply" :class="{'icon-reply-active': replying}"></i>
@@ -147,6 +159,8 @@
<style lang="scss">
@import '../../_variables.scss';
+$status-margin: 0.75em;
+
.status-body {
flex: 1;
min-width: 0;
@@ -202,13 +216,16 @@
}
}
+.media-left {
+ margin-right: $status-margin;
+}
+
.status-el {
hyphens: auto;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
border-left-width: 0px;
- line-height: 18px;
min-width: 0;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
@@ -229,22 +246,34 @@
.media-body {
flex: 1;
padding: 0;
- margin: 0 0 0.25em 0.8em;
}
.usercard {
- margin-bottom: .7em
+ margin: 0;
+ margin-bottom: $status-margin;
}
- .media-heading {
- flex-wrap: nowrap;
- line-height: 18px;
+ .user-name {
+ white-space: nowrap;
+ font-size: 14px;
+ overflow: hidden;
+ flex-shrink: 0;
+ max-width: 85%;
+ font-weight: bold;
+
+ img {
+ width: 14px;
+ height: 14px;
+ vertical-align: middle;
+ object-fit: contain
+ }
}
- .media-heading-left {
+ .media-heading {
padding: 0;
vertical-align: bottom;
flex-basis: 100%;
+ margin-bottom: 0.5em;
a {
display: inline-block;
@@ -254,83 +283,102 @@
small {
font-weight: lighter;
}
- h4 {
- white-space: nowrap;
- font-size: 14px;
- margin-right: 0.25em;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .name-and-links {
+
+ .heading-name-row {
padding: 0;
- flex: 1 0;
display: flex;
- flex-wrap: wrap;
- align-items: baseline;
+ justify-content: space-between;
+ line-height: 18px;
+
+ .name-and-account-name {
+ display: flex;
+ min-width: 0;
+ }
.user-name {
- margin-right: .45em;
+ flex-shrink: 1;
+ margin-right: 0.4em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
- img {
- width: 14px;
- height: 14px;
- vertical-align: middle;
- object-fit: contain
- }
+ .account-name {
+ min-width: 1.6em;
+ margin-right: 0.4em;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 1 1 0;
}
}
- .links {
+ .heading-right {
display: flex;
+ flex-shrink: 0;
+ }
+
+ .timeago {
+ margin-right: 0.2em;
+ }
+
+ .heading-reply-row {
+ align-content: baseline;
font-size: 12px;
- color: $fallback--link;
- color: var(--link, $fallback--link);
+ line-height: 18px;
max-width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: stretch;
+
a {
max-width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
- & > span {
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- }
- & > a:last-child {
- flex-shrink: 0;
+ }
+
+ .reply-to-and-accountname {
+ display: flex;
+ height: 18px;
+ margin-right: 0.5em;
+ overflow: hidden;
+ max-width: 100%;
+ .icon-reply {
+ transform: scaleX(-1);
}
}
+
.reply-info {
display: flex;
}
- .replies {
- line-height: 16px;
+
+ .reply-to {
+ display: flex;
}
- .reply-link {
- margin-right: 0.2em;
+
+ .reply-to-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin: 0 0.4em 0 0.2em;
}
- }
- .media-heading-right {
- display: inline-flex;
- flex-shrink: 0;
- flex-wrap: nowrap;
- margin-left: .25em;
- align-self: baseline;
+ .replies-separator {
+ margin-left: 0.4em;
+ }
- .timeago {
- margin-right: 0.2em;
+ .replies {
+ line-height: 18px;
font-size: 12px;
- align-self: last baseline;
+ display: flex;
+ flex-wrap: wrap;
+ & > * {
+ margin-right: 0.4em;
+ }
}
- > * {
- margin-left: 0.2em;
- }
- a:hover i {
- color: $fallback--text;
- color: var(--text, $fallback--text);
+ .reply-link {
+ height: 17px;
}
}
@@ -366,14 +414,19 @@
}
.status-content {
- margin-right: 0.5em;
font-family: var(--postFont, sans-serif);
+ line-height: 1.4em;
img, video {
max-width: 100%;
max-height: 400px;
vertical-align: middle;
object-fit: contain;
+
+ &.emoji {
+ width: 32px;
+ height: 32px;
+ }
}
blockquote {
@@ -390,9 +443,11 @@
}
p {
- margin: 0;
- margin-top: 0.2em;
- margin-bottom: 0.5em;
+ margin: 0 0 1em 0;
+ }
+
+ p:last-child {
+ margin: 0 0 0 0;
}
h1 {
@@ -417,7 +472,7 @@
}
.retweet-info {
- padding: 0.4em 0.6em 0 0.6em;
+ padding: 0.4em $status-margin;
margin: 0;
.avatar.still-image {
@@ -488,10 +543,10 @@
.status-actions {
width: 100%;
display: flex;
+ margin-top: $status-margin;
div, favorite-button {
- padding-top: 0.25em;
- max-width: 6em;
+ max-width: 4em;
flex: 1;
}
}
@@ -517,9 +572,9 @@
.status {
display: flex;
- padding: 0.6em;
+ padding: $status-margin;
&.is-retweet {
- padding-top: 0.1em;
+ padding-top: 0;
}
}
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 62536bc5..655bfb3f 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -1,7 +1,6 @@
import Status from '../status/status.vue'
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
import StatusOrConversation from '../status_or_conversation/status_or_conversation.vue'
-import UserCard from '../user_card/user_card.vue'
import { throttle } from 'lodash'
const Timeline = {
@@ -44,8 +43,7 @@ const Timeline = {
},
components: {
Status,
- StatusOrConversation,
- UserCard
+ StatusOrConversation
},
created () {
const store = this.$store
@@ -108,7 +106,7 @@ const Timeline = {
tag: this.tag
}).then(statuses => {
store.commit('setLoading', { timeline: this.timelineName, value: false })
- if (statuses.length === 0) {
+ if (statuses && statuses.length === 0) {
this.bottomedOut = true
}
})
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
deleted file mode 100644
index 28e22f09..00000000
--- a/src/components/user_card/user_card.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import UserCardContent from '../user_card_content/user_card_content.vue'
-import UserAvatar from '../user_avatar/user_avatar.vue'
-import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
-import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
-
-const UserCard = {
- props: [
- 'user',
- 'noFollowsYou',
- 'showApproval'
- ],
- data () {
- return {
- userExpanded: false,
- followRequestInProgress: false,
- followRequestSent: false,
- updated: false
- }
- },
- components: {
- UserCardContent,
- UserAvatar
- },
- computed: {
- currentUser () { return this.$store.state.users.currentUser },
- following () { return this.updated ? this.updated.following : this.user.following },
- showFollow () {
- return !this.showApproval && (!this.following || this.updated && !this.updated.following)
- }
- },
- methods: {
- toggleUserExpanded () {
- this.userExpanded = !this.userExpanded
- },
- approveUser () {
- this.$store.state.api.backendInteractor.approveUser(this.user.id)
- this.$store.dispatch('removeFollowRequest', this.user)
- },
- denyUser () {
- this.$store.state.api.backendInteractor.denyUser(this.user.id)
- this.$store.dispatch('removeFollowRequest', this.user)
- },
- userProfileLink (user) {
- return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
- },
- followUser () {
- this.followRequestInProgress = true
- requestFollow(this.user, this.$store).then(({ sent, updated }) => {
- this.followRequestInProgress = false
- this.followRequestSent = sent
- this.updated = updated
- })
- },
- unfollowUser () {
- this.followRequestInProgress = true
- requestUnfollow(this.user, this.$store).then(({ updated }) => {
- this.followRequestInProgress = false
- this.updated = updated
- })
- }
- }
-}
-
-export default UserCard
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
deleted file mode 100644
index ce4edb3c..00000000
--- a/src/components/user_card/user_card.vue
+++ /dev/null
@@ -1,159 +0,0 @@
-<template>
- <div class="card">
- <router-link :to="userProfileLink(user)">
- <UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
- </router-link>
- <div class="user-card-main-content">
- <div class="usercard" v-if="userExpanded">
- <user-card-content :user="user" :switcher="false"></user-card-content>
- </div>
- <div class="name-and-screen-name" v-if="!userExpanded">
- <div :title="user.name" class="user-name">
- <span v-if="user.name_html" v-html="user.name_html"></span>
- <span v-else>{{ user.name }}</span>
- </div>
- <div class="user-link-action">
- <router-link class='user-screen-name' :to="userProfileLink(user)">
- @{{user.screen_name}}
- </router-link>
- </div>
- </div>
- <div class="follow-box" v-if="!userExpanded">
- <span class="faint" v-if="!noFollowsYou && user.follows_you">
- {{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }}
- </span>
- <button
- v-if="showFollow"
- class="btn btn-default"
- @click="followUser"
- :disabled="followRequestInProgress"
- :title="followRequestSent ? $t('user_card.follow_again') : ''"
- >
- <template v-if="followRequestInProgress">
- {{ $t('user_card.follow_progress') }}
- </template>
- <template v-else-if="followRequestSent">
- {{ $t('user_card.follow_sent') }}
- </template>
- <template v-else>
- {{ $t('user_card.follow') }}
- </template>
- </button>
- <button v-if="following" class="btn btn-default pressed" @click="unfollowUser" :disabled="followRequestInProgress">
- <template v-if="followRequestInProgress">
- {{ $t('user_card.follow_progress') }}
- </template>
- <template v-else>
- {{ $t('user_card.follow_unfollow') }}
- </template>
- </button>
- </div>
- <div class="approval" v-if="showApproval">
- <button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button>
- <button class="btn btn-default" @click="denyUser">{{ $t('user_card.deny') }}</button>
- </div>
- </div>
- </div>
-</template>
-
-<script src="./user_card.js"></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.user-card-main-content {
- display: flex;
- flex-direction: column;
- flex: 1 1 100%;
- margin-left: 0.7em;
- min-width: 0;
-}
-
-.name-and-screen-name {
- text-align: left;
- width: 100%;
-
- .user-name {
- img {
- object-fit: contain;
- height: 16px;
- width: 16px;
- vertical-align: middle;
- }
- }
-
- .user-link-action {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- }
-}
-
-
-.card {
- display: flex;
- flex: 1 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);
-
- .avatar {
- padding: 0;
- }
-
- .follow-box {
- text-align: center;
- flex-shrink: 0;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- flex-wrap: wrap;
- line-height: 1.5em;
-
- .btn {
- margin-top: 0.5em;
- margin-left: auto;
- width: 10em;
- }
- }
-}
-
-.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;
- }
-}
-
-.approval {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- button {
- margin-top: 0.5em;
- margin-right: 0.5em;
- flex: 1 1;
- max-width: 12em;
- min-width: 8em;
- }
-}
-</style>
diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js
index 7a7b89d4..35139504 100644
--- a/src/components/user_card_content/user_card_content.js
+++ b/src/components/user_card_content/user_card_content.js
@@ -93,22 +93,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_content/user_card_content.vue
index a3d24eb1..702c3385 100644
--- a/src/components/user_card_content/user_card_content.vue
+++ b/src/components/user_card_content/user_card_content.vue
@@ -222,6 +222,14 @@
overflow: hidden;
flex: 1 1 auto;
margin-right: 1em;
+ font-size: 15px;
+
+ img {
+ object-fit: contain;
+ height: 16px;
+ width: 16px;
+ vertical-align: middle;
+ }
}
.user-screen-name {
@@ -386,4 +394,24 @@
}
}
+.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_profile/user_profile.js b/src/components/user_profile/user_profile.js
index 37179ce1..cdf1cee9 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -1,9 +1,39 @@
+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 FollowList from '../follow_list/follow_list.vue'
+import withLoadMore from '../../hocs/with_load_more/with_load_more'
+import withList from '../../hocs/with_list/with_list'
+
+const FollowerList = compose(
+ withLoadMore({
+ fetch: (props, $store) => $store.dispatch('addFollowers', props.userId),
+ select: (props, $store) => get($store.getters.userById(props.userId), 'followers', []),
+ destory: (props, $store) => $store.dispatch('clearFollowers', props.userId),
+ childPropName: 'entries',
+ additionalPropNames: ['userId']
+ }),
+ withList({ getEntryProps: user => ({ user }) })
+)(FollowCard)
+
+const FriendList = compose(
+ withLoadMore({
+ fetch: (props, $store) => $store.dispatch('addFriends', props.userId),
+ select: (props, $store) => get($store.getters.userById(props.userId), 'friends', []),
+ destory: (props, $store) => $store.dispatch('clearFriends', props.userId),
+ childPropName: 'entries',
+ additionalPropNames: ['userId']
+ }),
+ withList({ getEntryProps: user => ({ user }) })
+)(FollowCard)
const UserProfile = {
+ data () {
+ return {
+ error: false
+ }
+ },
created () {
this.$store.commit('clearTimeline', { timeline: 'user' })
this.$store.commit('clearTimeline', { timeline: 'favorites' })
@@ -13,6 +43,16 @@ const UserProfile = {
this.startFetchFavorites()
if (!this.user.id) {
this.$store.dispatch('fetchUser', this.fetchBy)
+ .catch((reason) => {
+ const errorMessage = get(reason, 'error.error')
+ if (errorMessage === 'No user with such user_id') { // Known error
+ this.error = this.$t('user_profile.profile_does_not_exist')
+ } else if (errorMessage) {
+ this.error = errorMessage
+ } else {
+ this.error = this.$t('user_profile.profile_loading_error')
+ }
+ })
}
},
destroyed () {
@@ -101,13 +141,16 @@ const UserProfile = {
}
this.cleanUp()
this.startUp()
+ },
+ $route () {
+ this.$refs.tabSwitcher.activateTab(0)()
}
},
components: {
UserCardContent,
- UserCard,
Timeline,
- FollowList
+ FollowerList,
+ FriendList
}
}
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 09fb93de..8090efa5 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -6,7 +6,7 @@
:switcher="true"
:selected="timeline.viewing"
/>
- <tab-switcher :renderOnlyFocused="true">
+ <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
<Timeline
:label="$t('user_card.statuses')"
:disabled="!user.statuses_count"
@@ -18,16 +18,10 @@
:user-id="fetchBy"
/>
<div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count">
- <FollowList v-if="user.friends_count > 0" :userId="userId" :showFollowers="false" />
- <div class="userlist-placeholder" v-else>
- <i class="icon-spin3 animate-spin"></i>
- </div>
+ <FriendList :userId="userId" />
</div>
<div :label="$t('user_card.followers')" v-if="followersTabVisible" :disabled="!user.followers_count">
- <FollowList v-if="user.followers_count > 0" :userId="userId" :showFollowers="true" />
- <div class="userlist-placeholder" v-else>
- <i class="icon-spin3 animate-spin"></i>
- </div>
+ <FollowerList :userId="userId" :entryProps="{noFollowsYou: isUs}" />
</div>
<Timeline
:label="$t('user_card.media')"
@@ -55,7 +49,8 @@
</div>
</div>
<div class="panel-body">
- <i class="icon-spin3 animate-spin"></i>
+ <span v-if="error">{{ error }}</span>
+ <i class="icon-spin3 animate-spin" v-else></i>
</div>
</div>
</div>
diff --git a/src/components/user_search/user_search.js b/src/components/user_search/user_search.js
index fe67b2ad..55040826 100644
--- a/src/components/user_search/user_search.js
+++ b/src/components/user_search/user_search.js
@@ -1,8 +1,8 @@
-import UserCard from '../user_card/user_card.vue'
+import FollowCard from '../follow_card/follow_card.vue'
import userSearchApi from '../../services/new_api/user_search.js'
const userSearch = {
components: {
- UserCard
+ FollowCard
},
props: [
'query'
diff --git a/src/components/user_search/user_search.vue b/src/components/user_search/user_search.vue
index b39e10f4..1269eea6 100644
--- a/src/components/user_search/user_search.vue
+++ b/src/components/user_search/user_search.vue
@@ -13,7 +13,7 @@
<i class="icon-spin3 animate-spin"/>
</div>
<div v-else class="panel-body">
- <user-card v-for="user in users" :key="user.id" :user="user" :showFollows="true"></user-card>
+ <FollowCard v-for="user in users" :key="user.id" :user="user"/>
</div>
</div>
</template>
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index d6972737..c0ab759c 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -157,8 +157,8 @@ const UserSettings = {
}
reader.readAsDataURL(file)
},
- submitAvatar (cropper) {
- const img = cropper.getCroppedCanvas().toDataURL('image/jpeg')
+ submitAvatar (cropper, file) {
+ const img = cropper.getCroppedCanvas().toDataURL(file.type)
return this.$store.state.api.backendInteractor.updateAvatar({ params: { img } }).then((user) => {
if (!user.error) {
this.$store.commit('addNewUsers', [user])
diff --git a/src/components/who_to_follow/who_to_follow.js b/src/components/who_to_follow/who_to_follow.js
index 82098fc2..be0b8827 100644
--- a/src/components/who_to_follow/who_to_follow.js
+++ b/src/components/who_to_follow/who_to_follow.js
@@ -1,9 +1,9 @@
import apiService from '../../services/api/api.service.js'
-import UserCard from '../user_card/user_card.vue'
+import FollowCard from '../follow_card/follow_card.vue'
const WhoToFollow = {
components: {
- UserCard
+ FollowCard
},
data () {
return {
diff --git a/src/components/who_to_follow/who_to_follow.vue b/src/components/who_to_follow/who_to_follow.vue
index df2e03c8..1630f5ac 100644
--- a/src/components/who_to_follow/who_to_follow.vue
+++ b/src/components/who_to_follow/who_to_follow.vue
@@ -4,7 +4,7 @@
{{$t('who_to_follow.who_to_follow')}}
</div>
<div class="panel-body">
- <user-card v-for="user in users" :key="user.id" :user="user" :showFollows="true"></user-card>
+ <FollowCard v-for="user in users" :key="user.id" :user="user"/>
</div>
</div>
</template>