...
 
Commits (24)
{
"presets": ["es2015", "stage-2", "env"],
"plugins": ["transform-runtime", "lodash", "transform-vue-jsx"],
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-transform-vue-jsx"],
"comments": false
}
import ProgressButton from '../progress_button/progress_button.vue'
const DomainMuteCard = {
props: ['domain'],
components: {
ProgressButton
},
methods: {
unmuteDomain () {
return this.$store.dispatch('unmuteDomain', this.domain)
}
}
}
export default DomainMuteCard
<template>
<div class="domain-mute-card">
<div class="domain-mute-card-domain">
{{ domain }}
</div>
<ProgressButton
:click="unmuteDomain"
class="btn btn-default"
>
{{ $t('domain_mute_card.unmute') }}
<template slot="progress">
{{ $t('domain_mute_card.unmute_progress') }}
</template>
</ProgressButton>
</div>
</template>
<script src="./domain_mute_card.js"></script>
<style lang="scss">
.domain-mute-card {
flex: 1 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.6em 1em 0.6em 0;
&-domain {
margin-right: 1em;
overflow: hidden;
text-overflow: ellipsis;
}
button {
width: 10em;
}
}
</style>
......@@ -58,7 +58,7 @@ const LoginForm = {
).then((result) => {
if (result.error) {
if (result.error === 'mfa_required') {
this.requireMFA({ app: app, settings: result })
this.requireMFA({ settings: result })
} else if (result.identifier === 'password_reset_required') {
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
} else {
......
......@@ -8,18 +8,23 @@ export default {
}),
computed: {
...mapGetters({
authApp: 'authFlow/app',
authSettings: 'authFlow/settings'
}),
...mapState({ instance: 'instance' })
...mapState({
instance: 'instance',
oauth: 'oauth'
})
},
methods: {
...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {
const { clientId, clientSecret } = this.oauth
const data = {
app: this.authApp,
clientId,
clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
......
......@@ -7,18 +7,23 @@ export default {
}),
computed: {
...mapGetters({
authApp: 'authFlow/app',
authSettings: 'authFlow/settings'
}),
...mapState({ instance: 'instance' })
...mapState({
instance: 'instance',
oauth: 'oauth'
})
},
methods: {
...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {
const { clientId, clientSecret } = this.oauth
const data = {
app: this.authApp,
clientId,
clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
......
......@@ -9,6 +9,7 @@ import ScopeSelector from '../scope_selector/scope_selector.vue'
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
import BlockCard from '../block_card/block_card.vue'
import MuteCard from '../mute_card/mute_card.vue'
import DomainMuteCard from '../domain_mute_card/domain_mute_card.vue'
import SelectableList from '../selectable_list/selectable_list.vue'
import ProgressButton from '../progress_button/progress_button.vue'
import EmojiInput from '../emoji_input/emoji_input.vue'
......@@ -32,6 +33,12 @@ const MuteList = withSubscription({
childPropName: 'items'
})(SelectableList)
const DomainMuteList = withSubscription({
fetch: (props, $store) => $store.dispatch('fetchDomainMutes'),
select: (props, $store) => get($store.state.users.currentUser, 'domainMutes', []),
childPropName: 'items'
})(SelectableList)
const UserSettings = {
data () {
return {
......@@ -67,7 +74,8 @@ const UserSettings = {
changedPassword: false,
changePasswordError: false,
activeTab: 'profile',
notificationSettings: this.$store.state.users.currentUser.notification_settings
notificationSettings: this.$store.state.users.currentUser.notification_settings,
newDomainToMute: ''
}
},
created () {
......@@ -80,10 +88,12 @@ const UserSettings = {
ImageCropper,
BlockList,
MuteList,
DomainMuteList,
EmojiInput,
Autosuggest,
BlockCard,
MuteCard,
DomainMuteCard,
ProgressButton,
Importer,
Exporter,
......@@ -365,6 +375,13 @@ const UserSettings = {
unmuteUsers (ids) {
return this.$store.dispatch('unmuteUsers', ids)
},
unmuteDomains (domains) {
return this.$store.dispatch('unmuteDomains', domains)
},
muteDomain () {
return this.$store.dispatch('muteDomain', this.newDomainToMute)
.then(() => { this.newDomainToMute = '' })
},
identity (value) {
return value
}
......
......@@ -563,6 +563,55 @@
</template>
</MuteList>
</div>
<div label="Domain Mutes">
<div class="profile-edit-domain-mute-form">
<input
v-model="newDomainToMute"
type="text"
>
<ProgressButton
class="btn btn-default"
:click="muteDomain"
>
{{ $t('domain_mute_card.mute') }}
<template slot="progress">
{{ $t('domain_mute_card.mute_progress') }}
</template>
</ProgressButton>
</div>
<DomainMuteList
:refresh="true"
:get-key="identity"
>
<template
slot="header"
slot-scope="{selected}"
>
<div class="profile-edit-bulk-actions">
<ProgressButton
v-if="selected.length > 0"
class="btn btn-default"
:click="() => unmuteDomains(selected)"
>
{{ $t('domain_mute_card.unmute') }}
<template slot="progress">
{{ $t('domain_mute_card.unmute_progress') }}
</template>
</ProgressButton>
</div>
</template>
<template
slot="item"
slot-scope="{item}"
>
<DomainMuteCard :domain="item" />
</template>
<template slot="empty">
{{ $t('settings.no_mutes') }}
</template>
</DomainMuteList>
</div>
</tab-switcher>
</div>
</div>
......@@ -639,6 +688,14 @@
}
}
&-domain-mute-form {
padding: 1em 10px;
button {
width: 10em;
}
}
.setting-subitem {
margin-left: 1.75em;
}
......
......@@ -65,7 +65,7 @@ const withLoadMore = ({
}
}
},
render (createElement) {
render (h) {
const props = {
props: {
...this.$props,
......@@ -74,7 +74,7 @@ const withLoadMore = ({
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
return (
<div class="with-load-more">
<WrappedComponent {...props}>
......
......@@ -49,7 +49,7 @@ const withSubscription = ({
}
}
},
render (createElement) {
render (h) {
if (!this.error && !this.loading) {
const props = {
props: {
......@@ -59,7 +59,7 @@ const withSubscription = ({
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
return (
<div class="with-subscription">
<WrappedComponent {...props}>
......
......@@ -21,6 +21,12 @@
"chat": {
"title": "Chat"
},
"domain_mute_card": {
"mute": "Mute",
"mute_progress": "Muting...",
"unmute": "Unmute",
"unmute_progress": "Unmuting..."
},
"exporter": {
"export": "Export",
"processing": "Processing, you'll soon be asked to download your file"
......
......@@ -7,7 +7,6 @@ const RECOVERY_STRATEGY = 'recovery'
// initial state
const state = {
app: null,
settings: {},
strategy: PASSWORD_STRATEGY,
initStrategy: PASSWORD_STRATEGY // default strategy from config
......@@ -16,14 +15,10 @@ const state = {
const resetState = (state) => {
state.strategy = state.initStrategy
state.settings = {}
state.app = null
}
// getters
const getters = {
app: (state, getters) => {
return state.app
},
settings: (state, getters) => {
return state.settings
},
......@@ -55,9 +50,8 @@ const mutations = {
requireToken (state) {
state.strategy = TOKEN_STRATEGY
},
requireMFA (state, { app, settings }) {
requireMFA (state, { settings }) {
state.settings = settings
state.app = app
state.strategy = TOTP_STRATEGY // default strategy of MFA
},
requireRecovery (state) {
......
......@@ -72,6 +72,16 @@ const showReblogs = (store, userId) => {
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
}
const muteDomain = (store, domain) => {
return store.rootState.api.backendInteractor.muteDomain(domain)
.then(() => store.commit('addDomainMute', domain))
}
const unmuteDomain = (store, domain) => {
return store.rootState.api.backendInteractor.unmuteDomain(domain)
.then(() => store.commit('removeDomainMute', domain))
}
export const mutations = {
setMuted (state, { user: { id }, muted }) {
const user = state.usersObject[id]
......@@ -177,6 +187,20 @@ export const mutations = {
state.currentUser.muteIds.push(muteId)
}
},
saveDomainMutes (state, domainMutes) {
state.currentUser.domainMutes = domainMutes
},
addDomainMute (state, domain) {
if (state.currentUser.domainMutes.indexOf(domain) === -1) {
state.currentUser.domainMutes.push(domain)
}
},
removeDomainMute (state, domain) {
const index = state.currentUser.domainMutes.indexOf(domain)
if (index !== -1) {
state.currentUser.domainMutes.splice(index, 1)
}
},
setPinnedToUser (state, status) {
const user = state.usersObject[status.user.id]
const index = user.pinnedStatusIds.indexOf(status.id)
......@@ -297,6 +321,25 @@ const users = {
unmuteUsers (store, ids = []) {
return Promise.all(ids.map(id => unmuteUser(store, id)))
},
fetchDomainMutes (store) {
return store.rootState.api.backendInteractor.fetchDomainMutes()
.then((domainMutes) => {
store.commit('saveDomainMutes', domainMutes)
return domainMutes
})
},
muteDomain (store, domain) {
return muteDomain(store, domain)
},
unmuteDomain (store, domain) {
return unmuteDomain(store, domain)
},
muteDomains (store, domains = []) {
return Promise.all(domains.map(domain => muteDomain(store, domain)))
},
unmuteDomains (store, domain = []) {
return Promise.all(domain.map(domain => unmuteDomain(store, domain)))
},
fetchFriends ({ rootState, commit }, id) {
const user = rootState.users.usersObject[id]
const maxId = last(user.friendIds)
......@@ -451,6 +494,7 @@ const users = {
user.credentials = accessToken
user.blockIds = []
user.muteIds = []
user.domainMutes = []
commit('setCurrentUser', user)
commit('addNewUsers', [user])
......
......@@ -71,6 +71,7 @@ const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute`
const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute`
const MASTODON_SEARCH_2 = `/api/v2/search`
const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search'
const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
const oldfetch = window.fetch
......@@ -932,6 +933,28 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
})
}
const fetchDomainMutes = ({ credentials }) => {
return promisedRequest({ url: MASTODON_DOMAIN_BLOCKS_URL, credentials })
}
const muteDomain = ({ domain, credentials }) => {
return promisedRequest({
url: MASTODON_DOMAIN_BLOCKS_URL,
method: 'POST',
payload: { domain },
credentials
})
}
const unmuteDomain = ({ domain, credentials }) => {
return promisedRequest({
url: MASTODON_DOMAIN_BLOCKS_URL,
method: 'DELETE',
payload: { domain },
credentials
})
}
const apiService = {
verifyCredentials,
fetchTimeline,
......@@ -1000,7 +1023,10 @@ const apiService = {
reportUser,
updateNotificationSettings,
search2,
searchUsers
searchUsers,
fetchDomainMutes,
muteDomain,
unmuteDomain
}
export default apiService
......@@ -156,6 +156,9 @@ const backendInteractorService = credentials => {
const search2 = ({ q, resolve, limit, offset, following }) =>
apiService.search2({ credentials, q, resolve, limit, offset, following })
const searchUsers = (query) => apiService.searchUsers({ query, credentials })
const fetchDomainMutes = () => apiService.fetchDomainMutes({ credentials })
const muteDomain = (domain) => apiService.muteDomain({ domain, credentials })
const unmuteDomain = (domain) => apiService.unmuteDomain({ domain, credentials })
const backendInteractorServiceInstance = {
fetchStatus,
......@@ -221,7 +224,10 @@ const backendInteractorService = credentials => {
unretweet,
updateNotificationSettings,
search2,
searchUsers
searchUsers,
fetchDomainMutes,
muteDomain,
unmuteDomain
}
return backendInteractorServiceInstance
......
const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
const verifyOTPCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
const url = `${instance}/oauth/mfa/challenge`
const form = new window.FormData()
form.append('client_id', app.client_id)
form.append('client_secret', app.client_secret)
form.append('client_id', clientId)
form.append('client_secret', clientSecret)
form.append('mfa_token', mfaToken)
form.append('code', code)
form.append('challenge_type', 'totp')
......@@ -14,12 +14,12 @@ const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
}).then((data) => data.json())
}
const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
const verifyRecoveryCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
const url = `${instance}/oauth/mfa/challenge`
const form = new window.FormData()
form.append('client_id', app.client_id)
form.append('client_secret', app.client_secret)
form.append('client_id', clientId)
form.append('client_secret', clientSecret)
form.append('mfa_token', mfaToken)
form.append('code', code)
form.append('challenge_type', 'recovery')
......
require('babel-register')
require('@babel/register')
var config = require('../../config')
// http://nightwatchjs.org/guide#settings-file
......
This diff is collapsed.