Merge branch 'develop' into 'muting-fixes'
# Conflicts: # src/components/status/status.js
This commit is contained in:
commit
1306fac38f
27 changed files with 724 additions and 79 deletions
|
@ -5,12 +5,8 @@ const conversationPage = {
|
|||
Conversation
|
||||
},
|
||||
computed: {
|
||||
statusoid () {
|
||||
const id = this.$route.params.id
|
||||
const statuses = this.$store.state.statuses.allStatusesObject
|
||||
const status = statuses[id]
|
||||
|
||||
return status
|
||||
statusId () {
|
||||
return this.$route.params.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<conversation
|
||||
:collapsable="false"
|
||||
is-page="true"
|
||||
:statusoid="statusoid"
|
||||
:status-id="statusId"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { reduce, filter, findIndex, clone } from 'lodash'
|
||||
import { reduce, filter, findIndex, clone, get } from 'lodash'
|
||||
import Status from '../status/status.vue'
|
||||
|
||||
const sortById = (a, b) => {
|
||||
|
@ -39,10 +39,11 @@ const conversation = {
|
|||
}
|
||||
},
|
||||
props: [
|
||||
'statusoid',
|
||||
'statusId',
|
||||
'collapsable',
|
||||
'isPage',
|
||||
'pinnedStatusIdsObject'
|
||||
'pinnedStatusIdsObject',
|
||||
'inProfile'
|
||||
],
|
||||
created () {
|
||||
if (this.isPage) {
|
||||
|
@ -51,21 +52,17 @@ const conversation = {
|
|||
},
|
||||
computed: {
|
||||
status () {
|
||||
return this.statusoid
|
||||
return this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
},
|
||||
statusId () {
|
||||
if (this.statusoid.retweeted_status) {
|
||||
return this.statusoid.retweeted_status.id
|
||||
originalStatusId () {
|
||||
if (this.status.retweeted_status) {
|
||||
return this.status.retweeted_status.id
|
||||
} else {
|
||||
return this.statusoid.id
|
||||
return this.statusId
|
||||
}
|
||||
},
|
||||
conversationId () {
|
||||
if (this.statusoid.retweeted_status) {
|
||||
return this.statusoid.retweeted_status.statusnet_conversation_id
|
||||
} else {
|
||||
return this.statusoid.statusnet_conversation_id
|
||||
}
|
||||
return this.getConversationId(this.statusId)
|
||||
},
|
||||
conversation () {
|
||||
if (!this.status) {
|
||||
|
@ -77,7 +74,7 @@ const conversation = {
|
|||
}
|
||||
|
||||
const conversation = clone(this.$store.state.statuses.conversationsObject[this.conversationId])
|
||||
const statusIndex = findIndex(conversation, { id: this.statusId })
|
||||
const statusIndex = findIndex(conversation, { id: this.originalStatusId })
|
||||
if (statusIndex !== -1) {
|
||||
conversation[statusIndex] = this.status
|
||||
}
|
||||
|
@ -110,7 +107,15 @@ const conversation = {
|
|||
Status
|
||||
},
|
||||
watch: {
|
||||
status: 'fetchConversation',
|
||||
statusId (newVal, oldVal) {
|
||||
const newConversationId = this.getConversationId(newVal)
|
||||
const oldConversationId = this.getConversationId(oldVal)
|
||||
if (newConversationId && oldConversationId && newConversationId === oldConversationId) {
|
||||
this.setHighlight(this.originalStatusId)
|
||||
} else {
|
||||
this.fetchConversation()
|
||||
}
|
||||
},
|
||||
expanded (value) {
|
||||
if (value) {
|
||||
this.fetchConversation()
|
||||
|
@ -120,24 +125,25 @@ const conversation = {
|
|||
methods: {
|
||||
fetchConversation () {
|
||||
if (this.status) {
|
||||
this.$store.state.api.backendInteractor.fetchConversation({ id: this.status.id })
|
||||
this.$store.state.api.backendInteractor.fetchConversation({ id: this.statusId })
|
||||
.then(({ ancestors, descendants }) => {
|
||||
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
||||
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
||||
this.setHighlight(this.originalStatusId)
|
||||
})
|
||||
.then(() => this.setHighlight(this.statusId))
|
||||
} else {
|
||||
const id = this.$route.params.id
|
||||
this.$store.state.api.backendInteractor.fetchStatus({ id })
|
||||
.then((status) => this.$store.dispatch('addNewStatuses', { statuses: [status] }))
|
||||
.then(() => this.fetchConversation())
|
||||
this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId })
|
||||
.then((status) => {
|
||||
this.$store.dispatch('addNewStatuses', { statuses: [status] })
|
||||
this.fetchConversation()
|
||||
})
|
||||
}
|
||||
},
|
||||
getReplies (id) {
|
||||
return this.replies[id] || []
|
||||
},
|
||||
focused (id) {
|
||||
return (this.isExpanded) && id === this.status.id
|
||||
return (this.isExpanded) && id === this.statusId
|
||||
},
|
||||
setHighlight (id) {
|
||||
if (!id) return
|
||||
|
@ -149,6 +155,10 @@ const conversation = {
|
|||
},
|
||||
toggleExpanded () {
|
||||
this.expanded = !this.expanded
|
||||
},
|
||||
getConversationId (statusId) {
|
||||
const status = this.$store.state.statuses.allStatusesObject[statusId]
|
||||
return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
:in-conversation="isExpanded"
|
||||
:highlight="getHighlight()"
|
||||
:replies="getReplies(status.id)"
|
||||
:in-profile="inProfile"
|
||||
class="status-fadein panel-body"
|
||||
@goto="setHighlight"
|
||||
@toggleExpanded="toggleExpanded"
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
type="password"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<router-link :to="{name: 'password-reset'}">
|
||||
{{ $t('password_reset.forgot_password') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
|
|
62
src/components/password_reset/password_reset.js
Normal file
62
src/components/password_reset/password_reset.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { mapState } from 'vuex'
|
||||
import passwordResetApi from '../../services/new_api/password_reset.js'
|
||||
|
||||
const passwordReset = {
|
||||
data: () => ({
|
||||
user: {
|
||||
email: ''
|
||||
},
|
||||
isPending: false,
|
||||
success: false,
|
||||
throttled: false,
|
||||
error: null
|
||||
}),
|
||||
computed: {
|
||||
...mapState({
|
||||
signedIn: (state) => !!state.users.currentUser,
|
||||
instance: state => state.instance
|
||||
}),
|
||||
mailerEnabled () {
|
||||
return this.instance.mailerEnabled
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.signedIn) {
|
||||
this.$router.push({ name: 'root' })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dismissError () {
|
||||
this.error = null
|
||||
},
|
||||
submit () {
|
||||
this.isPending = true
|
||||
const email = this.user.email
|
||||
const instance = this.instance.server
|
||||
|
||||
passwordResetApi({ instance, email }).then(({ status }) => {
|
||||
this.isPending = false
|
||||
this.user.email = ''
|
||||
|
||||
if (status === 204) {
|
||||
this.success = true
|
||||
this.error = null
|
||||
} else if (status === 404 || status === 400) {
|
||||
this.error = this.$t('password_reset.not_found')
|
||||
this.$nextTick(() => {
|
||||
this.$refs.email.focus()
|
||||
})
|
||||
} else if (status === 429) {
|
||||
this.throttled = true
|
||||
this.error = this.$t('password_reset.too_many_requests')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.isPending = false
|
||||
this.user.email = ''
|
||||
this.error = this.$t('general.generic_error')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default passwordReset
|
116
src/components/password_reset/password_reset.vue
Normal file
116
src/components/password_reset/password_reset.vue
Normal file
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<div class="settings panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ $t('password_reset.password_reset') }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form
|
||||
class="password-reset-form"
|
||||
@submit.prevent="submit"
|
||||
>
|
||||
<div class="container">
|
||||
<div v-if="!mailerEnabled">
|
||||
<p>
|
||||
{{ $t('password_reset.password_reset_disabled') }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="success || throttled">
|
||||
<p v-if="success">
|
||||
{{ $t('password_reset.check_email') }}
|
||||
</p>
|
||||
<div class="form-group text-center">
|
||||
<router-link :to="{name: 'root'}">
|
||||
{{ $t('password_reset.return_home') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>
|
||||
{{ $t('password_reset.instruction') }}
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<input
|
||||
ref="email"
|
||||
v-model="user.email"
|
||||
:disabled="isPending"
|
||||
:placeholder="$t('password_reset.placeholder')"
|
||||
class="form-control"
|
||||
type="input"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button
|
||||
:disabled="isPending"
|
||||
type="submit"
|
||||
class="btn btn-default btn-block"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
v-if="error"
|
||||
class="alert error notice-dismissible"
|
||||
>
|
||||
<span>{{ error }}</span>
|
||||
<a
|
||||
class="button-icon dismiss"
|
||||
@click.prevent="dismissError()"
|
||||
>
|
||||
<i class="icon-cancel" />
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./password_reset.js"></script>
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.password-reset-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0.6em;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex: 1 0;
|
||||
flex-direction: column;
|
||||
margin-top: 0.6em;
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1em;
|
||||
padding: 0.3em 0.0em 0.3em;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.error {
|
||||
text-align: center;
|
||||
animation-name: shakeError;
|
||||
animation-duration: 0.4s;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 0.5em;
|
||||
margin: 0.3em 0.0em 1em;
|
||||
}
|
||||
|
||||
.notice-dismissible {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.icon-cancel {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -29,7 +29,8 @@ const Status = {
|
|||
'isPreview',
|
||||
'noHeading',
|
||||
'inlineExpanded',
|
||||
'showPinned'
|
||||
'showPinned',
|
||||
'inProfile'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
|
@ -117,7 +118,7 @@ const Status = {
|
|||
|
||||
return hits
|
||||
},
|
||||
muted () { return !this.unmuted && (this.status.user.muted || this.status.thread_muted || this.muteWordHits.length > 0) },
|
||||
muted () { return !this.unmuted && ((!this.inProfile && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) },
|
||||
hideFilteredStatuses () {
|
||||
return typeof this.$store.state.config.hideFilteredStatuses === 'undefined'
|
||||
? this.$store.state.instance.hideFilteredStatuses
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
v-if="animated"
|
||||
ref="canvas"
|
||||
/>
|
||||
<!-- NOTE: key is required to force to re-render img tag when src is changed -->
|
||||
<img
|
||||
ref="src"
|
||||
:key="src"
|
||||
:src="src"
|
||||
:referrerpolicy="referrerpolicy"
|
||||
@load="onLoad"
|
||||
|
|
|
@ -25,7 +25,8 @@ const Timeline = {
|
|||
'tag',
|
||||
'embedded',
|
||||
'count',
|
||||
'pinnedStatusIds'
|
||||
'pinnedStatusIds',
|
||||
'inProfile'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -33,9 +33,10 @@
|
|||
v-if="timeline.statusesObject[statusId]"
|
||||
:key="statusId + '-pinned'"
|
||||
class="status-fadein"
|
||||
:statusoid="timeline.statusesObject[statusId]"
|
||||
:status-id="statusId"
|
||||
:collapsable="true"
|
||||
:pinned-status-ids-object="pinnedStatusIdsObject"
|
||||
:in-profile="inProfile"
|
||||
/>
|
||||
</template>
|
||||
<template v-for="status in timeline.visibleStatuses">
|
||||
|
@ -43,8 +44,9 @@
|
|||
v-if="!excludedStatusIdsObject[status.id]"
|
||||
:key="status.id"
|
||||
class="status-fadein"
|
||||
:statusoid="status"
|
||||
:status-id="status.id"
|
||||
:collapsable="true"
|
||||
:in-profile="inProfile"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,6 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
followRequestInProgress: false,
|
||||
followRequestSent: false,
|
||||
hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined'
|
||||
? this.$store.state.instance.hideUserStats
|
||||
: this.$store.state.config.hideUserStats,
|
||||
|
@ -112,9 +111,8 @@ export default {
|
|||
followUser () {
|
||||
const store = this.$store
|
||||
this.followRequestInProgress = true
|
||||
requestFollow(this.user, store).then(({ sent }) => {
|
||||
requestFollow(this.user, store).then(() => {
|
||||
this.followRequestInProgress = false
|
||||
this.followRequestSent = sent
|
||||
})
|
||||
},
|
||||
unfollowUser () {
|
||||
|
|
|
@ -135,13 +135,13 @@
|
|||
<button
|
||||
class="btn btn-default btn-block"
|
||||
:disabled="followRequestInProgress"
|
||||
:title="followRequestSent ? $t('user_card.follow_again') : ''"
|
||||
:title="user.requested ? $t('user_card.follow_again') : ''"
|
||||
@click="followUser"
|
||||
>
|
||||
<template v-if="followRequestInProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else-if="followRequestSent">
|
||||
<template v-else-if="user.requested">
|
||||
{{ $t('user_card.follow_sent') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
timeline-name="user"
|
||||
:user-id="userId"
|
||||
:pinned-status-ids="user.pinnedStatusIds"
|
||||
:in-profile="true"
|
||||
/>
|
||||
<div
|
||||
v-if="followsTabVisible"
|
||||
|
@ -69,6 +70,7 @@
|
|||
timeline-name="media"
|
||||
:timeline="media"
|
||||
:user-id="userId"
|
||||
:in-profile="true"
|
||||
/>
|
||||
<Timeline
|
||||
v-if="isUs"
|
||||
|
@ -79,6 +81,7 @@
|
|||
:title="$t('user_card.favorites')"
|
||||
timeline-name="favorites"
|
||||
:timeline="favorites"
|
||||
:in-profile="true"
|
||||
/>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
|
|
|
@ -26,7 +26,7 @@ const WhoToFollow = {
|
|||
}
|
||||
this.users.push(user)
|
||||
|
||||
this.$store.state.api.backendInteractor.externalProfile(user.screen_name)
|
||||
this.$store.state.api.backendInteractor.fetchUser({ id: user.screen_name })
|
||||
.then((externalUser) => {
|
||||
if (!externalUser.error) {
|
||||
this.$store.commit('addNewUsers', [externalUser])
|
||||
|
|
|
@ -13,7 +13,7 @@ function showWhoToFollow (panel, reply) {
|
|||
toFollow.img = img
|
||||
toFollow.name = name
|
||||
|
||||
panel.$store.state.api.backendInteractor.externalProfile(name)
|
||||
panel.$store.state.api.backendInteractor.fetchUser({ id: name })
|
||||
.then((externalUser) => {
|
||||
if (!externalUser.error) {
|
||||
panel.$store.commit('addNewUsers', [externalUser])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue