aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorHenry Jameson <me@hjkos.com>2021-04-07 20:45:57 +0300
committerHenry Jameson <me@hjkos.com>2021-04-07 20:45:57 +0300
commit4e56e64034ce1e153ab2bbe3432ada89bdce09cd (patch)
tree791cbd7cf3cc9452bbf9b3dbc67fb35ae550dd50 /src/components
parent2da37f15ab6a7e0c6088a6e5a0b2c2885f1cb85a (diff)
parent8b96ea93776fd1eb462a7c54822d4f8ad6a9e776 (diff)
Merge remote-tracking branch 'origin/develop' into better-selects
* origin/develop: (76 commits) Translated using Weblate (Italian) Translated using Weblate (Basque) Translated using Weblate (Spanish) Translated using Weblate (Chinese (Simplified)) Translated using Weblate (Italian) Translated using Weblate (Chinese (Traditional)) Translated using Weblate (Russian) Translated using Weblate (Italian) Translated using Weblate (French) Translated using Weblate (Russian) Translated using Weblate (Italian) Translated using Weblate (French) Translated using Weblate (Basque) Translated using Weblate (Spanish) Translated using Weblate (Chinese (Simplified)) Translated using Weblate (Japanese) Translated using Weblate (Italian) Translated using Weblate (Esperanto) Translated using Weblate (Chinese (Traditional)) Translated using Weblate (Norwegian Bokmål) ...
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>