aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/export_import/export_import.vue102
-rw-r--r--src/components/global_notice_list/global_notice_list.vue8
-rw-r--r--src/components/moderation_tools/moderation_tools.js5
-rw-r--r--src/components/moderation_tools/moderation_tools.vue9
-rw-r--r--src/components/nav_panel/nav_panel.js36
-rw-r--r--src/components/nav_panel/nav_panel.vue70
-rw-r--r--src/components/notifications/notification_filters.vue122
-rw-r--r--src/components/notifications/notifications.js13
-rw-r--r--src/components/notifications/notifications.scss7
-rw-r--r--src/components/notifications/notifications.vue11
-rw-r--r--src/components/password_reset/password_reset.vue2
-rw-r--r--src/components/popover/popover.js7
-rw-r--r--src/components/post_status_form/post_status_form.vue4
-rw-r--r--src/components/registration/registration.vue2
-rw-r--r--src/components/settings_modal/settings_modal.js124
-rw-r--r--src/components/settings_modal/settings_modal.vue70
-rw-r--r--src/components/settings_modal/tabs/notifications_tab.vue2
-rw-r--r--src/components/settings_modal/tabs/profile_tab.vue6
-rw-r--r--src/components/settings_modal/tabs/security_tab/security_tab.vue6
-rw-r--r--src/components/settings_modal/tabs/theme_tab/theme_tab.js20
-rw-r--r--src/components/settings_modal/tabs/theme_tab/theme_tab.vue42
-rw-r--r--src/components/timeline/timeline.scss31
-rw-r--r--src/components/timeline/timeline.vue38
-rw-r--r--src/components/timeline/timeline_quick_settings.js4
-rw-r--r--src/components/timeline/timeline_quick_settings.vue8
-rw-r--r--src/components/timeline_menu/timeline_menu.js31
-rw-r--r--src/components/timeline_menu/timeline_menu.vue48
-rw-r--r--src/components/timeline_menu/timeline_menu_content.js29
-rw-r--r--src/components/timeline_menu/timeline_menu_content.vue66
29 files changed, 631 insertions, 292 deletions
diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue
deleted file mode 100644
index 8ffe34f8..00000000
--- a/src/components/export_import/export_import.vue
+++ /dev/null
@@ -1,102 +0,0 @@
-<template>
- <div class="import-export-container">
- <slot name="before" />
- <button
- class="btn button-default"
- @click="exportData"
- >
- {{ exportLabel }}
- </button>
- <button
- class="btn button-default"
- @click="importData"
- >
- {{ importLabel }}
- </button>
- <slot name="afterButtons" />
- <p
- v-if="importFailed"
- class="alert error"
- >
- {{ importFailedText }}
- </p>
- <slot name="afterError" />
- </div>
-</template>
-
-<script>
-export default {
- props: [
- 'exportObject',
- 'importLabel',
- 'exportLabel',
- 'importFailedText',
- 'validator',
- 'onImport',
- 'onImportFailure'
- ],
- data () {
- return {
- importFailed: false
- }
- },
- methods: {
- exportData () {
- const stringified = JSON.stringify(this.exportObject, null, 2) // Pretty-print and indent with 2 spaces
-
- // Create an invisible link with a data url and simulate a click
- const e = document.createElement('a')
- e.setAttribute('download', 'pleroma_theme.json')
- e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified))
- e.style.display = 'none'
-
- document.body.appendChild(e)
- e.click()
- document.body.removeChild(e)
- },
- importData () {
- this.importFailed = false
- const filePicker = document.createElement('input')
- filePicker.setAttribute('type', 'file')
- filePicker.setAttribute('accept', '.json')
-
- filePicker.addEventListener('change', event => {
- if (event.target.files[0]) {
- // eslint-disable-next-line no-undef
- const reader = new FileReader()
- reader.onload = ({ target }) => {
- try {
- const parsed = JSON.parse(target.result)
- const valid = this.validator(parsed)
- if (valid) {
- this.onImport(parsed)
- } else {
- this.importFailed = true
- // this.onImportFailure(valid)
- }
- } catch (e) {
- // This will happen both if there is a JSON syntax error or the theme is missing components
- this.importFailed = true
- // this.onImportFailure(e)
- }
- }
- reader.readAsText(event.target.files[0])
- }
- })
-
- document.body.appendChild(filePicker)
- filePicker.click()
- document.body.removeChild(filePicker)
- }
- }
-}
-</script>
-
-<style lang="scss">
-.import-export-container {
- display: flex;
- flex-wrap: wrap;
- align-items: baseline;
- justify-content: center;
-}
-</style>
diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue
index 049e23db..a45f4586 100644
--- a/src/components/global_notice_list/global_notice_list.vue
+++ b/src/components/global_notice_list/global_notice_list.vue
@@ -71,6 +71,14 @@
}
}
+ .global-success {
+ background-color: var(--alertPopupSuccess, $fallback--cGreen);
+ color: var(--alertPopupSuccessText, $fallback--text);
+ .svg-inline--fa {
+ color: var(--alertPopupSuccessText, $fallback--text);
+ }
+ }
+
.global-info {
background-color: var(--alertPopupNeutral, $fallback--fg);
color: var(--alertPopupNeutralText, $fallback--text);
diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js
index d4fdc53e..2469327a 100644
--- a/src/components/moderation_tools/moderation_tools.js
+++ b/src/components/moderation_tools/moderation_tools.js
@@ -1,6 +1,11 @@
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
+
import DialogModal from '../dialog_modal/dialog_modal.vue'
import Popover from '../popover/popover.vue'
+library.add(faChevronDown)
+
const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
const STRIP_MEDIA = 'mrf_tag:media-strip'
const FORCE_UNLISTED = 'mrf_tag:force-unlisted'
diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue
index bf25adc5..c4c6ee46 100644
--- a/src/components/moderation_tools/moderation_tools.vue
+++ b/src/components/moderation_tools/moderation_tools.vue
@@ -124,10 +124,11 @@
</div>
<button
slot="trigger"
- class="btn button-default btn-block"
+ class="btn button-default btn-block moderation-tools-button"
:class="{ toggled }"
>
{{ $t('user_card.admin_menu.moderation') }}
+ <FAIcon icon="chevron-down" />
</button>
</Popover>
<portal to="modal">
@@ -170,4 +171,10 @@
height: 100%;
}
}
+
+.moderation-tools-button {
+ svg,i {
+ font-size: 0.8em;
+ }
+}
</style>
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 81d49cc2..37bcb409 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -1,4 +1,4 @@
-import { timelineNames } from '../timeline_menu/timeline_menu.js'
+import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue'
import { mapState, mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
@@ -7,10 +7,12 @@ import {
faGlobe,
faBookmark,
faEnvelope,
- faHome,
+ faChevronDown,
+ faChevronUp,
faComments,
faBell,
- faInfoCircle
+ faInfoCircle,
+ faStream
} from '@fortawesome/free-solid-svg-icons'
library.add(
@@ -18,10 +20,12 @@ library.add(
faGlobe,
faBookmark,
faEnvelope,
- faHome,
+ faChevronDown,
+ faChevronUp,
faComments,
faBell,
- faInfoCircle
+ faInfoCircle,
+ faStream
)
const NavPanel = {
@@ -30,16 +34,20 @@ const NavPanel = {
this.$store.dispatch('startFetchingFollowRequests')
}
},
+ components: {
+ TimelineMenuContent
+ },
+ data () {
+ return {
+ showTimelines: false
+ }
+ },
+ methods: {
+ toggleTimelines () {
+ this.showTimelines = !this.showTimelines
+ }
+ },
computed: {
- onTimelineRoute () {
- return !!timelineNames()[this.$route.name]
- },
- timelinesRoute () {
- if (this.$store.state.interface.lastTimeline) {
- return this.$store.state.interface.lastTimeline
- }
- return this.currentUser ? 'friends' : 'public-timeline'
- },
...mapState({
currentUser: state => state.users.currentUser,
followRequestCount: state => state.api.followRequests.length,
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 0c83d0fe..7ae7b1d6 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -3,19 +3,33 @@
<div class="panel panel-default">
<ul>
<li v-if="currentUser || !privateMode">
- <router-link
- :to="{ name: timelinesRoute }"
- :class="onTimelineRoute && 'router-link-active'"
+ <button
+ class="button-unstyled menu-item"
+ @click="toggleTimelines"
>
<FAIcon
fixed-width
class="fa-scale-110"
- icon="home"
+ icon="stream"
/>{{ $t("nav.timelines") }}
- </router-link>
+ <FAIcon
+ class="timelines-chevron"
+ fixed-width
+ :icon="showTimelines ? 'chevron-up' : 'chevron-down'"
+ />
+ </button>
+ <div
+ v-show="showTimelines"
+ class="timelines-background"
+ >
+ <TimelineMenuContent class="timelines" />
+ </div>
</li>
<li v-if="currentUser">
- <router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'interactions', params: { username: currentUser.screen_name } }"
+ >
<FAIcon
fixed-width
class="fa-scale-110"
@@ -24,7 +38,10 @@
</router-link>
</li>
<li v-if="currentUser && pleromaChatMessagesAvailable">
- <router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'chats', params: { username: currentUser.screen_name } }"
+ >
<div
v-if="unreadChatCount"
class="badge badge-notification"
@@ -39,7 +56,10 @@
</router-link>
</li>
<li v-if="currentUser && currentUser.locked">
- <router-link :to="{ name: 'friend-requests' }">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'friend-requests' }"
+ >
<FAIcon
fixed-width
class="fa-scale-110"
@@ -54,7 +74,10 @@
</router-link>
</li>
<li>
- <router-link :to="{ name: 'about' }">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'about' }"
+ >
<FAIcon
fixed-width
class="fa-scale-110"
@@ -91,14 +114,14 @@
border-color: var(--border, $fallback--border);
padding: 0;
- &:first-child a {
+ &:first-child .menu-item {
border-top-right-radius: $fallback--panelRadius;
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
border-top-left-radius: $fallback--panelRadius;
border-top-left-radius: var(--panelRadius, $fallback--panelRadius);
}
- &:last-child a {
+ &:last-child .menu-item {
border-bottom-right-radius: $fallback--panelRadius;
border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius);
border-bottom-left-radius: $fallback--panelRadius;
@@ -110,13 +133,15 @@
border: none;
}
- a {
+ .menu-item {
display: block;
box-sizing: border-box;
- align-items: stretch;
height: 3.5em;
line-height: 3.5em;
padding: 0 1em;
+ width: 100%;
+ color: $fallback--link;
+ color: var(--link, $fallback--link);
&:hover {
background-color: $fallback--lightBg;
@@ -146,6 +171,25 @@
}
}
+ .timelines-chevron {
+ margin-left: 0.8em;
+ font-size: 1.1em;
+ }
+
+ .timelines-background {
+ padding: 0 0 0 0.6em;
+ background-color: $fallback--lightBg;
+ background-color: var(--selectedMenu, $fallback--lightBg);
+ border-top: 1px solid;
+ border-color: $fallback--border;
+ border-color: var(--border, $fallback--border);
+ }
+
+ .timelines {
+ background-color: $fallback--bg;
+ background-color: var(--bg, $fallback--bg);
+ }
+
.fa-scale-110 {
margin-right: 0.8em;
}
diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue
new file mode 100644
index 00000000..e86a0fcc
--- /dev/null
+++ b/src/components/notifications/notification_filters.vue
@@ -0,0 +1,122 @@
+<template>
+ <Popover
+ trigger="click"
+ class="NotificationFilters"
+ placement="bottom"
+ :bound-to="{ x: 'container' }"
+ >
+ <template
+ v-slot:content
+ >
+ <div class="dropdown-menu">
+ <button
+ class="button-default dropdown-item"
+ @click="toggleNotificationFilter('likes')"
+ >
+ <span
+ class="menu-checkbox"
+ :class="{ 'menu-checkbox-checked': filters.likes }"
+ />{{ $t('settings.notification_visibility_likes') }}
+ </button>
+ <button
+ class="button-default dropdown-item"
+ @click="toggleNotificationFilter('repeats')"
+ >
+ <span
+ class="menu-checkbox"
+ :class="{ 'menu-checkbox-checked': filters.repeats }"
+ />{{ $t('settings.notification_visibility_repeats') }}
+ </button>
+ <button
+ class="button-default dropdown-item"
+ @click="toggleNotificationFilter('follows')"
+ >
+ <span
+ class="menu-checkbox"
+ :class="{ 'menu-checkbox-checked': filters.follows }"
+ />{{ $t('settings.notification_visibility_follows') }}
+ </button>
+ <button
+ class="button-default dropdown-item"
+ @click="toggleNotificationFilter('mentions')"
+ >
+ <span
+ class="menu-checkbox"
+ :class="{ 'menu-checkbox-checked': filters.mentions }"
+ />{{ $t('settings.notification_visibility_mentions') }}
+ </button>
+ <button
+ class="button-default dropdown-item"
+ @click="toggleNotificationFilter('emojiReactions')"
+ >
+ <span
+ class="menu-checkbox"
+ :class="{ 'menu-checkbox-checked': filters.emojiReactions }"
+ />{{ $t('settings.notification_visibility_emoji_reactions') }}
+ </button>
+ <button
+ class="button-default dropdown-item"
+ @click="toggleNotificationFilter('moves')"
+ >
+ <span
+ class="menu-checkbox"
+ :class="{ 'menu-checkbox-checked': filters.moves }"
+ />{{ $t('settings.notification_visibility_moves') }}
+ </button>
+ </div>
+ </template>
+ <template v-slot:trigger>
+ <FAIcon icon="filter" />
+ </template>
+ </Popover>
+</template>
+
+<script>
+import Popover from '../popover/popover.vue'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { faFilter } from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faFilter
+)
+
+export default {
+ components: { Popover },
+ computed: {
+ filters () {
+ return this.$store.getters.mergedConfig.notificationVisibility
+ }
+ },
+ methods: {
+ toggleNotificationFilter (type) {
+ this.$store.dispatch('setOption', {
+ name: 'notificationVisibility',
+ value: {
+ ...this.filters,
+ [type]: !this.filters[type]
+ }
+ })
+ }
+ }
+}
+</script>
+
+<style lang="scss">
+
+.NotificationFilters {
+ align-self: stretch;
+
+ > button {
+ font-size: 1.2em;
+ padding-left: 0.7em;
+ padding-right: 0.2em;
+ line-height: 100%;
+ height: 100%;
+ }
+
+ .dropdown-item {
+ margin: 0;
+ }
+}
+
+</style>
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 49258563..c8f1ebcb 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -1,5 +1,6 @@
import { mapGetters } from 'vuex'
import Notification from '../notification/notification.vue'
+import NotificationFilters from './notification_filters.vue'
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
import {
notificationsFromStore,
@@ -17,6 +18,10 @@ library.add(
const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30
const Notifications = {
+ components: {
+ Notification,
+ NotificationFilters
+ },
props: {
// Disables display of panel header
noHeading: Boolean,
@@ -35,11 +40,6 @@ const Notifications = {
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
}
},
- created () {
- const store = this.$store
- const credentials = store.state.users.currentUser.credentials
- notificationsFetcher.fetchAndUpdate({ store, credentials })
- },
computed: {
mainClass () {
return this.minimalMode ? '' : 'panel panel-default'
@@ -70,9 +70,6 @@ const Notifications = {
},
...mapGetters(['unreadChatCount'])
},
- components: {
- Notification
- },
watch: {
unseenCountTitle (count) {
if (count > 0) {
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index 682ae127..2bb627a8 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -1,6 +1,6 @@
@import '../../_variables.scss';
-.notifications {
+.Notifications {
&:not(.minimal) {
// a bit of a hack to allow scrolling below notifications
padding-bottom: 15em;
@@ -11,6 +11,10 @@
color: var(--text, $fallback--text);
}
+ .notifications-footer {
+ border: none;
+ }
+
.notification {
position: relative;
@@ -82,7 +86,6 @@
}
}
-
.follow-text, .move-text {
padding: 0.5em 0;
overflow-wrap: break-word;
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index 725d1ad4..2ce5d56f 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -1,7 +1,7 @@
<template>
<div
:class="{ minimal: minimalMode }"
- class="notifications"
+ class="Notifications"
>
<div :class="mainClass">
<div
@@ -22,6 +22,7 @@
>
{{ $t('notifications.read') }}
</button>
+ <NotificationFilters />
</div>
<div class="panel-body">
<div
@@ -34,10 +35,10 @@
<notification :notification="notification" />
</div>
</div>
- <div class="panel-footer">
+ <div class="panel-footer notifications-footer">
<div
v-if="bottomedOut"
- class="new-status-notification text-center panel-footer faint"
+ class="new-status-notification text-center faint"
>
{{ $t('notifications.no_more_notifications') }}
</div>
@@ -46,13 +47,13 @@
class="button-unstyled -link -fullwidth"
@click.prevent="fetchOlderNotifications()"
>
- <div class="new-status-notification text-center panel-footer">
+ <div class="new-status-notification text-center">
{{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }}
</div>
</button>
<div
v-else
- class="new-status-notification text-center panel-footer"
+ class="new-status-notification text-center"
>
<FAIcon
icon="circle-notch"
diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue
index a931cb5a..3ffa5425 100644
--- a/src/components/password_reset/password_reset.vue
+++ b/src/components/password_reset/password_reset.vue
@@ -53,7 +53,7 @@
type="submit"
class="btn button-default btn-block"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index 391d6422..bc6cd8e7 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -56,6 +56,9 @@ const Popover = {
// Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one slot="trigger".
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
+ // SVGs don't have offsetWidth/Height, use fallback
+ const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
+ const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
const screenBox = anchorEl.getBoundingClientRect()
// Screen position of the origin point for popover
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }
@@ -114,11 +117,11 @@ const Popover = {
const yOffset = (this.offset && this.offset.y) || 0
const translateY = usingTop
- ? -anchorEl.offsetHeight + vPadding - yOffset - content.offsetHeight
+ ? -anchorHeight + vPadding - yOffset - content.offsetHeight
: yOffset
const xOffset = (this.offset && this.offset.x) || 0
- const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset
+ const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset
// Note, separate translateX and translateY avoids blurry text on chromium,
// single translate or translate3d resulted in blurry text.
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index cc4f6251..fbda41d6 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -263,7 +263,7 @@
disabled
class="btn button-default"
>
- {{ $t('general.submit') }}
+ {{ $t('post_status.post') }}
</button>
<!-- touchstart is used to keep the OSK at the same position after a message send -->
<button
@@ -273,7 +273,7 @@
@touchstart.stop.prevent="postStatus($event, newStatus)"
@click.stop.prevent="postStatus($event, newStatus)"
>
- {{ $t('general.submit') }}
+ {{ $t('post_status.post') }}
</button>
</div>
<div
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index 062d4121..65b4bb33 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -230,7 +230,7 @@
type="submit"
class="btn button-default"
>
- {{ $t('general.submit') }}
+ {{ $t('registration.register') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js
index f0d49c91..04043483 100644
--- a/src/components/settings_modal/settings_modal.js
+++ b/src/components/settings_modal/settings_modal.js
@@ -2,10 +2,55 @@ import Modal from 'src/components/modal/modal.vue'
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue'
import getResettableAsyncComponent from 'src/services/resettable_async_component.js'
+import Popover from '../popover/popover.vue'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { cloneDeep } from 'lodash'
+import {
+ newImporter,
+ newExporter
+} from 'src/services/export_import/export_import.js'
+import {
+ faTimes,
+ faFileUpload,
+ faFileDownload,
+ faChevronDown
+} from '@fortawesome/free-solid-svg-icons'
+import {
+ faWindowMinimize
+} from '@fortawesome/free-regular-svg-icons'
+
+const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1
+const PLEROMAFE_SETTINGS_MINOR_VERSION = 0
+
+library.add(
+ faTimes,
+ faWindowMinimize,
+ faFileUpload,
+ faFileDownload,
+ faChevronDown
+)
const SettingsModal = {
+ data () {
+ return {
+ dataImporter: newImporter({
+ validator: this.importValidator,
+ onImport: this.onImport,
+ onImportFailure: this.onImportFailure
+ }),
+ dataThemeExporter: newExporter({
+ filename: 'pleromafe_settings.full',
+ getExportedObject: () => this.generateExport(true)
+ }),
+ dataExporter: newExporter({
+ filename: 'pleromafe_settings',
+ getExportedObject: () => this.generateExport()
+ })
+ }
+ },
components: {
Modal,
+ Popover,
SettingsModalContent: getResettableAsyncComponent(
() => import('./settings_modal_content.vue'),
{
@@ -21,6 +66,85 @@ const SettingsModal = {
},
peekModal () {
this.$store.dispatch('togglePeekSettingsModal')
+ },
+ importValidator (data) {
+ if (!Array.isArray(data._pleroma_settings_version)) {
+ return {
+ messageKey: 'settings.file_import_export.invalid_file'
+ }
+ }
+
+ const [major, minor] = data._pleroma_settings_version
+
+ if (major > PLEROMAFE_SETTINGS_MAJOR_VERSION) {
+ return {
+ messageKey: 'settings.file_export_import.errors.file_too_new',
+ messageArgs: {
+ fileMajor: major,
+ feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION
+ }
+ }
+ }
+
+ if (major < PLEROMAFE_SETTINGS_MAJOR_VERSION) {
+ return {
+ messageKey: 'settings.file_export_import.errors.file_too_old',
+ messageArgs: {
+ fileMajor: major,
+ feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION
+ }
+ }
+ }
+
+ if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) {
+ this.$store.dispatch('pushGlobalNotice', {
+ level: 'warning',
+ messageKey: 'settings.file_export_import.errors.file_slightly_new'
+ })
+ }
+
+ return true
+ },
+ onImportFailure (result) {
+ if (result.error) {
+ this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_settings_imported', level: 'error' })
+ } else {
+ this.$store.dispatch('pushGlobalNotice', { ...result.validationResult, level: 'error' })
+ }
+ },
+ onImport (data) {
+ if (data) { this.$store.dispatch('loadSettings', data) }
+ },
+ restore () {
+ this.dataImporter.importData()
+ },
+ backup () {
+ this.dataExporter.exportData()
+ },
+ backupWithTheme () {
+ this.dataThemeExporter.exportData()
+ },
+ generateExport (theme = false) {
+ const { config } = this.$store.state
+ let sample = config
+ if (!theme) {
+ const ignoreList = new Set([
+ 'customTheme',
+ 'customThemeSource',
+ 'colors'
+ ])
+ sample = Object.fromEntries(
+ Object
+ .entries(sample)
+ .filter(([key]) => !ignoreList.has(key))
+ )
+ }
+ const clone = cloneDeep(sample)
+ clone._pleroma_settings_version = [
+ PLEROMAFE_SETTINGS_MAJOR_VERSION,
+ PLEROMAFE_SETTINGS_MINOR_VERSION
+ ]
+ return clone
}
},
computed: {
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index 552ca41f..c7da5433 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -31,20 +31,86 @@
</transition>
<button
class="btn button-default"
+ :title="$t('general.peek')"
@click="peekModal"
>
- {{ $t('general.peek') }}
+ <FAIcon
+ :icon="['far', 'window-minimize']"
+ fixed-width
+ />
</button>
<button
class="btn button-default"
+ :title="$t('general.close')"
@click="closeModal"
>
- {{ $t('general.close') }}
+ <FAIcon
+ icon="times"
+ fixed-width
+ />
</button>
</div>
<div class="panel-body">
<SettingsModalContent v-if="modalOpenedOnce" />
</div>
+ <div class="panel-footer">
+ <Popover
+ class="export"
+ trigger="click"
+ placement="top"
+ :offset="{ y: 5, x: 5 }"
+ :bound-to="{ x: 'container' }"
+ remove-padding
+ >
+ <button
+ slot="trigger"
+ class="btn button-default"
+ :title="$t('general.close')"
+ >
+ <span>{{ $t("settings.file_export_import.backup_restore") }}</span>
+ <FAIcon
+ icon="chevron-down"
+ />
+ </button>
+ <div
+ slot="content"
+ slot-scope="{close}"
+ >
+ <div class="dropdown-menu">
+ <button
+ class="button-default dropdown-item dropdown-item-icon"
+ @click.prevent="backup"
+ @click="close"
+ >
+ <FAIcon
+ icon="file-download"
+ fixed-width
+ /><span>{{ $t("settings.file_export_import.backup_settings") }}</span>
+ </button>
+ <button
+ class="button-default dropdown-item dropdown-item-icon"
+ @click.prevent="backupWithTheme"
+ @click="close"
+ >
+ <FAIcon
+ icon="file-download"
+ fixed-width
+ /><span>{{ $t("settings.file_export_import.backup_settings_theme") }}</span>
+ </button>
+ <button
+ class="button-default dropdown-item dropdown-item-icon"
+ @click.prevent="restore"
+ @click="close"
+ >
+ <FAIcon
+ icon="file-upload"
+ fixed-width
+ /><span>{{ $t("settings.file_export_import.restore_settings") }}</span>
+ </button>
+ </div>
+ </div>
+ </Popover>
+ </div>
</div>
</Modal>
</template>
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index 8f8fe48e..7e0568ea 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -24,7 +24,7 @@
class="btn button-default"
@click="updateNotificationSettings"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index 175a0219..bb3c301d 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -153,7 +153,7 @@
class="btn button-default"
@click="updateProfile"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
@@ -227,7 +227,7 @@
class="btn button-default"
@click="submitBanner(banner)"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
@@ -266,7 +266,7 @@
class="btn button-default"
@click="submitBackground(background)"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue
index 56bea1f4..275d4616 100644
--- a/src/components/settings_modal/tabs/security_tab/security_tab.vue
+++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue
@@ -22,7 +22,7 @@
class="btn button-default"
@click="changeEmail"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
<p v-if="changedEmail">
{{ $t('settings.changed_email') }}
@@ -60,7 +60,7 @@
class="btn button-default"
@click="changePassword"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
<p v-if="changedPassword">
{{ $t('settings.changed_password') }}
@@ -133,7 +133,7 @@
class="btn button-default"
@click="confirmDelete"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js
index 73068e19..1388f74b 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js
@@ -16,6 +16,10 @@ import {
colors2to3
} from 'src/services/style_setter/style_setter.js'
import {
+ newImporter,
+ newExporter
+} from 'src/services/export_import/export_import.js'
+import {
SLOT_INHERITANCE
} from 'src/services/theme_data/pleromafe.js'
import {
@@ -31,7 +35,6 @@ import ShadowControl from 'src/components/shadow_control/shadow_control.vue'
import FontControl from 'src/components/font_control/font_control.vue'
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
-import ExportImport from 'src/components/export_import/export_import.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Select from 'src/components/select/select.vue'
@@ -60,6 +63,15 @@ const colorConvert = (color) => {
export default {
data () {
return {
+ themeImporter: newImporter({
+ validator: this.importValidator,
+ onImport: this.onImport,
+ onImportFailure: this.onImportFailure
+ }),
+ themeExporter: newExporter({
+ filename: 'pleroma_theme',
+ getExportedObject: () => this.exportedTheme
+ }),
availableStyles: [],
selected: this.$store.getters.mergedConfig.theme,
themeWarning: undefined,
@@ -376,7 +388,6 @@ export default {
FontControl,
TabSwitcher,
Preview,
- ExportImport,
Checkbox,
Select
},
@@ -522,10 +533,15 @@ export default {
this.previewColors.mod
)
},
+ importTheme () { this.themeImporter.importData() },
+ exportTheme () { this.themeExporter.exportData() },
onImport (parsed, forceSource = false) {
this.tempImportFile = parsed
this.loadTheme(parsed, 'file', forceSource)
},
+ onImportFailure (result) {
+ this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' })
+ },
importValidator (parsed) {
const version = parsed._pleroma_theme_version
return version >= 1 || version <= 2
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
index 2b4b8ba0..570cd11b 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
@@ -48,17 +48,13 @@
</template>
</div>
</div>
- <ExportImport
- :export-object="exportedTheme"
- :export-label="$t(&quot;settings.export_theme&quot;)"
- :import-label="$t(&quot;settings.import_theme&quot;)"
- :import-failed-text="$t(&quot;settings.invalid_theme_imported&quot;)"
- :on-import="onImport"
- :validator="importValidator"
- >
- <template slot="before">
- <div class="presets">
- {{ $t('settings.presets') }}
+ <div class="top">
+ <div class="presets">
+ {{ $t('settings.presets') }}
+ <label
+ for="preset-switcher"
+ class="select"
+ >
<Select
id="preset-switcher"
v-model="selected"
@@ -76,9 +72,27 @@
{{ style[0] || style.name }}
</option>
</Select>
- </div>
- </template>
- </ExportImport>
+ <FAIcon
+ class="select-down-icon"
+ icon="chevron-down"
+ />
+ </label>
+ </div>
+ <div class="export-import">
+ <button
+ class="btn button-default"
+ @click="importTheme"
+ >
+ {{ $t(&quot;settings.import_theme&quot;) }}
+ </button>
+ <button
+ class="btn button-default"
+ @click="exportTheme"
+ >
+ {{ $t(&quot;settings.export_theme&quot;) }}
+ </button>
+ </div>
+ </div>
</div>
<div class="save-load-options">
<span class="keep-option">
diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss
new file mode 100644
index 00000000..2c5a67e2
--- /dev/null
+++ b/src/components/timeline/timeline.scss
@@ -0,0 +1,31 @@
+@import '../../_variables.scss';
+
+.Timeline {
+ .loadmore-text {
+ opacity: 1;
+ }
+
+ &.-blocked {
+ cursor: progress;
+ }
+
+ .timeline-heading {
+ max-width: 100%;
+ flex-wrap: nowrap;
+ align-items: center;
+ position: relative;
+
+ .loadmore-button {
+ flex-shrink: 0;
+ }
+
+ .loadmore-text {
+ flex-shrink: 0;
+ line-height: 1em;
+ }
+ }
+
+ .timeline-footer {
+ border: none;
+ }
+}
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 286477c2..767428f0 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -52,13 +52,13 @@
<div :class="classes.footer">
<div
v-if="count===0"
- class="new-status-notification text-center panel-footer faint"
+ class="new-status-notification text-center faint"
>
{{ $t('timeline.no_statuses') }}
</div>
<div
v-else-if="bottomedOut"
- class="new-status-notification text-center panel-footer faint"
+ class="new-status-notification text-center faint"
>
{{ $t('timeline.no_more_statuses') }}
</div>
@@ -67,13 +67,13 @@
class="button-unstyled -link -fullwidth"
@click.prevent="fetchOlderStatuses()"
>
- <div class="new-status-notification text-center panel-footer">
+ <div class="new-status-notification text-center">
{{ $t('timeline.load_older') }}
</div>
</button>
<div
v-else
- class="new-status-notification text-center panel-footer"
+ class="new-status-notification text-center"
>
<FAIcon
icon="circle-notch"
@@ -87,32 +87,4 @@
<script src="./timeline.js"></script>
-<style lang="scss">
-@import '../../_variables.scss';
-
-.Timeline {
- .loadmore-text {
- opacity: 1;
- }
-
- &.-blocked {
- cursor: progress;
- }
-}
-
-.timeline-heading {
- max-width: 100%;
- flex-wrap: nowrap;
- align-items: center;
- position: relative;
-
- .loadmore-button {
- flex-shrink: 0;
- }
-
- .loadmore-text {
- flex-shrink: 0;
- line-height: 1em;
- }
-}
-</style>
+<style src="./timeline.scss" lang="scss"> </style>
diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js
index 9ec1a007..eae65a55 100644
--- a/src/components/timeline/timeline_quick_settings.js
+++ b/src/components/timeline/timeline_quick_settings.js
@@ -1,5 +1,4 @@
import Popover from '../popover/popover.vue'
-import BooleanSetting from '../settings_modal/helpers/boolean_setting.vue'
import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
@@ -12,8 +11,7 @@ library.add(
const TimelineQuickSettings = {
components: {
- Popover,
- BooleanSetting
+ Popover
},
methods: {
setReplyVisibility (visibility) {
diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue
index 4ad2842b..94997c0d 100644
--- a/src/components/timeline/timeline_quick_settings.vue
+++ b/src/components/timeline/timeline_quick_settings.vue
@@ -6,7 +6,7 @@
>
<div
slot="content"
- class="timeline-settings-menu dropdown-menu"
+ class="dropdown-menu"
>
<div v-if="loggedIn">
<button
@@ -96,12 +96,6 @@
.dropdown-item {
margin: 0;
}
-
- .timeline-settings-menu {
- display: flex;
- min-width: 12em;
- flex-direction: column;
- }
}
</style>
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
index 8d6a58b1..bab51e75 100644
--- a/src/components/timeline_menu/timeline_menu.js
+++ b/src/components/timeline_menu/timeline_menu.js
@@ -1,29 +1,17 @@
import Popover from '../popover/popover.vue'
-import { mapState } from 'vuex'
+import TimelineMenuContent from './timeline_menu_content.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
- faUsers,
- faGlobe,
- faBookmark,
- faEnvelope,
- faHome,
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
-library.add(
- faUsers,
- faGlobe,
- faBookmark,
- faEnvelope,
- faHome,
- faChevronDown
-)
+library.add(faChevronDown)
// Route -> i18n key mapping, exported and not in the computed
// because nav panel benefits from the same information.
export const timelineNames = () => {
return {
- 'friends': 'nav.timeline',
+ 'friends': 'nav.home_timeline',
'bookmarks': 'nav.bookmarks',
'dms': 'nav.dms',
'public-timeline': 'nav.public_tl',
@@ -33,7 +21,8 @@ export const timelineNames = () => {
const TimelineMenu = {
components: {
- Popover
+ Popover,
+ TimelineMenuContent
},
data () {
return {
@@ -41,9 +30,6 @@ const TimelineMenu = {
}
},
created () {
- if (this.currentUser && this.currentUser.locked) {
- this.$store.dispatch('startFetchingFollowRequests')
- }
if (timelineNames()[this.$route.name]) {
this.$store.dispatch('setLastTimeline', this.$route.name)
}
@@ -75,13 +61,6 @@ const TimelineMenu = {
const i18nkey = timelineNames()[this.$route.name]
return i18nkey ? this.$t(i18nkey) : route
}
- },
- computed: {
- ...mapState({
- currentUser: state => state.users.currentUser,
- privateMode: state => state.instance.private,
- federating: state => state.instance.federating
- })
}
}
diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue
index 3c86842b..22dc3432 100644
--- a/src/components/timeline_menu/timeline_menu.vue
+++ b/src/components/timeline_menu/timeline_menu.vue
@@ -13,53 +13,7 @@
slot="content"
class="timeline-menu-popover panel panel-default"
>
- <ul>
- <li v-if="currentUser">
- <router-link :to="{ name: 'friends' }">
- <FAIcon
- fixed-width
- class="fa-scale-110 fa-old-padding "
- icon="home"
- />{{ $t("nav.timeline") }}
- </router-link>
- </li>
- <li v-if="currentUser">
- <router-link :to="{ name: 'bookmarks'}">
- <FAIcon
- fixed-width
- class="fa-scale-110 fa-old-padding "
- icon="bookmark"
- />{{ $t("nav.bookmarks") }}
- </router-link>
- </li>
- <li v-if="currentUser">
- <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
- <FAIcon
- fixed-width
- class="fa-scale-110 fa-old-padding "
- icon="envelope"
- />{{ $t("nav.dms") }}
- </router-link>
- </li>
- <li v-if="currentUser || !privateMode">
- <router-link :to="{ name: 'public-timeline' }">
- <FAIcon
- fixed-width
- class="fa-scale-110 fa-old-padding "
- icon="users"
- />{{ $t("nav.public_tl") }}
- </router-link>
- </li>
- <li v-if="federating && (currentUser || !privateMode)">
- <router-link :to="{ name: 'public-external-timeline' }">
- <FAIcon
- fixed-width
- class="fa-scale-110 fa-old-padding "
- icon="globe"
- />{{ $t("nav.twkn") }}
- </router-link>
- </li>
- </ul>
+ <TimelineMenuContent />
</div>
<div
slot="trigger"
diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js
new file mode 100644
index 00000000..671570dd
--- /dev/null
+++ b/src/components/timeline_menu/timeline_menu_content.js
@@ -0,0 +1,29 @@
+import { mapState } from 'vuex'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+ faUsers,
+ faGlobe,
+ faBookmark,
+ faEnvelope,
+ faHome
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faUsers,
+ faGlobe,
+ faBookmark,
+ faEnvelope,
+ faHome
+)
+
+const TimelineMenuContent = {
+ computed: {
+ ...mapState({
+ currentUser: state => state.users.currentUser,
+ privateMode: state => state.instance.private,
+ federating: state => state.instance.federating
+ })
+ }
+}
+
+export default TimelineMenuContent
diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue
new file mode 100644
index 00000000..bed1b679
--- /dev/null
+++ b/src/components/timeline_menu/timeline_menu_content.vue
@@ -0,0 +1,66 @@
+<template>
+ <ul>
+ <li v-if="currentUser">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'friends' }"
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding "
+ icon="home"
+ />{{ $t("nav.home_timeline") }}
+ </router-link>
+ </li>
+ <li v-if="currentUser || !privateMode">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'public-timeline' }"
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding "
+ icon="users"
+ />{{ $t("nav.public_tl") }}
+ </router-link>
+ </li>
+ <li v-if="federating && (currentUser || !privateMode)">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'public-external-timeline' }"
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding "
+ icon="globe"
+ />{{ $t("nav.twkn") }}
+ </router-link>
+ </li>
+ <li v-if="currentUser">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'bookmarks'}"
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding "
+ icon="bookmark"
+ />{{ $t("nav.bookmarks") }}
+ </router-link>
+ </li>
+ <li v-if="currentUser">
+ <router-link
+ class="menu-item"
+ :to="{ name: 'dms', params: { username: currentUser.screen_name } }"
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding "
+ icon="envelope"
+ />{{ $t("nav.dms") }}
+ </router-link>
+ </li>
+ </ul>
+</template>
+
+<script src="./timeline_menu_content.js" ></script>