Merge branch 'develop' into 'feature/copy-link'

# Conflicts:
#   static/fontello.json
This commit is contained in:
Shpuld Shpludson 2020-05-08 08:20:03 +00:00
commit f4676e17d3
19 changed files with 605 additions and 89 deletions

View file

@ -29,17 +29,29 @@ export default data => input => {
export const suggestEmoji = emojis => input => {
const noPrefix = input.toLowerCase().substr(1)
return emojis
.filter(({ displayText }) => displayText.toLowerCase().startsWith(noPrefix))
.filter(({ displayText }) => displayText.toLowerCase().match(noPrefix))
.sort((a, b) => {
let aScore = 0
let bScore = 0
// Make custom emojis a priority
aScore += a.imageUrl ? 10 : 0
bScore += b.imageUrl ? 10 : 0
// An exact match always wins
aScore += a.displayText.toLowerCase() === noPrefix ? 200 : 0
bScore += b.displayText.toLowerCase() === noPrefix ? 200 : 0
// Sort alphabetically
const alphabetically = a.displayText > b.displayText ? 1 : -1
// Prioritize custom emoji a lot
aScore += a.imageUrl ? 100 : 0
bScore += b.imageUrl ? 100 : 0
// Prioritize prefix matches somewhat
aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0
bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0
// Sort by length
aScore -= a.displayText.length
bScore -= b.displayText.length
// Break ties alphabetically
const alphabetically = a.displayText > b.displayText ? 0.5 : -0.5
return bScore - aScore + alphabetically
})

View file

@ -1,4 +1,5 @@
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js'
const FollowRequestCard = {
props: ['user'],
@ -6,13 +7,32 @@ const FollowRequestCard = {
BasicUserCard
},
methods: {
findFollowRequestNotificationId () {
const notif = notificationsFromStore(this.$store).find(
(notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request'
)
return notif && notif.id
},
approveUser () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
const notifId = this.findFollowRequestNotificationId()
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
this.$store.dispatch('updateNotification', {
id: notifId,
updater: notification => {
notification.type = 'follow'
}
})
},
denyUser () {
const notifId = this.findFollowRequestNotificationId()
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
this.$store.dispatch('removeFollowRequest', this.user)
})
}
}
}

View file

@ -2,6 +2,7 @@ import Status from '../status/status.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
import UserCard from '../user_card/user_card.vue'
import Timeago from '../timeago/timeago.vue'
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -32,6 +33,24 @@ const Notification = {
},
toggleMute () {
this.unmuted = !this.unmuted
},
approveUser () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
this.$store.dispatch('updateNotification', {
id: this.notification.id,
updater: notification => {
notification.type = 'follow'
}
})
},
denyUser () {
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id })
this.$store.dispatch('removeFollowRequest', this.user)
})
}
},
computed: {
@ -57,6 +76,9 @@ const Notification = {
},
needMute () {
return this.user.muted
},
isStatusNotification () {
return isStatusNotification(this.notification.type)
}
}
}

View file

@ -47,7 +47,7 @@
<span class="notification-details">
<div class="name-and-action">
<!-- eslint-disable vue/no-v-html -->
<span
<bdi
v-if="!!notification.from_profile.name_html"
class="username"
:title="'@'+notification.from_profile.screen_name"
@ -74,6 +74,10 @@
<i class="fa icon-user-plus lit" />
<small>{{ $t('notifications.followed_you') }}</small>
</span>
<span v-if="notification.type === 'follow_request'">
<i class="fa icon-user lit" />
<small>{{ $t('notifications.follow_request') }}</small>
</span>
<span v-if="notification.type === 'move'">
<i class="fa icon-arrow-curved lit" />
<small>{{ $t('notifications.migrated_to') }}</small>
@ -87,18 +91,7 @@
</span>
</div>
<div
v-if="notification.type === 'follow' || notification.type === 'move'"
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<div
v-else
v-if="isStatusNotification"
class="timeago"
>
<router-link
@ -112,6 +105,17 @@
/>
</router-link>
</div>
<div
v-else
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<a
v-if="needMute"
href="#"
@ -119,12 +123,30 @@
><i class="button-icon icon-eye-off" /></a>
</span>
<div
v-if="notification.type === 'follow'"
v-if="notification.type === 'follow' || notification.type === 'follow_request'"
class="follow-text"
>
<router-link :to="userProfileLink">
<router-link
:to="userProfileLink"
class="follow-name"
>
@{{ notification.from_profile.screen_name }}
</router-link>
<div
v-if="notification.type === 'follow_request'"
style="white-space: nowrap;"
>
<i
class="icon-ok button-icon follow-request-accept"
:title="$t('tool_tip.accept_follow_request')"
@click="approveUser()"
/>
<i
class="icon-cancel button-icon follow-request-reject"
:title="$t('tool_tip.reject_follow_request')"
@click="denyUser()"
/>
</div>
</div>
<div
v-else-if="notification.type === 'move'"

View file

@ -79,9 +79,38 @@
}
}
.follow-request-accept {
cursor: pointer;
&:hover {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
.follow-request-reject {
cursor: pointer;
&:hover {
color: $fallback--cRed;
color: var(--cRed, $fallback--cRed);
}
}
.follow-text, .move-text {
padding: 0.5em 0;
overflow-wrap: break-word;
display: flex;
justify-content: space-between;
.follow-name {
display: block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.status-el {
@ -143,6 +172,11 @@
color: var(--cGreen, $fallback--cGreen);
}
.icon-user.lit {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
.icon-user-plus.lit {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);

View file

@ -1,5 +1,5 @@
import { validationMixin } from 'vuelidate'
import { required, sameAs } from 'vuelidate/lib/validators'
import { required, requiredIf, sameAs } from 'vuelidate/lib/validators'
import { mapActions, mapState } from 'vuex'
const registration = {
@ -14,15 +14,17 @@ const registration = {
},
captcha: {}
}),
validations: {
user: {
email: { required },
username: { required },
fullname: { required },
password: { required },
confirm: {
required,
sameAsPassword: sameAs('password')
validations () {
return {
user: {
email: { required: requiredIf(() => this.accountActivationRequired) },
username: { required },
fullname: { required },
password: { required },
confirm: {
required,
sameAsPassword: sameAs('password')
}
}
}
},
@ -43,7 +45,8 @@ const registration = {
signedIn: (state) => !!state.users.currentUser,
isPending: (state) => state.users.signUpPending,
serverValidationErrors: (state) => state.users.signUpErrors,
termsOfService: (state) => state.instance.tos
termsOfService: (state) => state.instance.tos,
accountActivationRequired: (state) => state.instance.accountActivationRequired
})
},
methods: {

View file

@ -188,23 +188,22 @@ const Status = {
}
return this.status.attentions.length > 0
},
// When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status.
mightHideBecauseSubject () {
return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault)
},
mightHideBecauseTall () {
return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault)
},
hideSubjectStatus () {
if (this.tallStatus && !this.localCollapseSubjectDefault) {
return false
}
return !this.expandingSubject && this.status.summary
return this.mightHideBecauseSubject && !this.expandingSubject
},
hideTallStatus () {
if (this.status.summary && this.localCollapseSubjectDefault) {
return false
}
if (this.showingTall) {
return false
}
return this.tallStatus
return this.mightHideBecauseTall && !this.showingTall
},
showingMore () {
return (this.tallStatus && this.showingTall) || (this.status.summary && this.expandingSubject)
return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject)
},
nsfwClickthrough () {
if (!this.status.nsfw) {
@ -408,14 +407,10 @@ const Status = {
this.userExpanded = !this.userExpanded
},
toggleShowMore () {
if (this.showingTall) {
this.showingTall = false
} else if (this.expandingSubject && this.status.summary) {
this.expandingSubject = false
} else if (this.hideTallStatus) {
this.showingTall = true
} else if (this.hideSubjectStatus && this.status.summary) {
this.expandingSubject = true
if (this.mightHideBecauseTall) {
this.showingTall = !this.showingTall
} else if (this.mightHideBecauseSubject) {
this.expandingSubject = !this.expandingSubject
}
},
generateUserProfileLink (id, name) {

View file

@ -379,6 +379,7 @@
:label="$t('settings.notifications')"
>
<div class="setting-item">
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
<div class="select-multiple">
<span class="label">{{ $t('settings.notification_setting') }}</span>
<ul class="option-list">
@ -404,6 +405,17 @@
</li>
</ul>
</div>
</div>
<div class="setting-item">
<h2>{{ $t('settings.notification_setting_privacy') }}</h2>
<p>
<Checkbox v-model="notificationSettings.privacy_option">
{{ $t('settings.notification_setting_privacy_option') }}
</Checkbox>
</p>
</div>
<div class="setting-item">
<p>{{ $t('settings.notification_mutes') }}</p>
<p>{{ $t('settings.notification_blocks') }}</p>
<button