Merge remote-tracking branch 'origin/develop' into from/develop/tusooa/sw-cache-assets
This commit is contained in:
commit
a1f43234cd
235 changed files with 6354 additions and 4065 deletions
|
|
@ -315,7 +315,7 @@ const AppearanceTab = {
|
|||
},
|
||||
onImportFailure (result) {
|
||||
console.error('Failure importing theme:', result)
|
||||
this.$store.useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
},
|
||||
importValidator (parsed, filename) {
|
||||
if (filename.endsWith('.json')) {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@
|
|||
|
||||
.palettes-container {
|
||||
height: 15em;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
overflow: hidden auto;
|
||||
scrollbar-gutter: stable;
|
||||
border-radius: var(--roundness);
|
||||
border: 1px solid var(--border);
|
||||
|
|
@ -112,8 +111,7 @@
|
|||
flex-wrap: wrap;
|
||||
margin: -0.5em 0;
|
||||
height: 25em;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow: hidden auto;
|
||||
scrollbar-gutter: stable;
|
||||
border-radius: var(--roundness);
|
||||
border: 1px solid var(--border);
|
||||
|
|
|
|||
|
|
@ -336,6 +336,15 @@
|
|||
{{ $t('settings.show_scrollbars') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="themeEditorMinWidth"
|
||||
:units="['px', 'rem']"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.theme_editor_min_width') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Importer from 'src/components/importer/importer.vue'
|
|||
import Exporter from 'src/components/exporter/exporter.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import { mapState } from 'vuex'
|
||||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
||||
|
||||
const DataImportExportTab = {
|
||||
data () {
|
||||
|
|
@ -15,7 +16,7 @@ const DataImportExportTab = {
|
|||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
useOAuthTokensStore().fetchTokens()
|
||||
this.fetchBackups()
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,48 +1,209 @@
|
|||
import { filter, trim, debounce } from 'lodash'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
import {
|
||||
newImporter,
|
||||
newExporter
|
||||
} from 'src/services/export_import/export_import.js'
|
||||
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import HelpIndicator from '../helpers/help_indicator.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const SUPPORTED_TYPES = new Set(['word', 'regexp', 'user', 'user_regexp'])
|
||||
|
||||
const FilteringTab = {
|
||||
data () {
|
||||
return {
|
||||
muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n'),
|
||||
replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.reply_visibility_${mode}`)
|
||||
}))
|
||||
})),
|
||||
muteFiltersDraftObject: cloneDeep(useServerSideStorageStore().prefsStorage.simple.muteFilters),
|
||||
muteFiltersDraftDirty: Object.fromEntries(
|
||||
Object.entries(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters
|
||||
).map(([k]) => [k, false])
|
||||
),
|
||||
exportedFilter: null,
|
||||
filterImporter: newImporter({
|
||||
validator (parsed) {
|
||||
if (Array.isArray(parsed)) return false
|
||||
if (!SUPPORTED_TYPES.has(parsed.type)) return false
|
||||
return true
|
||||
},
|
||||
onImport: (data) => {
|
||||
const {
|
||||
enabled = true,
|
||||
expires = null,
|
||||
hide = false,
|
||||
name = '',
|
||||
value = ''
|
||||
} = data
|
||||
|
||||
this.createFilter({
|
||||
enabled,
|
||||
expires,
|
||||
hide,
|
||||
name,
|
||||
value
|
||||
})
|
||||
},
|
||||
onImportFailure (result) {
|
||||
console.error('Failure importing filter:', result)
|
||||
useInterfaceStore()
|
||||
.pushGlobalNotice({
|
||||
messageKey: 'settings.filter.import_failure',
|
||||
level: 'error'
|
||||
})
|
||||
}
|
||||
}),
|
||||
filterExporter: newExporter({
|
||||
filename: 'pleromafe_mute-filter',
|
||||
getExportedObject: () => this.exportedFilter
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
UnitSetting,
|
||||
IntegerSetting
|
||||
IntegerSetting,
|
||||
Checkbox,
|
||||
Select,
|
||||
HelpIndicator
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
muteWordsString: {
|
||||
get () {
|
||||
return this.muteWordsStringLocal
|
||||
},
|
||||
set (value) {
|
||||
this.muteWordsStringLocal = value
|
||||
this.debouncedSetMuteWords(value)
|
||||
...mapState(
|
||||
useServerSideStorageStore,
|
||||
{
|
||||
muteFilters: store => Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: store => store.prefsStorage.simple.muteFilters
|
||||
}
|
||||
),
|
||||
muteFiltersDraft () {
|
||||
return Object.entries(this.muteFiltersDraftObject)
|
||||
},
|
||||
debouncedSetMuteWords () {
|
||||
return debounce((value) => {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'muteWords',
|
||||
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||
})
|
||||
}, 1000)
|
||||
muteFiltersExpired () {
|
||||
const now = Date.now()
|
||||
return Object
|
||||
.entries(this.muteFiltersDraftObject)
|
||||
.filter(([, { expires }]) => expires != null && expires <= now)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']),
|
||||
getDatetimeLocal (timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
let fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2})
|
||||
const datetime = [
|
||||
date.getFullYear(),
|
||||
'-',
|
||||
fmt.format(date.getMonth() + 1),
|
||||
'-',
|
||||
fmt.format(date.getDate()),
|
||||
'T',
|
||||
fmt.format(date.getHours()),
|
||||
':',
|
||||
fmt.format(date.getMinutes())
|
||||
].join('')
|
||||
return datetime
|
||||
},
|
||||
checkRegexValid (id) {
|
||||
const filter = this.muteFiltersObject[id]
|
||||
if (filter.type !== 'regexp') return true
|
||||
if (filter.type !== 'user_regexp') return true
|
||||
const { value } = filter
|
||||
let valid = true
|
||||
try {
|
||||
new RegExp(value)
|
||||
} catch {
|
||||
valid = false
|
||||
console.error('Invalid RegExp: ' + value)
|
||||
}
|
||||
return valid
|
||||
},
|
||||
createFilter (filter = {
|
||||
type: 'word',
|
||||
value: '',
|
||||
name: 'New Filter',
|
||||
enabled: true,
|
||||
expires: null,
|
||||
hide: false,
|
||||
}) {
|
||||
const newId = uuidv4()
|
||||
|
||||
filter.order = this.muteFilters.length + 2
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
exportFilter(id) {
|
||||
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
||||
delete this.exportedFilter.order
|
||||
this.filterExporter.exportData()
|
||||
},
|
||||
importFilter() {
|
||||
this.filterImporter.importData()
|
||||
},
|
||||
copyFilter (id) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
const newId = uuidv4()
|
||||
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
deleteFilter (id) {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
purgeExpiredFilters () {
|
||||
this.muteFiltersExpired.forEach(([id]) => {
|
||||
console.log(id)
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
updateFilter(id, field, value) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
if (field === 'expires-never') {
|
||||
if (!value) {
|
||||
const offset = 1000 * 60 * 60 * 24 * 14 // 2 weeks
|
||||
const date = Date.now() + offset
|
||||
filter.expires = date
|
||||
} else {
|
||||
filter.expires = null
|
||||
}
|
||||
} else if (field === 'expires') {
|
||||
const parsed = Date.parse(value)
|
||||
filter.expires = parsed.valueOf()
|
||||
} else {
|
||||
filter[field] = value
|
||||
}
|
||||
this.muteFiltersDraftObject[id] = filter
|
||||
this.muteFiltersDraftDirty[id] = true
|
||||
},
|
||||
saveFilter(id) {
|
||||
this.setPreference({ path: 'simple.muteFilters.' + id , value: this.muteFiltersDraftObject[id] })
|
||||
this.pushServerSideStorage()
|
||||
this.muteFiltersDraftDirty[id] = false
|
||||
},
|
||||
},
|
||||
// Updating nested properties
|
||||
watch: {
|
||||
replyVisibility () {
|
||||
|
|
|
|||
89
src/components/settings_modal/tabs/filtering_tab.scss
Normal file
89
src/components/settings_modal/tabs/filtering_tab.scss
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
.filtering-tab {
|
||||
.muteFilterContainer {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--roundness);
|
||||
height: 33vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mute-filter {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--roundness);
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
display: grid;
|
||||
align-items: baseline;
|
||||
grid-gap: 0.5em;
|
||||
}
|
||||
|
||||
.never {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.filter-name {
|
||||
grid-column: 1 / span 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.alert,
|
||||
.button-default {
|
||||
display: inline-block;
|
||||
line-height: 2;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.filter-enabled {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
text-align: right;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.filter-field {
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
grid-template-rows: subgrid;
|
||||
grid-column: 1 / span 3;
|
||||
align-items: baseline;
|
||||
|
||||
label {
|
||||
grid-column: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.filter-field-value {
|
||||
grid-column: 2 / span 2;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
grid-column: 1 / span 3;
|
||||
justify-self: end;
|
||||
display: inline-grid;
|
||||
grid-gap: 0.5em;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
max-width: 100%;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.muteFiltersActions,
|
||||
.muteFiltersActionsBottom {
|
||||
display: grid;
|
||||
align-items: baseline;
|
||||
grid-auto-flow: column;
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
|
||||
.total {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.settings-modal.-mobile .filtering-tab {
|
||||
.filter-buttons {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,88 @@
|
|||
<template>
|
||||
<div :label="$t('settings.filtering')">
|
||||
<div
|
||||
:label="$t('settings.filtering')"
|
||||
class="filtering-tab"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.posts') }}</h2>
|
||||
<h2>{{ $t('settings.filter.clutter') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
v-if="user"
|
||||
id="replyVisibility"
|
||||
path="replyVisibility"
|
||||
:options="replyVisibilityOptions"
|
||||
>
|
||||
{{ $t('settings.replies_in_timeline') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
expert="1"
|
||||
path="hidePostStats"
|
||||
>
|
||||
{{ $t('settings.hide_post_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
expert="1"
|
||||
path="hideUserStats"
|
||||
>
|
||||
{{ $t('settings.hide_user_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideBotIndication">
|
||||
{{ $t('settings.hide_actor_type_indication') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideScrobbles">
|
||||
{{ $t('settings.hide_scrobbles') }}
|
||||
</BooleanSetting>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<UnitSetting
|
||||
key="hideScrobblesAfter"
|
||||
path="hideScrobblesAfter"
|
||||
:units="['m', 'h', 'd']"
|
||||
unit-set="time"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_scrobbles_after') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path="maxThumbnails"
|
||||
expert="1"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.max_thumbnails') }}
|
||||
</IntegerSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachments">
|
||||
{{ $t('settings.hide_attachments_in_tl') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachmentsInConv">
|
||||
{{ $t('settings.hide_attachments_in_convo') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.filter.mute_filter') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="hideFilteredStatuses">
|
||||
{{ $t('settings.hide_filtered_statuses') }}
|
||||
{{ $t('settings.hide_muted_statuses') }}
|
||||
</BooleanSetting>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
|
|
@ -13,8 +90,9 @@
|
|||
parent-path="hideFilteredStatuses"
|
||||
:parent-invert="true"
|
||||
path="hideWordFilteredPosts"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_wordfiltered_statuses') }}
|
||||
{{ $t('settings.hide_filtered_statuses') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -50,83 +128,218 @@
|
|||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hidePostStats">
|
||||
{{ $t('settings.hide_post_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideBotIndication">
|
||||
{{ $t('settings.hide_actor_type_indication') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ChoiceSetting
|
||||
v-if="user"
|
||||
id="replyVisibility"
|
||||
path="replyVisibility"
|
||||
:options="replyVisibilityOptions"
|
||||
>
|
||||
{{ $t('settings.replies_in_timeline') }}
|
||||
</ChoiceSetting>
|
||||
<li>
|
||||
<h3>{{ $t('settings.wordfilter') }}</h3>
|
||||
<textarea
|
||||
id="muteWords"
|
||||
v-model="muteWordsString"
|
||||
class="input resize-height"
|
||||
/>
|
||||
<div>{{ $t('settings.filtering_explanation') }}</div>
|
||||
</li>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path="maxThumbnails"
|
||||
expert="1"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.max_thumbnails') }}
|
||||
</IntegerSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachments">
|
||||
{{ $t('settings.hide_attachments_in_tl') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachmentsInConv">
|
||||
{{ $t('settings.hide_attachments_in_convo') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideScrobbles">
|
||||
{{ $t('settings.hide_scrobbles') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<UnitSetting
|
||||
key="hideScrobblesAfter"
|
||||
path="hideScrobblesAfter"
|
||||
:units="['m', 'h', 'd']"
|
||||
unit-set="time"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_scrobbles_after') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
v-if="expertLevel > 0"
|
||||
class="setting-item"
|
||||
>
|
||||
<h2>{{ $t('settings.user_profiles') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="hideUserStats">
|
||||
{{ $t('settings.hide_user_stats') }}
|
||||
</BooleanSetting>
|
||||
<h3>{{ $t('settings.filter.custom_filters') }}</h3>
|
||||
<p class="muteFiltersActions">
|
||||
<span class="total">
|
||||
{{ $t('settings.filter.total_count', { count: muteFiltersDraft.length }) }}
|
||||
</span>
|
||||
<button
|
||||
class="add-button button-default"
|
||||
type="button"
|
||||
@click="createFilter()"
|
||||
>
|
||||
{{ $t('settings.filter.new') }}
|
||||
</button>
|
||||
<button
|
||||
class="add-button button-default"
|
||||
type="button"
|
||||
@click="importFilter()"
|
||||
>
|
||||
{{ $t('settings.filter.import') }}
|
||||
</button>
|
||||
</p>
|
||||
<div class="muteFilterContainer">
|
||||
<div
|
||||
v-for="filter in muteFiltersDraft"
|
||||
:key="filter[0]"
|
||||
class="mute-filter"
|
||||
:style="{ order: filter[1].order }"
|
||||
>
|
||||
<div class="filter-name">
|
||||
<label
|
||||
:for="'filterName' + filter[0]"
|
||||
>
|
||||
{{ $t('settings.filter.name') }}
|
||||
</label>
|
||||
{{ ' ' }}
|
||||
<input
|
||||
:id="'filterName' + filter[0]"
|
||||
class="input"
|
||||
:value="filter[1].name"
|
||||
@input="updateFilter(filter[0], 'name', $event.target.value)"
|
||||
>
|
||||
<span
|
||||
v-if="filter[1].expires !== null && Date.now() > filter[1].expires"
|
||||
class="alert neutral"
|
||||
>
|
||||
{{ $t('settings.filter.expired') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="filter-enabled">
|
||||
<Checkbox
|
||||
:id="'filterHide' + filter[0]"
|
||||
:model-value="filter[1].hide"
|
||||
:name="'filterHide' + filter[0]"
|
||||
@update:model-value="updateFilter(filter[0], 'hide', $event)"
|
||||
>
|
||||
{{ $t('settings.filter.hide') }}
|
||||
</Checkbox>
|
||||
{{ ' ' }}
|
||||
<Checkbox
|
||||
:id="'filterEnabled' + filter[0]"
|
||||
:model-value="filter[1].enabled"
|
||||
:name="'filterEnabled' + filter[0]"
|
||||
@update:model-value="updateFilter(filter[0], 'enabled', $event)"
|
||||
>
|
||||
{{ $t('settings.enabled') }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div class="filter-type filter-field">
|
||||
<label :for="'filterType' + filter[0]">
|
||||
<HelpIndicator>
|
||||
<p>
|
||||
{{ $t('settings.filter.help.word') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('settings.filter.help.user') }}
|
||||
</p>
|
||||
<i18n-t
|
||||
keypath="settings.filter.help.regexp"
|
||||
tag="p"
|
||||
>
|
||||
<template #link>
|
||||
<a
|
||||
:href="$t('settings.filter.help.regexp_url')"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('settings.filter.help.regexp_link') }}
|
||||
</a>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</HelpIndicator>
|
||||
{{ $t('settings.filter.type') }}
|
||||
</label>
|
||||
<Select
|
||||
:id="'filterType' + filter[0]"
|
||||
class="filter-field-value"
|
||||
:model-value="filter[1].type"
|
||||
@update:model-value="updateFilter(filter[0], 'type', $event)"
|
||||
>
|
||||
<option value="word">
|
||||
{{ $t('settings.filter.plain') }}
|
||||
</option>
|
||||
<option value="regexp">
|
||||
{{ $t('settings.filter.regexp') }}
|
||||
</option>
|
||||
<option value="user">
|
||||
{{ $t('settings.filter.user') }}
|
||||
</option>
|
||||
<option value="user_regexp">
|
||||
{{ $t('settings.filter.user_regexp') }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="filter-value filter-field">
|
||||
<label
|
||||
:for="'filterValue' + filter[0]"
|
||||
>
|
||||
{{ $t('settings.filter.value') }}
|
||||
</label>
|
||||
{{ ' ' }}
|
||||
<input
|
||||
:id="'filterValue' + filter[0]"
|
||||
class="input filter-field-value"
|
||||
:value="filter[1].value"
|
||||
@input="updateFilter(filter[0], 'value', $event.target.value)"
|
||||
>
|
||||
</div>
|
||||
<div class="filter-expires filter-field">
|
||||
<label
|
||||
:for="'filterExpires' + filter[0]"
|
||||
>
|
||||
{{ $t('settings.filter.expires') }}
|
||||
</label>
|
||||
{{ ' ' }}
|
||||
<div class="filter-field-value">
|
||||
<input
|
||||
:id="'filterExpires' + filter[0]"
|
||||
class="input"
|
||||
:class="{ disabled: filter[1].expires === null }"
|
||||
type="datetime-local"
|
||||
:disabled="filter[1].expires === null"
|
||||
:value="filter[1].expires ? getDatetimeLocal(filter[1].expires) : null"
|
||||
@input="updateFilter(filter[0], 'expires', $event.target.value)"
|
||||
>
|
||||
{{ ' ' }}
|
||||
<Checkbox
|
||||
:id="'filterExpiresNever' + filter[0]"
|
||||
:model-value="filter[1].expires === null"
|
||||
:name="'filterExpiresNever' + filter[0]"
|
||||
class="input-inset input-boolean never"
|
||||
@update:model-value="updateFilter(filter[0], 'expires-never', $event)"
|
||||
>
|
||||
{{ $t('settings.filter.never_expires') }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!checkRegexValid(filter[0])"
|
||||
class="alert error"
|
||||
>
|
||||
{{ $t('settings.filter.regexp_error') }}
|
||||
</div>
|
||||
<div class="filter-buttons">
|
||||
<button
|
||||
class="delete-button button-default -danger"
|
||||
type="button"
|
||||
@click="deleteFilter(filter[0])"
|
||||
>
|
||||
{{ $t('settings.filter.delete') }}
|
||||
</button>
|
||||
<button
|
||||
class="export-button button-default"
|
||||
type="button"
|
||||
@click="exportFilter(filter[0])"
|
||||
>
|
||||
{{ $t('settings.filter.export') }}
|
||||
</button>
|
||||
<button
|
||||
class="copy-button button-default"
|
||||
type="button"
|
||||
@click="copyFilter(filter[0])"
|
||||
>
|
||||
{{ $t('settings.filter.copy') }}
|
||||
</button>
|
||||
<button
|
||||
class="save-button button-default"
|
||||
:class="{ disabled: !muteFiltersDraftDirty[filter[0]] }"
|
||||
:disabled="!muteFiltersDraftDirty[filter[0]]"
|
||||
type="button"
|
||||
@click="saveFilter(filter[0])"
|
||||
>
|
||||
{{ $t('settings.filter.save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="muteFiltersActionsBottom">
|
||||
<span class="total">
|
||||
{{ $t('settings.filter.expired_count', { count: muteFiltersExpired.length }) }}
|
||||
</span>
|
||||
<button
|
||||
class="add-button button-default"
|
||||
type="button"
|
||||
:class="{ disabled: muteFiltersExpired.length === 0 }"
|
||||
:disabled="muteFiltersExpired.length === 0"
|
||||
@click="purgeExpiredFilters()"
|
||||
>
|
||||
{{ $t('settings.filter.purge_expired') }}
|
||||
</button>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script src="./filtering_tab.js"></script>
|
||||
<style src="./filtering_tab.scss"></style>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
|||
import withSubscription from 'src/components/../hocs/with_subscription/with_subscription'
|
||||
import withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
||||
|
||||
const BlockList = withLoadMore({
|
||||
fetch: (props, $store) => $store.dispatch('fetchBlocks'),
|
||||
|
|
@ -39,7 +40,7 @@ const MutesAndBlocks = {
|
|||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
useOAuthTokensStore().fetchTokens()
|
||||
this.$store.dispatch('getKnownDomains')
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ const ProfileTab = {
|
|||
this.submitBackground('')
|
||||
}
|
||||
},
|
||||
submitAvatar (cropper, file) {
|
||||
submitAvatar (canvas, file) {
|
||||
const that = this
|
||||
return new Promise((resolve, reject) => {
|
||||
function updateAvatar (avatar, avatarName) {
|
||||
|
|
@ -232,8 +232,8 @@ const ProfileTab = {
|
|||
})
|
||||
}
|
||||
|
||||
if (cropper) {
|
||||
cropper.getCroppedCanvas().toBlob((data) => updateAvatar(data, file.name), file.type)
|
||||
if (canvas) {
|
||||
canvas.toBlob((data) => updateAvatar(data, file.name), file.type)
|
||||
} else {
|
||||
updateAvatar(file, file.name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
|||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Mfa from './mfa.vue'
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
||||
|
||||
const SecurityTab = {
|
||||
data () {
|
||||
|
|
@ -28,7 +29,7 @@ const SecurityTab = {
|
|||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
useOAuthTokensStore().fetchTokens()
|
||||
this.fetchAliases()
|
||||
},
|
||||
components: {
|
||||
|
|
@ -40,11 +41,11 @@ const SecurityTab = {
|
|||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
pleromaBackend () {
|
||||
return this.$store.state.instance.pleromaBackend
|
||||
pleromaExtensionsAvailable () {
|
||||
return this.$store.state.instance.pleromaExtensionsAvailable
|
||||
},
|
||||
oauthTokens () {
|
||||
return this.$store.state.oauthTokens.tokens.map(oauthToken => {
|
||||
return useOAuthTokensStore().tokens.map(oauthToken => {
|
||||
return {
|
||||
id: oauthToken.id,
|
||||
appName: oauthToken.app_name,
|
||||
|
|
@ -151,7 +152,7 @@ const SecurityTab = {
|
|||
},
|
||||
revokeToken (id) {
|
||||
if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
|
||||
this.$store.dispatch('revokeToken', id)
|
||||
useOAuthTokensStore().revokeToken(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ export default {
|
|||
parser (string) { return deserialize(string) },
|
||||
onImportFailure (result) {
|
||||
console.error('Failure importing style:', result)
|
||||
this.$store.useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
},
|
||||
onImport
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
.StyleTab {
|
||||
min-width: var(--themeEditorMinWidth, fit-content);
|
||||
|
||||
.style-control {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -185,7 +187,7 @@
|
|||
.state-selector,
|
||||
.variant-selector {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr minmax(1fr, 10em);
|
||||
grid-template-columns: 1fr minmax(10em, 1fr);
|
||||
grid-template-rows: auto;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 0.5em;
|
||||
|
|
|
|||
|
|
@ -241,10 +241,7 @@ export default {
|
|||
|
||||
.underlay-preview {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
inset: 0 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
.theme-tab {
|
||||
min-width: var(--themeEditorMinWidth, fit-content);
|
||||
|
||||
.deprecation-warning {
|
||||
padding: 0.5em;
|
||||
margin: 2em;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue