diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.scss | 48 | ||||
| -rw-r--r-- | src/App.vue | 5 | ||||
| -rw-r--r-- | src/boot/after_store.js | 15 | ||||
| -rw-r--r-- | src/components/attachment/attachment.js | 2 | ||||
| -rw-r--r-- | src/components/status/status.vue | 2 | ||||
| -rw-r--r-- | src/components/timeline/timeline.js | 5 | ||||
| -rw-r--r-- | src/components/user_card/user_card.js | 3 | ||||
| -rw-r--r-- | src/components/user_card/user_card.vue | 4 | ||||
| -rw-r--r-- | src/components/user_card_content/user_card_content.js | 12 | ||||
| -rw-r--r-- | src/components/user_card_content/user_card_content.vue | 5 | ||||
| -rw-r--r-- | src/components/user_finder/user_finder.vue | 5 | ||||
| -rw-r--r-- | src/components/user_profile/user_profile.vue | 18 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.js | 56 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.vue | 14 | ||||
| -rw-r--r-- | src/i18n/en.json | 3 | ||||
| -rw-r--r-- | src/i18n/ru.json | 2 | ||||
| -rw-r--r-- | src/main.js | 6 | ||||
| -rw-r--r-- | src/modules/instance.js | 2 | ||||
| -rw-r--r-- | src/modules/users.js | 5 |
19 files changed, 159 insertions, 53 deletions
diff --git a/src/App.scss b/src/App.scss index 5355d899..871f193f 100644 --- a/src/App.scss +++ b/src/App.scss @@ -228,24 +228,23 @@ i[class*=icon-] { padding: 0 10px 0 10px; } -.gaps { - margin: -1em 0 0 -1em; -} - .item { flex: 1; line-height: 50px; height: 50px; overflow: hidden; + display: flex; + flex-wrap: wrap; .nav-icon { font-size: 1.1em; margin-left: 0.4em; } -} -.gaps > .item { - padding: 1em 0 0 1em; + &.right { + justify-content: right; + padding-right: 20px; + } } .auto-size { @@ -293,8 +292,6 @@ nav { } .inner-nav { - padding-left: 20px; - padding-right: 20px; display: flex; align-items: center; flex-basis: 970px; @@ -452,6 +449,23 @@ nav { color: var(--faint, $fallback--faint); box-shadow: 0px 0px 4px rgba(0,0,0,.6); box-shadow: var(--topBarShadow); + + .back-button { + display: block; + max-width: 99px; + transition-property: opacity, max-width; + transition-duration: 300ms; + transition-timing-function: ease-out; + + i { + margin: 0 1em; + } + + &.hidden { + opacity: 0; + max-width: 20px; + } + } } .fade-enter-active, .fade-leave-active { @@ -486,6 +500,7 @@ nav { display: none; width: 100%; height: 46px; + button { display: block; flex: 1; @@ -499,6 +514,16 @@ nav { body { overflow-y: scroll; } + + nav { + .back-button { + display: none; + } + .site-name { + padding-left: 20px; + } + } + .sidebar-bounds { overflow: hidden; max-height: 100vh; @@ -591,11 +616,6 @@ nav { } } -.item.right { - text-align: right; - padding-right: 20px; -} - .visibility-tray { font-size: 1.2em; padding: 3px; diff --git a/src/App.vue b/src/App.vue index 16cd08d4..048c1e77 100644 --- a/src/App.vue +++ b/src/App.vue @@ -7,7 +7,10 @@ </div> <div class='inner-nav'> <div class='item'> - <router-link :to="{ name: 'root'}">{{sitename}}</router-link> + <router-link class="back-button" @click.native="activatePanel('timeline')" :to="{ name: 'root' }" active-class="hidden"> + <i class="icon-left-open" :title="$t('nav.back')"></i> + </router-link> + <router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link> </div> <div class='item right'> <user-finder class="nav-icon"></user-finder> diff --git a/src/boot/after_store.js b/src/boot/after_store.js index bc1d1acc..24a8d3ac 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -32,6 +32,10 @@ const afterStoreSetup = ({ store, i18n }) => { store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadlimit.bannerlimit) }) store.dispatch('setInstanceOption', { name: 'server', value: server }) + if (data.nsfwCensorImage) { + store.dispatch('setInstanceOption', { name: 'nsfwCensorImage', value: data.nsfwCensorImage }) + } + if (vapidPublicKey) { store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) } @@ -46,8 +50,17 @@ const afterStoreSetup = ({ store, i18n }) => { return {} }) .then((staticConfig) => { + const overrides = window.___pleromafe_dev_overrides || {} + const env = window.___pleromafe_mode.NODE_ENV + // This takes static config and overrides properties that are present in apiConfig - var config = Object.assign({}, staticConfig, apiConfig) + let config = {} + if (overrides.staticConfigPreference && env === 'development') { + console.warn('OVERRIDING API CONFIG WITH STATIC CONFIG') + config = Object.assign({}, apiConfig, staticConfig) + } else { + config = Object.assign({}, staticConfig, apiConfig) + } var theme = (config.theme) var background = (config.background) diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 16114c30..97c4f283 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -11,7 +11,7 @@ const Attachment = { ], data () { return { - nsfwImage, + nsfwImage: this.$store.state.config.nsfwCensorImage || nsfwImage, hideNsfwLocal: this.$store.state.config.hideNsfw, preloadImage: this.$store.state.config.preloadImage, loopVideo: this.$store.state.config.loopVideo, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 96709084..067980ac 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -54,7 +54,7 @@ </h4> </div> <div class="media-heading-right"> - <router-link @click.native="activatePanel('timeline')" :to="{ name: 'conversation', params: { id: status.id } }"> + <router-link class="timeago" @click.native="activatePanel('timeline')" :to="{ name: 'conversation', params: { id: status.id } }"> <timeago :since="status.created_at" :auto-update="60"></timeago> </router-link> <div class="visibility-icon" v-if="status.visibility"> diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index a651f619..f28b85bd 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -2,6 +2,7 @@ import Status from '../status/status.vue' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' import StatusOrConversation from '../status_or_conversation/status_or_conversation.vue' import UserCard from '../user_card/user_card.vue' +import { throttle } from 'lodash' const Timeline = { props: [ @@ -88,7 +89,7 @@ const Timeline = { this.paused = false } }, - fetchOlderStatuses () { + fetchOlderStatuses: throttle(function () { const store = this.$store const credentials = store.state.users.currentUser.credentials store.commit('setLoading', { timeline: this.timelineName, value: true }) @@ -101,7 +102,7 @@ const Timeline = { userId: this.userId, tag: this.tag }).then(() => store.commit('setLoading', { timeline: this.timelineName, value: false })) - }, + }, 1000, this), fetchFollowers () { const id = this.userId this.$store.state.api.backendInteractor.fetchFollowers({ id }) diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index a019627a..b8eb28e3 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -14,6 +14,9 @@ const UserCard = { components: { UserCardContent }, + computed: { + currentUser () { return this.$store.state.users.currentUser } + }, methods: { toggleUserExpanded () { this.userExpanded = !this.userExpanded diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 5a8e5531..eb0d7576 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -10,13 +10,13 @@ <div :title="user.name" v-if="user.name_html" class="user-name"> <span v-html="user.name_html"></span> <span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you"> - {{ $t('user_card.follows_you') }} + {{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }} </span> </div> <div :title="user.name" v-else class="user-name"> {{ user.name }} <span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you"> - {{ $t('user_card.follows_you') }} + {{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }} </span> </div> <router-link class='user-screen-name' :to="{ name: 'user-profile', params: { id: user.id } }"> diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index e7f19953..be7a2349 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -20,10 +20,20 @@ export default { if (color) { const rgb = (typeof color === 'string') ? hex2rgb(color) : color const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)` + + const gradient = [ + [tintColor, this.hideBio ? '60%' : ''], + this.hideBio ? [ + color, '100%' + ] : [ + tintColor, '' + ] + ].map(_ => _.join(' ')).join(', ') + return { backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`, backgroundImage: [ - `linear-gradient(to bottom, ${tintColor}, ${tintColor})`, + `linear-gradient(to bottom, ${gradient})`, `url(${this.user.cover_photo})` ].join(', ') } diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 18504277..08b25595 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -90,7 +90,7 @@ </div> </div> </div> - <div class="panel-body profile-panel-body" v-if="switcher"> + <div class="panel-body profile-panel-body" v-if="!hideBio"> <div v-if="!hideUserStatsLocal || switcher" class="user-counts" :class="{clickable: switcher}"> <div class="user-count" v-on:click.prevent="setProfileView('statuses')" :class="{selected: selected === 'statuses'}"> <h5>{{ $t('user_card.statuses') }}</h5> @@ -122,6 +122,9 @@ border-radius: var(--panelRadius, $fallback--panelRadius); overflow: hidden; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + .panel-heading { padding: 0.6em 0em; text-align: center; diff --git a/src/components/user_finder/user_finder.vue b/src/components/user_finder/user_finder.vue index 8786f6c7..4d9f6842 100644 --- a/src/components/user_finder/user_finder.vue +++ b/src/components/user_finder/user_finder.vue @@ -1,12 +1,12 @@ <template> - <span class="user-finder-container"> + <div class="user-finder-container"> <i class="icon-spin4 user-finder-icon animate-spin-slow" v-if="loading" /> <a href="#" v-if="hidden" :title="$t('finder.find_user')" ><i class="icon-user-plus user-finder-icon" @click.prevent.stop="toggleHidden" /></a> <span v-else> <input class="user-finder-input" @keyup.enter="findUser(username)" v-model="username" :placeholder="$t('finder.find_user')" id="user-finder-input" type="text"/> <i class="icon-cancel user-finder-icon" @click.prevent.stop="toggleHidden"/> </span> - </span> + </div> </template> <script src="./user_finder.js"></script> @@ -15,7 +15,6 @@ @import '../../_variables.scss'; .user-finder-container { - height: 29px; max-width: 100%; } diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 91d4acd2..4d2853a6 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -3,6 +3,16 @@ <div v-if="user" class="user-profile panel panel-default"> <user-card-content :user="user" :switcher="true" :selected="timeline.viewing"></user-card-content> </div> + <div v-else class="panel user-profile-placeholder"> + <div class="panel-heading"> + <div class="title"> + {{ $t('settings.profile_tab') }} + </div> + </div> + <div class="panel-body"> + <i class="icon-spin3 animate-spin"></i> + </div> + </div> <Timeline :title="$t('user_profile.timeline_title')" :timeline="timeline" :timeline-name="'user'" :user-id="userId"/> </div> </template> @@ -21,4 +31,12 @@ align-items: stretch; } } +.user-profile-placeholder { + .panel-body { + display: flex; + justify-content: center; + align-items: middle; + padding: 7em; + } +} </style> diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index 43e39b13..ca7bf319 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -5,11 +5,12 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for const UserSettings = { data () { return { - newname: this.$store.state.users.currentUser.name, - newbio: this.$store.state.users.currentUser.description, - newlocked: this.$store.state.users.currentUser.locked, - newnorichtext: this.$store.state.users.currentUser.no_rich_text, - newdefaultScope: this.$store.state.users.currentUser.default_scope, + newName: this.$store.state.users.currentUser.name, + newBio: this.$store.state.users.currentUser.description, + newLocked: this.$store.state.users.currentUser.locked, + newNoRichText: this.$store.state.users.currentUser.no_rich_text, + newDefaultScope: this.$store.state.users.currentUser.default_scope, + newHideNetwork: this.$store.state.users.currentUser.hide_network, followList: null, followImportError: false, followsImported: false, @@ -49,31 +50,45 @@ const UserSettings = { }, vis () { return { - public: { selected: this.newdefaultScope === 'public' }, - unlisted: { selected: this.newdefaultScope === 'unlisted' }, - private: { selected: this.newdefaultScope === 'private' }, - direct: { selected: this.newdefaultScope === 'direct' } + public: { selected: this.newDefaultScope === 'public' }, + unlisted: { selected: this.newDefaultScope === 'unlisted' }, + private: { selected: this.newDefaultScope === 'private' }, + direct: { selected: this.newDefaultScope === 'direct' } } } }, methods: { updateProfile () { const name = this.newname - const description = this.newbio - const locked = this.newlocked + const description = this.newBio + const locked = this.newLocked + // Backend notation. /* eslint-disable camelcase */ - const default_scope = this.newdefaultScope - const no_rich_text = this.newnorichtext - this.$store.state.api.backendInteractor.updateProfile({params: {name, description, locked, default_scope, no_rich_text}}).then((user) => { - if (!user.error) { - this.$store.commit('addNewUsers', [user]) - this.$store.commit('setCurrentUser', user) - } - }) + const default_scope = this.newDefaultScope + const no_rich_text = this.newNoRichText + const hide_network = this.newHideNetwork /* eslint-enable camelcase */ + this.$store.state.api.backendInteractor + .updateProfile({ + params: { + name, + description, + locked, + // Backend notation. + /* eslint-disable camelcase */ + default_scope, + no_rich_text, + hide_network + /* eslint-enable camelcase */ + }}).then((user) => { + if (!user.error) { + this.$store.commit('addNewUsers', [user]) + this.$store.commit('setCurrentUser', user) + } + }) }, changeVis (visibility) { - this.newdefaultScope = visibility + this.newDefaultScope = visibility }, uploadFile (slot, e) { const file = e.target.files[0] @@ -221,6 +236,7 @@ const UserSettings = { .fetchFriends({id: this.$store.state.users.currentUser.id}) .then((friendList) => { this.exportPeople(friendList, 'friends.csv') + setTimeout(() => { this.enableFollowsExport = true }, 2000) }) }, followListChange () { diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index 1a98b788..67b65b57 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -9,11 +9,11 @@ <div class="setting-item" > <h2>{{$t('settings.name_bio')}}</h2> <p>{{$t('settings.name')}}</p> - <input class='name-changer' id='username' v-model="newname"></input> + <input class='name-changer' id='username' v-model="newName"></input> <p>{{$t('settings.bio')}}</p> - <textarea class="bio" v-model="newbio"></textarea> + <textarea class="bio" v-model="newBio"></textarea> <p> - <input type="checkbox" v-model="newlocked" id="account-locked"> + <input type="checkbox" v-model="newLocked" id="account-locked"> <label for="account-locked">{{$t('settings.lock_account_description')}}</label> </p> <div v-if="scopeOptionsEnabled"> @@ -26,10 +26,14 @@ </div> </div> <p> - <input type="checkbox" v-model="newnorichtext" id="account-no-rich-text"> + <input type="checkbox" v-model="newNoRichText" id="account-no-rich-text"> <label for="account-no-rich-text">{{$t('settings.no_rich_text_description')}}</label> </p> - <button :disabled='newname.length <= 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button> + <p> + <input type="checkbox" v-model="newHideNetwork" id="account-hide-network"> + <label for="account-no-rich-text">{{$t('settings.hide_network_description')}}</label> + </p> + <button :disabled='newName.length <= 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button> </div> <div class="setting-item"> <h2>{{$t('settings.avatar')}}</h2> diff --git a/src/i18n/en.json b/src/i18n/en.json index ac5eef5a..1ce53796 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -29,6 +29,7 @@ "username": "Username" }, "nav": { + "back": "Back", "chat": "Local Chat", "friend_requests": "Follow Requests", "mentions": "Mentions", @@ -151,6 +152,7 @@ "notification_visibility_mentions": "Mentions", "notification_visibility_repeats": "Repeats", "no_rich_text_description": "Strip rich text formatting from all posts", + "hide_network_description": "Don't show who I'm following and who's following me", "nsfw_clickthrough": "Enable clickthrough NSFW attachment hiding", "panelRadius": "Panels", "pause_on_unfocused": "Pause streaming when tab is not focused", @@ -322,6 +324,7 @@ "followers": "Followers", "following": "Following!", "follows_you": "Follows you!", + "its_you": "It's you!", "mute": "Mute", "muted": "Muted", "per_day": "per day", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index c764005a..80598b0c 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -19,6 +19,7 @@ "username": "Имя пользователя" }, "nav": { + "back": "Назад", "chat": "Локальный чат", "mentions": "Упоминания", "public_tl": "Публичная лента", @@ -126,6 +127,7 @@ "notification_visibility_mentions": "Упоминания", "notification_visibility_repeats": "Повторы", "no_rich_text_description": "Убрать форматирование из всех постов", + "hide_network_description": "Не показывать кого я читаю и кто меня читает", "nsfw_clickthrough": "Включить скрытие NSFW вложений", "panelRadius": "Панели", "pause_on_unfocused": "Приостановить загрузку когда вкладка не в фокусе", diff --git a/src/main.js b/src/main.js index 23ea854b..bf92e78e 100644 --- a/src/main.js +++ b/src/main.js @@ -95,3 +95,9 @@ createPersistedState(persistedStateOptions).then((persistedState) => { afterStoreSetup({ store, i18n }) }) + +// These are inlined by webpack's DefinePlugin +/* eslint-disable */ +window.___pleromafe_mode = process.env +window.___pleromafe_commit_hash = COMMIT_HASH +window.___pleromafe_dev_overrides = DEV_OVERRIDES diff --git a/src/modules/instance.js b/src/modules/instance.js index 7c27d52a..ab88306f 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -25,6 +25,8 @@ const defaultState = { scopeCopy: true, subjectLineBehavior: 'email', loginMethod: 'password', + nsfwCensorImage: undefined, + vapidPublicKey: undefined, // Nasty stuff pleromaBackend: true, diff --git a/src/modules/users.js b/src/modules/users.js index d2ac95cd..25d1c81f 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -17,6 +17,9 @@ export const mergeOrAdd = (arr, obj, item) => { // This is a new item, prepare it arr.push(item) obj[item.id] = item + if (item.screen_name && !item.screen_name.includes('@')) { + obj[item.screen_name] = item + } return { item, new: true } } } @@ -87,7 +90,7 @@ const users = { actions: { fetchUser (store, id) { store.rootState.api.backendInteractor.fetchUser({ id }) - .then((user) => store.commit('addNewUsers', user)) + .then((user) => store.commit('addNewUsers', [user])) }, registerPushNotifications (store) { const token = store.state.currentUser.credentials |
