Merge branch 'develop' into fix/hashtag-opening

This commit is contained in:
Edijs 2019-02-10 11:59:19 -07:00
commit fc26f1852e
38 changed files with 1491 additions and 78 deletions

View file

@ -7,6 +7,9 @@ const About = {
InstanceSpecificPanel,
FeaturesPanel,
TermsOfServicePanel
},
computed: {
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }
}
}

View file

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

View file

@ -65,7 +65,6 @@ const PostStatusForm = {
newStatus: {
spoilerText: this.subject || '',
status: statusText,
contentType: 'text/plain',
nsfw: false,
files: [],
visibility: scope
@ -167,6 +166,11 @@ const PostStatusForm = {
},
formattingOptionsEnabled () {
return this.$store.state.instance.formattingOptionsEnabled
},
defaultPostContentType () {
return typeof this.$store.state.config.postContentType === 'undefined'
? this.$store.state.instance.postContentType
: this.$store.state.config.postContentType
}
},
methods: {

View file

@ -35,7 +35,7 @@
<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">
<select id="post-content-type" v-model="defaultPostContentType" 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>

View file

@ -7,7 +7,7 @@ const PublicAndExternalTimeline = {
timeline () { return this.$store.state.statuses.timelines.publicAndExternal }
},
created () {
this.$store.dispatch('startFetching', 'publicAndExternal')
this.$store.dispatch('startFetching', { timeline: 'publicAndExternal' })
},
destroyed () {
this.$store.dispatch('stopFetching', 'publicAndExternal')

View file

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

View file

@ -7,7 +7,7 @@ const PublicTimeline = {
timeline () { return this.$store.state.statuses.timelines.public }
},
created () {
this.$store.dispatch('startFetching', 'public')
this.$store.dispatch('startFetching', { timeline: 'public' })
},
destroyed () {
this.$store.dispatch('stopFetching', 'public')

View file

@ -27,6 +27,11 @@ const settings = {
: user.hideUserStats,
hideUserStatsDefault: this.$t('settings.values.' + instance.hideUserStats),
hideFilteredStatusesLocal: typeof user.hideFilteredStatuses === 'undefined'
? instance.hideFilteredStatuses
: user.hideFilteredStatuses,
hideFilteredStatusesDefault: this.$t('settings.values.' + instance.hideFilteredStatuses),
notificationVisibilityLocal: user.notificationVisibility,
replyVisibilityLocal: user.replyVisibility,
loopVideoLocal: user.loopVideo,
@ -46,6 +51,11 @@ const settings = {
: user.subjectLineBehavior,
subjectLineBehaviorDefault: instance.subjectLineBehavior,
postContentTypeLocal: typeof user.postContentType === 'undefined'
? instance.postContentType
: user.postContentType,
postContentTypeDefault: instance.postContentType,
alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined'
? instance.alwaysShowSubjectInput
: user.alwaysShowSubjectInput,
@ -96,6 +106,9 @@ const settings = {
hideUserStatsLocal (value) {
this.$store.dispatch('setOption', { name: 'hideUserStats', value })
},
hideFilteredStatusesLocal (value) {
this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
},
hideNsfwLocal (value) {
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
},
@ -157,6 +170,9 @@ const settings = {
subjectLineBehaviorLocal (value) {
this.$store.dispatch('setOption', { name: 'subjectLineBehavior', value })
},
postContentTypeLocal (value) {
this.$store.dispatch('setOption', { name: 'postContentType', value })
},
stopGifs (value) {
this.$store.dispatch('setOption', { name: 'stopGifs', value })
},

View file

@ -100,6 +100,28 @@
</label>
</div>
</li>
<li>
<div>
{{$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>
</select>
<i class="icon-down-open"/>
</label>
</div>
</li>
</ul>
</div>
@ -205,7 +227,6 @@
</label>
</li>
</ul>
</label>
</div>
<div>
{{$t('settings.replies_in_timeline')}}
@ -232,11 +253,18 @@
</div>
</div>
<div class="setting-item">
<p>{{$t('settings.filtering_explanation')}}</p>
<textarea id="muteWords" v-model="muteWordsString"></textarea>
<div>
<p>{{$t('settings.filtering_explanation')}}</p>
<textarea id="muteWords" v-model="muteWordsString"></textarea>
</div>
<div>
<input type="checkbox" id="hideFilteredStatuses" v-model="hideFilteredStatusesLocal">
<label for="hideFilteredStatuses">
{{$t('settings.hide_filtered_statuses')}} {{$t('settings.instance_default', { value: hideFilteredStatusesDefault })}}
</label>
</div>
</div>
</div>
</tab-switcher>
</keep-alive>
</div>

View file

@ -11,7 +11,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
import fileType from 'src/services/file_type/file_type.service'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
import { filter, find } from 'lodash'
import { filter, find, unescape } from 'lodash'
const Status = {
name: 'Status',
@ -110,6 +110,14 @@ const Status = {
return hits
},
muted () { return !this.unmuted && (this.status.user.muted || this.muteWordHits.length > 0) },
hideFilteredStatuses () {
return typeof this.$store.state.config.hideFilteredStatuses === 'undefined'
? this.$store.state.instance.hideFilteredStatuses
: this.$store.state.config.hideFilteredStatuses
},
hideStatus () {
return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses)
},
isFocused () {
// retweet or root of an expanded conversation
if (this.focused) {
@ -201,14 +209,15 @@ const Status = {
},
replySubject () {
if (!this.status.summary) return ''
const decodedSummary = unescape(this.status.summary)
const behavior = typeof this.$store.state.config.subjectLineBehavior === 'undefined'
? this.$store.state.instance.subjectLineBehavior
: this.$store.state.config.subjectLineBehavior
const startsWithRe = this.status.summary.match(/^re[: ]/i)
const startsWithRe = decodedSummary.match(/^re[: ]/i)
if (behavior !== 'noop' && startsWithRe || behavior === 'masto') {
return this.status.summary
return decodedSummary
} else if (behavior === 'email') {
return 're: '.concat(this.status.summary)
return 're: '.concat(decodedSummary)
} else if (behavior === 'noop') {
return ''
}

View file

@ -1,5 +1,5 @@
<template>
<div class="status-el" v-if="!hideReply && !deleted" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
<div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
<template v-if="muted && !noReplyLinks">
<div class="media status container muted">
<small>
@ -16,9 +16,8 @@
<UserAvatar v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/>
<div class="media-body faint">
<span class="user-name">
<router-link :to="retweeterProfileLink">
{{retweeterHtml || retweeter}}
</router-link>
<router-link v-if="retweeterHtml" :to="retweeterProfileLink" v-html="retweeterHtml"/>
<router-link v-else :to="retweeterProfileLink">{{retweeter}}</router-link>
</span>
<i class='fa icon-retweet retweeted' :title="$t('tool_tip.repeat')"></i>
{{$t('timeline.repeated')}}

View file

@ -37,7 +37,7 @@ export default Vue.component('tab-switcher', {
return (
<div class={ classesWrapper.join(' ')}>
<button onClick={this.activateTab(index)} class={ classesTab.join(' ') }>{slot.data.attrs.label}</button>
<button disabled={slot.data.attrs.disabled} onClick={this.activateTab(index)} class={ classesTab.join(' ') }>{slot.data.attrs.label}</button>
</div>
)
})

View file

@ -3,7 +3,7 @@ import Timeline from '../timeline/timeline.vue'
const TagTimeline = {
created () {
this.$store.commit('clearTimeline', { timeline: 'tag' })
this.$store.dispatch('startFetching', { 'tag': this.tag })
this.$store.dispatch('startFetching', { timeline: 'tag', tag: this.tag })
},
components: {
Timeline
@ -15,7 +15,7 @@ const TagTimeline = {
watch: {
tag () {
this.$store.commit('clearTimeline', { timeline: 'tag' })
this.$store.dispatch('startFetching', { 'tag': this.tag })
this.$store.dispatch('startFetching', { timeline: 'tag', tag: this.tag })
}
},
destroyed () {

View file

@ -79,6 +79,12 @@ export default {
set (color) {
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color })
}
},
visibleRole () {
const validRole = (this.user.role === 'admin' || this.user.role === 'moderator')
const showRole = this.isOtherUser || this.user.show_role
return validRole && showRole && this.user.role
}
},
components: {

View file

@ -19,7 +19,9 @@
</div>
<router-link class='user-screen-name' :to="userProfileLink(user)">
<span class="handle">@{{user.screen_name}}</span><span v-if="user.locked"><i class="icon icon-lock"></i></span>
<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>
<span v-if="!hideUserStatsLocal && !hideBio" class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span>
</router-link>
</div>
@ -247,6 +249,15 @@
text-overflow: ellipsis;
overflow: hidden;
}
// TODO use proper colors
.staff {
text-transform: capitalize;
color: $fallback--text;
color: var(--btnText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg);
}
}
.user-meta {

View file

@ -8,8 +8,8 @@ const UserProfile = {
this.$store.commit('clearTimeline', { timeline: 'user' })
this.$store.commit('clearTimeline', { timeline: 'favorites' })
this.$store.commit('clearTimeline', { timeline: 'media' })
this.$store.dispatch('startFetching', ['user', this.fetchBy])
this.$store.dispatch('startFetching', ['media', this.fetchBy])
this.$store.dispatch('startFetching', { timeline: 'user', userId: this.fetchBy })
this.$store.dispatch('startFetching', { timeline: 'media', userId: this.fetchBy })
this.startFetchFavorites()
if (!this.user.id) {
this.$store.dispatch('fetchUser', this.fetchBy)
@ -58,17 +58,23 @@ const UserProfile = {
},
isExternal () {
return this.$route.name === 'external-user-profile'
},
followsTabVisible () {
return this.isUs || !this.user.hide_follows
},
followersTabVisible () {
return this.isUs || !this.user.hide_followers
}
},
methods: {
startFetchFavorites () {
if (this.isUs) {
this.$store.dispatch('startFetching', ['favorites', this.fetchBy])
this.$store.dispatch('startFetching', { timeline: 'favorites', userId: this.fetchBy })
}
},
startUp () {
this.$store.dispatch('startFetching', ['user', this.fetchBy])
this.$store.dispatch('startFetching', ['media', this.fetchBy])
this.$store.dispatch('startFetching', { timeline: 'user', userId: this.fetchBy })
this.$store.dispatch('startFetching', { timeline: 'media', userId: this.fetchBy })
this.startFetchFavorites()
},

View file

@ -9,19 +9,20 @@
<tab-switcher :renderOnlyFocused="true">
<Timeline
:label="$t('user_card.statuses')"
:disabled="!user.statuses_count"
:embedded="true"
:title="$t('user_profile.timeline_title')"
:timeline="timeline"
:timeline-name="'user'"
:user-id="fetchBy"
/>
<div :label="$t('user_card.followees')">
<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>
</div>
<div :label="$t('user_card.followers')">
<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>
@ -29,6 +30,7 @@
</div>
<Timeline
:label="$t('user_card.media')"
:disabled="!media.visibleStatuses.length"
:embedded="true" :title="$t('user_card.media')"
timeline-name="media"
:timeline="media"
@ -37,6 +39,7 @@
<Timeline
v-if="isUs"
:label="$t('user_card.favorites')"
:disabled="!favorites.visibleStatuses.length"
:embedded="true"
:title="$t('user_card.favorites')"
timeline-name="favorites"

View file

@ -12,7 +12,10 @@ const UserSettings = {
newLocked: this.$store.state.users.currentUser.locked,
newNoRichText: this.$store.state.users.currentUser.no_rich_text,
newDefaultScope: this.$store.state.users.currentUser.default_scope,
newHideNetwork: this.$store.state.users.currentUser.hide_network,
hideFollows: this.$store.state.users.currentUser.hide_follows,
hideFollowers: this.$store.state.users.currentUser.hide_followers,
showRole: this.$store.state.users.currentUser.show_role,
role: this.$store.state.users.currentUser.role,
followList: null,
followImportError: false,
followsImported: false,
@ -68,7 +71,10 @@ const UserSettings = {
/* eslint-disable camelcase */
const default_scope = this.newDefaultScope
const no_rich_text = this.newNoRichText
const hide_network = this.newHideNetwork
const hide_follows = this.hideFollows
const hide_followers = this.hideFollowers
const show_role = this.showRole
/* eslint-enable camelcase */
this.$store.state.api.backendInteractor
.updateProfile({
@ -80,7 +86,9 @@ const UserSettings = {
/* eslint-disable camelcase */
default_scope,
no_rich_text,
hide_network
hide_follows,
hide_followers,
show_role
/* eslint-enable camelcase */
}}).then((user) => {
if (!user.error) {
@ -235,7 +243,9 @@ const UserSettings = {
exportFollows () {
this.enableFollowsExport = false
this.$store.state.api.backendInteractor
.fetchFriends({id: this.$store.state.users.currentUser.id})
.exportFriends({
id: this.$store.state.users.currentUser.id
})
.then((friendList) => {
this.exportPeople(friendList, 'friends.csv')
setTimeout(() => { this.enableFollowsExport = true }, 2000)

View file

@ -30,8 +30,17 @@
<label for="account-no-rich-text">{{$t('settings.no_rich_text_description')}}</label>
</p>
<p>
<input type="checkbox" v-model="newHideNetwork" id="account-hide-network">
<label for="account-hide-network">{{$t('settings.hide_network_description')}}</label>
<input type="checkbox" v-model="hideFollows" id="account-hide-follows">
<label for="account-hide-follows">{{$t('settings.hide_follows_description')}}</label>
</p>
<p>
<input type="checkbox" v-model="hideFollowers" id="account-hide-followers">
<label for="account-hide-followers">{{$t('settings.hide_followers_description')}}</label>
</p>
<p>
<input type="checkbox" v-model="showRole" id="account-show-role">
<label for="account-show-role" v-if="role === 'admin'">{{$t('settings.show_admin_badge')}}</label>
<label for="account-show-role" v-if="role === 'moderator'">{{$t('settings.show_moderator_badge')}}</label>
</p>
<button :disabled='newName && newName.length === 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button>
</div>
@ -180,5 +189,9 @@
.name-changer {
width: 100%;
}
.bg {
max-width: 100%;
}
}
</style>