aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/conversation/conversation.js3
-rw-r--r--src/components/conversation/conversation.vue1
-rw-r--r--src/components/delete_button/delete_button.js21
-rw-r--r--src/components/delete_button/delete_button.vue21
-rw-r--r--src/components/extra_buttons/extra_buttons.js64
-rw-r--r--src/components/extra_buttons/extra_buttons.vue47
-rw-r--r--src/components/moderation_tools/moderation_tools.vue8
-rw-r--r--src/components/post_status_form/post_status_form.vue7
-rw-r--r--src/components/scope_selector/scope_selector.vue18
-rw-r--r--src/components/status/status.js17
-rw-r--r--src/components/status/status.vue41
-rw-r--r--src/components/user_card/user_card.vue41
-rw-r--r--src/components/user_profile/user_profile.js6
-rw-r--r--src/components/user_profile/user_profile.vue32
-rw-r--r--src/components/user_settings/user_settings.vue4
15 files changed, 235 insertions, 96 deletions
diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index ffeb7244..b3074590 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -41,7 +41,8 @@ const conversation = {
props: [
'statusoid',
'collapsable',
- 'isPage'
+ 'isPage',
+ 'showPinned'
],
created () {
if (this.isPage) {
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index d04ff722..0b4998c3 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -14,6 +14,7 @@
:inlineExpanded="collapsable && isExpanded"
:statusoid="status"
:expandable='!isExpanded'
+ :showPinned="showPinned"
:focused="focused(status.id)"
:inConversation="isExpanded"
:highlight="getHighlight()"
diff --git a/src/components/delete_button/delete_button.js b/src/components/delete_button/delete_button.js
deleted file mode 100644
index 22f24625..00000000
--- a/src/components/delete_button/delete_button.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const DeleteButton = {
- props: [ 'status' ],
- methods: {
- deleteStatus () {
- const confirmed = window.confirm('Do you really want to delete this status?')
- if (confirmed) {
- this.$store.dispatch('deleteStatus', { id: this.status.id })
- }
- }
- },
- computed: {
- currentUser () { return this.$store.state.users.currentUser },
- canDelete () {
- if (!this.currentUser) { return }
- const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
- return superuser || this.status.user.id === this.currentUser.id
- }
- }
-}
-
-export default DeleteButton
diff --git a/src/components/delete_button/delete_button.vue b/src/components/delete_button/delete_button.vue
deleted file mode 100644
index f4c91cfd..00000000
--- a/src/components/delete_button/delete_button.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-<template>
- <div v-if="canDelete">
- <a href="#" v-on:click.prevent="deleteStatus()">
- <i class='button-icon icon-cancel delete-status'></i>
- </a>
- </div>
-</template>
-
-<script src="./delete_button.js" ></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.icon-cancel,.delete-status {
- cursor: pointer;
- &:hover {
- color: $fallback--cRed;
- color: var(--cRed, $fallback--cRed);
- }
-}
-</style>
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
new file mode 100644
index 00000000..528da301
--- /dev/null
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -0,0 +1,64 @@
+import Popper from 'vue-popperjs/src/component/popper.js.vue'
+
+const ExtraButtons = {
+ props: [ 'status' ],
+ components: {
+ Popper
+ },
+ data () {
+ return {
+ showDropDown: false,
+ showPopper: true
+ }
+ },
+ methods: {
+ deleteStatus () {
+ this.refreshPopper()
+ const confirmed = window.confirm(this.$t('status.delete_confirm'))
+ if (confirmed) {
+ this.$store.dispatch('deleteStatus', { id: this.status.id })
+ }
+ },
+ toggleMenu () {
+ this.showDropDown = !this.showDropDown
+ },
+ pinStatus () {
+ this.refreshPopper()
+ this.$store.dispatch('pinStatus', this.status.id)
+ .then(() => this.$emit('onSuccess'))
+ .catch(err => this.$emit('onError', err.error.error))
+ },
+ unpinStatus () {
+ this.refreshPopper()
+ this.$store.dispatch('unpinStatus', this.status.id)
+ .then(() => this.$emit('onSuccess'))
+ .catch(err => this.$emit('onError', err.error.error))
+ },
+ refreshPopper () {
+ this.showPopper = false
+ this.showDropDown = false
+ setTimeout(() => {
+ this.showPopper = true
+ })
+ }
+ },
+ computed: {
+ currentUser () { return this.$store.state.users.currentUser },
+ canDelete () {
+ if (!this.currentUser) { return }
+ const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
+ return superuser || this.status.user.id === this.currentUser.id
+ },
+ ownStatus () {
+ return this.status.user.id === this.currentUser.id
+ },
+ canPin () {
+ return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted')
+ },
+ enabled () {
+ return this.canPin || this.canDelete
+ }
+ }
+}
+
+export default ExtraButtons
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
new file mode 100644
index 00000000..ef11138d
--- /dev/null
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -0,0 +1,47 @@
+<template>
+ <Popper
+ trigger="click"
+ @hide='showDropDown = false'
+ append-to-body
+ v-if="enabled && showPopper"
+ :options="{
+ placement: 'top',
+ modifiers: {
+ arrow: { enabled: true },
+ offset: { offset: '0, 5px' },
+ }
+ }"
+ >
+ <div class="popper-wrapper">
+ <div class="dropdown-menu">
+ <button class="dropdown-item dropdown-item-icon" @click.prevent="pinStatus" v-if="!status.pinned && canPin">
+ <i class="icon-pin"></i><span>{{$t("status.pin")}}</span>
+ </button>
+ <button class="dropdown-item dropdown-item-icon" @click.prevent="unpinStatus" v-if="status.pinned && canPin">
+ <i class="icon-pin"></i><span>{{$t("status.unpin")}}</span>
+ </button>
+ <button class="dropdown-item dropdown-item-icon" @click.prevent="deleteStatus" v-if="canDelete">
+ <i class="icon-cancel"></i><span>{{$t("status.delete")}}</span>
+ </button>
+ </div>
+ </div>
+ <div class="button-icon" slot="reference" @click="toggleMenu">
+ <i class='icon-ellipsis' :class="{'icon-clicked': showDropDown}"></i>
+ </div>
+ </Popper>
+</template>
+
+<script src="./extra_buttons.js" ></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.icon-ellipsis {
+ cursor: pointer;
+
+ &:hover, &.icon-clicked {
+ color: $fallback--text;
+ color: var(--text, $fallback--text);
+ }
+}
+</style>
diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue
index c24a2280..c9e3fc78 100644
--- a/src/components/moderation_tools/moderation_tools.vue
+++ b/src/components/moderation_tools/moderation_tools.vue
@@ -127,6 +127,14 @@
width: 100%;
height: 100%;
+ &-icon {
+ padding-left: 0.5rem;
+
+ i {
+ margin-right: 0.25rem;
+ }
+ }
+
&:hover {
// TODO: improve the look on breeze themes
background-color: $fallback--fg;
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 4f014cc5..25c5284f 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -58,7 +58,7 @@
>
</textarea>
<div class="visibility-tray">
- <span class="text-format" v-if="formattingOptionsEnabled">
+ <div class="text-format" v-if="formattingOptionsEnabled">
<label for="post-content-type" class="select">
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
<option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat">
@@ -67,7 +67,7 @@
</select>
<i class="icon-down-open"></i>
</label>
- </span>
+ </div>
<scope-selector
:showAll="showAllScopes"
@@ -152,6 +152,7 @@
display: flex;
justify-content: space-between;
flex-direction: row-reverse;
+ padding-top: 5px;
}
}
@@ -250,7 +251,7 @@
.form-group {
display: flex;
flex-direction: column;
- padding: 0.3em 0.5em 0.6em;
+ padding: 0.25em 0.5em 0.5em;
line-height:24px;
}
diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue
index 33ea488f..5ebb5d56 100644
--- a/src/components/scope_selector/scope_selector.vue
+++ b/src/components/scope_selector/scope_selector.vue
@@ -1,5 +1,5 @@
<template>
-<div v-if="!showNothing">
+<div v-if="!showNothing" class="scope-selector">
<i class="icon-mail-alt"
:class="css.direct"
:title="$t('post_status.scope.direct')"
@@ -28,3 +28,19 @@
</template>
<script src="./scope_selector.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.scope-selector {
+ i {
+ font-size: 1.2em;
+ cursor: pointer;
+
+ &.selected {
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ }
+ }
+}
+</style>
diff --git a/src/components/status/status.js b/src/components/status/status.js
index c01cfe79..5b3d98c3 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -1,7 +1,7 @@
import Attachment from '../attachment/attachment.vue'
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 ExtraButtons from '../extra_buttons/extra_buttons.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue'
import UserCard from '../user_card/user_card.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
@@ -26,7 +26,8 @@ const Status = {
'replies',
'isPreview',
'noHeading',
- 'inlineExpanded'
+ 'inlineExpanded',
+ 'showPinned'
],
data () {
return {
@@ -37,6 +38,7 @@ const Status = {
showPreview: false,
showingTall: this.inConversation && this.focused,
showingLongSubject: false,
+ error: null,
expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
? !this.$store.state.instance.collapseMessageWithSubject
: !this.$store.state.config.collapseMessageWithSubject,
@@ -269,13 +271,16 @@ const Status = {
this.statusFromGlobalRepository.rebloggedBy
)
return uniqBy(combinedUsers, 'id')
+ },
+ ownStatus () {
+ return this.status.user.id === this.$store.state.users.currentUser.id
}
},
components: {
Attachment,
FavoriteButton,
RetweetButton,
- DeleteButton,
+ ExtraButtons,
PostStatusForm,
UserCard,
UserAvatar,
@@ -296,6 +301,12 @@ const Status = {
return 'icon-globe'
}
},
+ showError (error) {
+ this.error = error
+ },
+ clearError () {
+ this.error = undefined
+ },
linkClicked (event) {
let { target } = event
if (target.tagName === 'SPAN') {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index e8c1a874..aea5b78b 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,5 +1,9 @@
<template>
<div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
+ <div v-if="error" class="alert error">
+ {{error}}
+ <i class="button-icon icon-cancel" @click="clearError"></i>
+ </div>
<template v-if="muted && !isPreview">
<div class="media status container muted">
<small>
@@ -12,6 +16,10 @@
</div>
</template>
<template v-else>
+ <div v-if="showPinned && statusoid.pinned" class="status-pin">
+ <i class="fa icon-pin faint"></i>
+ <span class="faint">{{$t('status.pinned')}}</span>
+ </div>
<div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
<UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :user="statusoid.user"/>
<div class="media-body faint">
@@ -95,7 +103,7 @@
v-if="preview"
:isPreview="true"
:statusoid="preview"
- :compact=true
+ :compact="true"
/>
<div v-else class="status-preview status-preview-loading">
<i class="icon-spin4 animate-spin"></i>
@@ -157,13 +165,14 @@
</transition>
<div v-if="!noHeading && !isPreview" class='status-actions media-body'>
- <div v-if="loggedIn">
- <i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'icon-reply-active': replying}"></i>
+ <div>
+ <i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'button-icon-active': replying}" v-if="loggedIn"/>
+ <i class="button-icon button-icon-disabled icon-reply" :title="$t('tool_tip.reply')" v-else />
<span v-if="status.replies_count > 0">{{status.replies_count}}</span>
</div>
<retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button>
<favorite-button :loggedIn='loggedIn' :status='status'></favorite-button>
- <delete-button :status='status'></delete-button>
+ <extra-buttons :status="status" @onError="showError" @onSuccess="clearError"></extra-buttons>
</div>
</div>
</div>
@@ -197,6 +206,13 @@ $status-margin: 0.75em;
max-width: 100%;
}
+.status-pin {
+ padding: $status-margin $status-margin 0;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+}
+
.status-preview {
position: absolute;
max-width: 95%;
@@ -240,7 +256,6 @@ $status-margin: 0.75em;
}
.status-el {
- hyphens: auto;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
@@ -569,15 +584,13 @@ $status-margin: 0.75em;
}
}
-.icon-reply:hover {
- color: $fallback--cBlue;
- color: var(--cBlue, $fallback--cBlue);
- cursor: pointer;
-}
-
-.icon-reply.icon-reply-active {
- color: $fallback--cBlue;
- color: var(--cBlue, $fallback--cBlue);
+.button-icon.icon-reply {
+ &:not(.button-icon-disabled):hover,
+ &.button-icon-active {
+ color: $fallback--cBlue;
+ color: var(--cBlue, $fallback--cBlue);
+ cursor: pointer;
+ }
}
.status:hover .animated.avatar {
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index 2d02ca03..b4495673 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -6,7 +6,7 @@
<router-link :to="userProfileLink(user)">
<UserAvatar :betterShadow="betterShadow" :user="user"/>
</router-link>
- <div class="name-and-screen-name">
+ <div class="user-summary">
<div class="top-line">
<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>
@@ -18,12 +18,12 @@
</a>
</div>
- <router-link class='user-screen-name' :to="userProfileLink(user)">
- <span class="handle">@{{user.screen_name}}
- <span class="alert staff" v-if="!hideBio && !!visibleRole">{{visibleRole}}</span>
- </span><span v-if="user.locked"><i class="icon icon-lock"></i></span>
+ <div class="bottom-line">
+ <router-link class="user-screen-name" :to="userProfileLink(user)">@{{user.screen_name}}</router-link>
+ <span class="alert staff" v-if="!hideBio && !!visibleRole">{{visibleRole}}</span>
+ <span v-if="user.locked"><i class="icon icon-lock"></i></span>
<span v-if="!hideUserStatsLocal && !hideBio" class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span>
- </router-link>
+ </div>
</div>
</div>
<div class="user-meta">
@@ -232,7 +232,7 @@
opacity: .8;
}
- .name-and-screen-name {
+ .user-summary {
display: block;
margin-left: 0.6em;
text-align: left;
@@ -249,6 +249,7 @@
vertical-align: middle;
object-fit: contain
}
+
.top-line {
display: flex;
}
@@ -269,15 +270,19 @@
}
}
- .user-screen-name {
- color: $fallback--lightText;
- color: var(--lightText, $fallback--lightText);
- display: inline-block;
+ .bottom-line {
+ display: flex;
font-weight: light;
font-size: 15px;
- padding-right: 0.1em;
- width: 100%;
- display: flex;
+
+ .user-screen-name {
+ min-width: 1px;
+ flex: 0 1 auto;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ }
.dailyAvg {
min-width: 1px;
@@ -288,15 +293,9 @@
color: var(--text, $fallback--text);
}
- .handle {
- min-width: 1px;
- flex: 0 1 auto;
- text-overflow: ellipsis;
- overflow: hidden;
- }
-
// TODO use proper colors
.staff {
+ flex: none;
text-transform: capitalize;
color: $fallback--text;
color: var(--btnText, $fallback--text);
diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js
index 4eddb8b1..eab330e7 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -2,6 +2,7 @@ import get from 'lodash/get'
import UserCard from '../user_card/user_card.vue'
import FollowCard from '../follow_card/follow_card.vue'
import Timeline from '../timeline/timeline.vue'
+import Conversation from '../conversation/conversation.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue'
import List from '../list/list.vue'
import withLoadMore from '../../hocs/with_load_more/with_load_more'
@@ -95,6 +96,8 @@ const UserProfile = {
if (this.isUs) {
this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId })
}
+ // Fetch all pinned statuses immediately
+ this.$store.dispatch('fetchPinnedStatuses', userId)
},
cleanUp () {
this.$store.dispatch('stopFetching', 'user')
@@ -128,7 +131,8 @@ const UserProfile = {
FollowerList,
FriendList,
ModerationTools,
- FollowCard
+ FollowCard,
+ Conversation
}
}
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 71c625b7..48b774ea 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -3,16 +3,28 @@
<div v-if="user" class="user-profile panel panel-default">
<UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
- <Timeline
- :label="$t('user_card.statuses')"
- :disabled="!user.statuses_count"
- :count="user.statuses_count"
- :embedded="true"
- :title="$t('user_profile.timeline_title')"
- :timeline="timeline"
- :timeline-name="'user'"
- :user-id="userId"
- />
+ <div :label="$t('user_card.statuses')" :disabled="!user.statuses_count">
+ <div class="timeline">
+ <template v-for="statusId in user.pinnedStatuseIds">
+ <Conversation
+ v-if="timeline.statusesObject[statusId]"
+ class="status-fadein"
+ :key="statusId"
+ :statusoid="timeline.statusesObject[statusId]"
+ :collapsable="true"
+ :showPinned="true"
+ />
+ </template>
+ </div>
+ <Timeline
+ :count="user.statuses_count"
+ :embedded="true"
+ :title="$t('user_profile.timeline_title')"
+ :timeline="timeline"
+ :timeline-name="'user'"
+ :user-id="userId"
+ />
+ </div>
<div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count">
<FriendList :userId="userId">
<template slot="item" slot-scope="{item}">
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index 8a94f0b8..2cb8b37a 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -251,6 +251,10 @@
margin: 0;
}
+ .visibility-tray {
+ padding-top: 5px;
+ }
+
input[type=file] {
padding: 5px;
height: auto;