diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/features_panel/features_panel.js | 2 | ||||
| -rw-r--r-- | src/components/features_panel/features_panel.vue | 2 | ||||
| -rw-r--r-- | src/components/mobile_nav/mobile_nav.js | 77 | ||||
| -rw-r--r-- | src/components/mobile_nav/mobile_nav.vue | 140 | ||||
| -rw-r--r-- | src/components/notifications/notifications.js | 3 | ||||
| -rw-r--r-- | src/components/notifications/notifications.vue | 2 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 25 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 12 | ||||
| -rw-r--r-- | src/components/scope_selector/scope_selector.js | 54 | ||||
| -rw-r--r-- | src/components/scope_selector/scope_selector.vue | 30 | ||||
| -rw-r--r-- | src/components/settings/settings.js | 10 | ||||
| -rw-r--r-- | src/components/settings/settings.vue | 6 | ||||
| -rw-r--r-- | src/components/side_drawer/side_drawer.vue | 5 | ||||
| -rw-r--r-- | src/components/status/status.js | 6 | ||||
| -rw-r--r-- | src/components/status/status.vue | 14 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.js | 6 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.vue | 10 |
17 files changed, 364 insertions, 40 deletions
diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js index e0b7a118..5f0b7b25 100644 --- a/src/components/features_panel/features_panel.js +++ b/src/components/features_panel/features_panel.js @@ -6,7 +6,7 @@ const FeaturesPanel = { gopher: function () { return this.$store.state.instance.gopherAvailable }, whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled }, mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable }, - scopeOptions: function () { return this.$store.state.instance.scopeOptionsEnabled }, + minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode }, textlimit: function () { return this.$store.state.instance.textlimit } } } diff --git a/src/components/features_panel/features_panel.vue b/src/components/features_panel/features_panel.vue index 445143e9..7a263e01 100644 --- a/src/components/features_panel/features_panel.vue +++ b/src/components/features_panel/features_panel.vue @@ -12,7 +12,7 @@ <li v-if="gopher">{{$t('features_panel.gopher')}}</li> <li v-if="whoToFollow">{{$t('features_panel.who_to_follow')}}</li> <li v-if="mediaProxy">{{$t('features_panel.media_proxy')}}</li> - <li v-if="scopeOptions">{{$t('features_panel.scope_options')}}</li> + <li>{{$t('features_panel.scope_options')}}</li> <li>{{$t('features_panel.text_limit')}} = {{textlimit}}</li> </ul> </div> diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js new file mode 100644 index 00000000..bc63d2ba --- /dev/null +++ b/src/components/mobile_nav/mobile_nav.js @@ -0,0 +1,77 @@ +import SideDrawer from '../side_drawer/side_drawer.vue' +import Notifications from '../notifications/notifications.vue' +import MobilePostStatusModal from '../mobile_post_status_modal/mobile_post_status_modal.vue' +import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' +import GestureService from '../../services/gesture_service/gesture_service' + +const MobileNav = { + components: { + SideDrawer, + Notifications, + MobilePostStatusModal + }, + data: () => ({ + notificationsCloseGesture: undefined, + notificationsOpen: false + }), + created () { + this.notificationsCloseGesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + this.closeMobileNotifications, + 50 + ) + }, + computed: { + currentUser () { + return this.$store.state.users.currentUser + }, + unseenNotifications () { + return unseenNotificationsFromStore(this.$store) + }, + unseenNotificationsCount () { + return this.unseenNotifications.length + }, + sitename () { return this.$store.state.instance.name } + }, + methods: { + toggleMobileSidebar () { + this.$refs.sideDrawer.toggleDrawer() + }, + openMobileNotifications () { + this.notificationsOpen = true + }, + closeMobileNotifications () { + if (this.notificationsOpen) { + // make sure to mark notifs seen only when the notifs were open and not + // from close-calls. + this.notificationsOpen = false + this.markNotificationsAsSeen() + } + }, + notificationsTouchStart (e) { + GestureService.beginSwipe(e, this.notificationsCloseGesture) + }, + notificationsTouchMove (e) { + GestureService.updateSwipe(e, this.notificationsCloseGesture) + }, + scrollToTop () { + window.scrollTo(0, 0) + }, + logout () { + this.$router.replace('/main/public') + this.$store.dispatch('logout') + }, + markNotificationsAsSeen () { + this.$refs.notifications.markAsSeen() + } + }, + watch: { + $route () { + // handles closing notificaitons when you press any router-link on the + // notifications. + this.closeMobileNotifications() + } + } +} + +export default MobileNav diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue new file mode 100644 index 00000000..5fa41638 --- /dev/null +++ b/src/components/mobile_nav/mobile_nav.vue @@ -0,0 +1,140 @@ +<template> + <nav class='nav-bar container' id="nav"> + <div class='mobile-inner-nav' @click="scrollToTop()"> + <div class='item'> + <a href="#" class="mobile-nav-button" @click.stop.prevent="toggleMobileSidebar()"> + <i class="button-icon icon-menu"></i> + </a> + <router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link> + </div> + <div class='item right'> + <a class="mobile-nav-button" v-if="currentUser" href="#" @click.stop.prevent="openMobileNotifications()"> + <i class="button-icon icon-bell-alt"></i> + <div class="alert-dot" v-if="unseenNotificationsCount"></div> + </a> + </div> + </div> + <SideDrawer ref="sideDrawer" :logout="logout"/> + <div v-if="currentUser" + class="mobile-notifications-drawer" + :class="{ 'closed': !notificationsOpen }" + @touchstart="notificationsTouchStart" + @touchmove="notificationsTouchMove" + > + <div class="mobile-notifications-header"> + <span class="title">{{$t('notifications.notifications')}}</span> + <a class="mobile-nav-button" @click.stop.prevent="closeMobileNotifications()"> + <i class="button-icon icon-cancel"/> + </a> + </div> + <div v-if="currentUser" class="mobile-notifications"> + <Notifications ref="notifications" noHeading="true"/> + </div> + </div> + <MobilePostStatusModal /> + </nav> +</template> + +<script src="./mobile_nav.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.mobile-inner-nav { + width: 100%; + display: flex; + align-items: center; +} + +.mobile-nav-button { + display: flex; + justify-content: center; + width: 50px; + position: relative; + cursor: pointer; +} + +.alert-dot { + border-radius: 100%; + height: 8px; + width: 8px; + position: absolute; + left: calc(50% - 4px); + top: calc(50% - 4px); + margin-left: 6px; + margin-top: -6px; + background-color: $fallback--cRed; + background-color: var(--badgeNotification, $fallback--cRed); +} + +.mobile-notifications-drawer { + width: 100%; + height: 100vh; + overflow-x: hidden; + position: fixed; + top: 0; + left: 0; + box-shadow: 1px 1px 4px rgba(0,0,0,.6); + box-shadow: var(--panelShadow); + transition-property: transform; + transition-duration: 0.25s; + transform: translateX(0); + + &.closed { + transform: translateX(100%); + } +} + +.mobile-notifications-header { + display: flex; + align-items: center; + justify-content: space-between; + z-index: 1; + width: 100%; + height: 50px; + line-height: 50px; + position: absolute; + color: var(--topBarText); + background-color: $fallback--fg; + background-color: var(--topBar, $fallback--fg); + box-shadow: 0px 0px 4px rgba(0,0,0,.6); + box-shadow: var(--topBarShadow); + + .title { + font-size: 1.3em; + margin-left: 0.6em; + } +} + +.mobile-notifications { + margin-top: 50px; + width: 100vw; + height: calc(100vh - 50px); + overflow-x: hidden; + overflow-y: scroll; + + color: $fallback--text; + color: var(--text, $fallback--text); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + + .notifications { + padding: 0; + border-radius: 0; + box-shadow: none; + .panel { + border-radius: 0; + margin: 0; + box-shadow: none; + } + .panel:after { + border-radius: 0; + } + .panel .panel-heading { + border-radius: 0; + box-shadow: none; + } + } +} + +</style> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index e1c53c58..375e4751 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -7,6 +7,9 @@ import { } from '../../services/notification_utils/notification_utils.js' const Notifications = { + props: [ + 'noHeading' + ], created () { const store = this.$store const credentials = store.state.users.currentUser.credentials diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index 37104b90..88775be1 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -1,7 +1,7 @@ <template> <div class="notifications"> <div class="panel panel-default"> - <div class="panel-heading"> + <div v-if="!noHeading" class="panel-heading"> <div class="title"> {{$t('notifications.notifications')}} <span class="badge badge-notification unseen-count" v-if="unseenCount">{{unseenCount}}</span> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 229aefb7..40e2610e 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -1,5 +1,6 @@ import statusPoster from '../../services/status_poster/status_poster.service.js' import MediaUpload from '../media_upload/media_upload.vue' +import ScopeSelector from '../scope_selector/scope_selector.vue' import EmojiInput from '../emoji-input/emoji-input.vue' import fileTypeService from '../../services/file_type/file_type.service.js' import Completion from '../../services/completion/completion.js' @@ -30,6 +31,7 @@ const PostStatusForm = { ], components: { MediaUpload, + ScopeSelector, EmojiInput }, mounted () { @@ -80,14 +82,6 @@ const PostStatusForm = { } }, computed: { - vis () { - return { - public: { selected: this.newStatus.visibility === 'public' }, - unlisted: { selected: this.newStatus.visibility === 'unlisted' }, - private: { selected: this.newStatus.visibility === 'private' }, - direct: { selected: this.newStatus.visibility === 'direct' } - } - }, candidates () { const firstchar = this.textAtCaret.charAt(0) if (firstchar === '@') { @@ -135,6 +129,15 @@ const PostStatusForm = { users () { return this.$store.state.users.users }, + userDefaultScope () { + return this.$store.state.users.currentUser.default_scope + }, + showAllScopes () { + const minimalScopesMode = typeof this.$store.state.config.minimalScopesMode === 'undefined' + ? this.$store.state.instance.minimalScopesMode + : this.$store.state.config.minimalScopesMode + return !minimalScopesMode + }, emoji () { return this.$store.state.instance.emoji || [] }, @@ -159,8 +162,8 @@ const PostStatusForm = { isOverLengthLimit () { return this.hasStatusLengthLimit && (this.charactersLeft < 0) }, - scopeOptionsEnabled () { - return this.$store.state.instance.scopeOptionsEnabled + minimalScopesMode () { + return this.$store.state.instance.minimalScopesMode }, alwaysShowSubject () { if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') { @@ -168,7 +171,7 @@ const PostStatusForm = { } else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') { return this.$store.state.instance.alwaysShowSubjectInput } else { - return this.$store.state.instance.scopeOptionsEnabled + return true } }, formattingOptionsEnabled () { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 9f9f16ba..3d3a1082 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -48,12 +48,12 @@ </label> </span> - <div v-if="scopeOptionsEnabled"> - <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct" :title="$t('post_status.scope.direct')"></i> - <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private" :title="$t('post_status.scope.private')"></i> - <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted" :title="$t('post_status.scope.unlisted')"></i> - <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public" :title="$t('post_status.scope.public')"></i> - </div> + <scope-selector + :showAll="showAllScopes" + :userDefault="userDefaultScope" + :originalScope="copyMessageScope" + :initialScope="newStatus.visibility" + :onScopeChange="changeVis"/> </div> </div> <div class="autocomplete-panel" v-if="candidates"> diff --git a/src/components/scope_selector/scope_selector.js b/src/components/scope_selector/scope_selector.js new file mode 100644 index 00000000..8a42ee7b --- /dev/null +++ b/src/components/scope_selector/scope_selector.js @@ -0,0 +1,54 @@ +const ScopeSelector = { + props: [ + 'showAll', + 'userDefault', + 'originalScope', + 'initialScope', + 'onScopeChange' + ], + data () { + return { + currentScope: this.initialScope + } + }, + computed: { + showNothing () { + return !this.showPublic && !this.showUnlisted && !this.showPrivate && !this.showDirect + }, + showPublic () { + return this.originalScope !== 'direct' && this.shouldShow('public') + }, + showUnlisted () { + return this.originalScope !== 'direct' && this.shouldShow('unlisted') + }, + showPrivate () { + return this.originalScope !== 'direct' && this.shouldShow('private') + }, + showDirect () { + return this.shouldShow('direct') + }, + css () { + return { + public: {selected: this.currentScope === 'public'}, + unlisted: {selected: this.currentScope === 'unlisted'}, + private: {selected: this.currentScope === 'private'}, + direct: {selected: this.currentScope === 'direct'} + } + } + }, + methods: { + shouldShow (scope) { + return this.showAll || + this.currentScope === scope || + this.originalScope === scope || + this.userDefault === scope || + scope === 'direct' + }, + changeVis (scope) { + this.currentScope = scope + this.onScopeChange && this.onScopeChange(scope) + } + } +} + +export default ScopeSelector diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue new file mode 100644 index 00000000..33ea488f --- /dev/null +++ b/src/components/scope_selector/scope_selector.vue @@ -0,0 +1,30 @@ +<template> +<div v-if="!showNothing"> + <i class="icon-mail-alt" + :class="css.direct" + :title="$t('post_status.scope.direct')" + v-if="showDirect" + @click="changeVis('direct')"> + </i> + <i class="icon-lock" + :class="css.private" + :title="$t('post_status.scope.private')" + v-if="showPrivate" + v-on:click="changeVis('private')"> + </i> + <i class="icon-lock-open-alt" + :class="css.unlisted" + :title="$t('post_status.scope.unlisted')" + v-if="showUnlisted" + @click="changeVis('unlisted')"> + </i> + <i class="icon-globe" + :class="css.public" + :title="$t('post_status.scope.public')" + v-if="showPublic" + @click="changeVis('public')"> + </i> +</div> +</template> + +<script src="./scope_selector.js"></script> diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index 1d5f75ed..a85ab674 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -70,13 +70,18 @@ const settings = { alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined' ? instance.alwaysShowSubjectInput : user.alwaysShowSubjectInput, - alwaysShowSubjectInputDefault: instance.alwaysShowSubjectInput, + alwaysShowSubjectInputDefault: this.$t('settings.values.' + instance.alwaysShowSubjectInput), scopeCopyLocal: typeof user.scopeCopy === 'undefined' ? instance.scopeCopy : user.scopeCopy, scopeCopyDefault: this.$t('settings.values.' + instance.scopeCopy), + minimalScopesModeLocal: typeof user.minimalScopesMode === 'undefined' + ? instance.minimalScopesMode + : user.minimalScopesMode, + minimalScopesModeDefault: this.$t('settings.values.' + instance.minimalScopesMode), + stopGifs: user.stopGifs, webPushNotificationsLocal: user.webPushNotifications, loopVideoSilentOnlyLocal: user.loopVideosSilentOnly, @@ -200,6 +205,9 @@ const settings = { postContentTypeLocal (value) { this.$store.dispatch('setOption', { name: 'postContentType', value }) }, + minimalScopesModeLocal (value) { + this.$store.dispatch('setOption', { name: 'minimalScopesMode', value }) + }, stopGifs (value) { this.$store.dispatch('setOption', { name: 'stopGifs', value }) }, diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 33dad549..6ee103c7 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -118,6 +118,12 @@ </label> </div> </li> + <li> + <input type="checkbox" id="minimalScopesMode" v-model="minimalScopesModeLocal"> + <label for="minimalScopesMode"> + {{$t('settings.minimal_scopes_mode')}} {{$t('settings.instance_default', { value: minimalScopesModeDefault })}} + </label> + </li> </ul> </div> diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index e5046496..9abb8cef 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -22,11 +22,6 @@ </router-link> </li> <li v-if="currentUser" @click="toggleDrawer"> - <router-link :to="{ name: 'notifications', params: { username: currentUser.screen_name } }"> - {{ $t("notifications.notifications") }} {{ unseenNotificationsCount > 0 ? `(${unseenNotificationsCount})` : '' }} - </router-link> - </li> - <li v-if="currentUser" @click="toggleDrawer"> <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> {{ $t("nav.dms") }} </router-link> diff --git a/src/components/status/status.js b/src/components/status/status.js index 550fe76f..0295cd04 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -251,6 +251,12 @@ const Status = { }, maxThumbnails () { return this.$store.state.config.maxThumbnails + }, + contentHtml () { + if (!this.status.summary_html) { + return this.status.statusnet_html + } + return this.status.summary_html + '<br />' + this.status.statusnet_html } }, components: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 1f415534..690e8318 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -98,16 +98,16 @@ </div> <div class="status-content-wrapper" :class="{ 'tall-status': !showingLongSubject }" v-if="longSubject"> - <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="!showingLongSubject" href="#" @click.prevent="showingLongSubject=true">Show more</a> - <div @click.prevent="linkClicked" class="status-content media-body" v-html="status.statusnet_html"></div> - <a v-if="showingLongSubject" href="#" class="status-unhider" @click.prevent="showingLongSubject=false">Show less</a> + <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="!showingLongSubject" href="#" @click.prevent="showingLongSubject=true">{{$t("general.show_more")}}</a> + <div @click.prevent="linkClicked" class="status-content media-body" v-html="contentHtml"></div> + <a v-if="showingLongSubject" href="#" class="status-unhider" @click.prevent="showingLongSubject=false">{{$t("general.show_less")}}</a> </div> <div :class="{'tall-status': hideTallStatus}" class="status-content-wrapper" v-else> - <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="hideTallStatus" href="#" @click.prevent="toggleShowMore">Show more</a> - <div @click.prevent="linkClicked" class="status-content media-body" v-html="status.statusnet_html" v-if="!hideSubjectStatus"></div> + <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="hideTallStatus" href="#" @click.prevent="toggleShowMore">{{$t("general.show_more")}}</a> + <div @click.prevent="linkClicked" class="status-content media-body" v-html="contentHtml" v-if="!hideSubjectStatus"></div> <div @click.prevent="linkClicked" class="status-content media-body" v-html="status.summary_html" v-else></div> - <a v-if="hideSubjectStatus" href="#" class="cw-status-hider" @click.prevent="toggleShowMore">Show more</a> - <a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">Show less</a> + <a v-if="hideSubjectStatus" href="#" class="cw-status-hider" @click.prevent="toggleShowMore">{{$t("general.show_more")}}</a> + <a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">{{$t("general.show_less")}}</a> </div> <div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body"> diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index 5cb23b97..b6a0479d 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -4,6 +4,7 @@ import get from 'lodash/get' import TabSwitcher from '../tab_switcher/tab_switcher.js' import ImageCropper from '../image_cropper/image_cropper.vue' import StyleSwitcher from '../style_switcher/style_switcher.vue' +import ScopeSelector from '../scope_selector/scope_selector.vue' import fileSizeFormatService from '../../services/file_size_format/file_size_format.js' import BlockCard from '../block_card/block_card.vue' import MuteCard from '../mute_card/mute_card.vue' @@ -67,6 +68,7 @@ const UserSettings = { }, components: { StyleSwitcher, + ScopeSelector, TabSwitcher, ImageCropper, BlockList, @@ -80,8 +82,8 @@ const UserSettings = { pleromaBackend () { return this.$store.state.instance.pleromaBackend }, - scopeOptionsEnabled () { - return this.$store.state.instance.scopeOptionsEnabled + minimalScopesMode () { + return this.$store.state.instance.minimalScopesMode }, vis () { return { diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index 52df143c..c08698dc 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -38,13 +38,13 @@ <input type="checkbox" v-model="newLocked" id="account-locked"> <label for="account-locked">{{$t('settings.lock_account_description')}}</label> </p> - <div v-if="scopeOptionsEnabled"> + <div> <label for="default-vis">{{$t('settings.default_vis')}}</label> <div id="default-vis" class="visibility-tray"> - <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct" :title="$t('post_status.scope.direct')" ></i> - <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private" :title="$t('post_status.scope.private')"></i> - <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted" :title="$t('post_status.scope.unlisted')"></i> - <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public" :title="$t('post_status.scope.public')"></i> + <scope-selector + :showAll="true" + :userDefault="newDefaultScope" + :onScopeChange="changeVis"/> </div> </div> <p> |
