diff options
| author | Henry Jameson <me@hjkos.com> | 2018-12-18 22:43:26 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2018-12-18 22:43:26 +0300 |
| commit | 81c83566cd8a9bf09b568264e289591c52fbca0d (patch) | |
| tree | 327b6e195cb9820213dd838384353beb22e99f6b /src | |
| parent | 485a531d57108f8adf57ae5043610ef107b26d3e (diff) | |
| parent | f62ff2d894d87c7e9c200687eb2a91bcb111e7e1 (diff) | |
Merge remote-tracking branch 'upstream/develop' into user-profile-overhault
* upstream/develop: (24 commits)
Feature/bigger icons for mobile / #211
Fix oauth url
Make user profile page cleaner
Redirect to /main/all
Move userProfile link to methods
Fix user profile test
Add temporary redirects
Add local profile test
Implement user_profile.spec.js
Use babel polyfill in karma
Use 'userProfileLink' to generate user-profile link
Rollback disableDotRule
Use "-1" in indexOf
Add generateProfileLink
Use "~" as a route namespace symbol
Disable dot rule
Pass userName instead of userId
Typo in tests
Fix redirect paths
Fix typo
...
Diffstat (limited to 'src')
32 files changed, 253 insertions, 138 deletions
diff --git a/src/App.scss b/src/App.scss index 7f33cd51..9c8e2ad3 100644 --- a/src/App.scss +++ b/src/App.scss @@ -237,13 +237,11 @@ i[class*=icon-] { flex-wrap: wrap; .nav-icon { - font-size: 1.1em; margin-left: 0.4em; } &.right { justify-content: flex-end; - padding-right: 20px; } } @@ -251,7 +249,8 @@ i[class*=icon-] { flex: 1 } -nav { +.nav-bar { + padding: 0; width: 100%; align-items: center; position: fixed; @@ -295,10 +294,13 @@ nav { } .inner-nav { + margin: auto; + box-sizing: border-box; + padding-left: 10px; + padding-right: 10px; display: flex; align-items: center; flex-basis: 970px; - margin: auto; height: 50px; a, a i { @@ -466,7 +468,7 @@ nav { &.hidden { opacity: 0; - max-width: 20px; + max-width: 5px; } } } @@ -606,22 +608,8 @@ nav { } } -@media all and (max-width: 959px) { - .mobile-hidden { - display: none; - } - - .panel-switcher { - display: flex; - } - - .container { - padding: 0 0 0 0; - } - - .panel { - margin: 0.5em 0 0.5em 0; - } +.item.right { + text-align: right; } .visibility-tray { @@ -650,3 +638,31 @@ nav { border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); } + +@media all and (max-width: 959px) { + .mobile-hidden { + display: none; + } + + .panel-switcher { + display: flex; + } + + .container { + padding: 0; + } + + .panel { + margin: 0.5em 0 0.5em 0; + } + + .button-icon { + font-size: 1.2em; + } + + .status .status-actions { + div { + max-width: 4em; + } + } +} diff --git a/src/App.vue b/src/App.vue index a3a7ecf6..4d4680e2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,6 @@ <template> <div id="app" v-bind:style="style"> - <nav class='container' @click="scrollToTop()" id="nav"> + <nav class='nav-bar container' @click="scrollToTop()" id="nav"> <div class='logo' :style='logoBgStyle'> <div class='mask' :style='logoMaskStyle'></div> <img :src='logo' :style='logoStyle'> @@ -13,9 +13,9 @@ <router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link> </div> <div class='item right'> - <user-finder class="nav-icon" @toggled="onFinderToggled"></user-finder> - <router-link @click.native="activatePanel('timeline')" :to="{ name: 'settings'}"><i class="icon-cog nav-icon" :title="$t('nav.preferences')"></i></router-link> - <a href="#" v-if="currentUser" @click.prevent="logout"><i class="icon-logout nav-icon" :title="$t('login.logout')"></i></a> + <user-finder class="button-icon nav-icon" @toggled="onFinderToggled"></user-finder> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'settings'}"><i class="button-icon icon-cog nav-icon" :title="$t('nav.preferences')"></i></router-link> + <a href="#" v-if="currentUser" @click.prevent="logout"><i class="button-icon icon-logout nav-icon" :title="$t('login.logout')"></i></a> </div> </div> </nav> diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 24a8d3ac..5b9e5c96 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -1,21 +1,8 @@ import Vue from 'vue' import VueRouter from 'vue-router' +import routes from './routes' import App from '../App.vue' -import PublicTimeline from '../components/public_timeline/public_timeline.vue' -import PublicAndExternalTimeline from '../components/public_and_external_timeline/public_and_external_timeline.vue' -import FriendsTimeline from '../components/friends_timeline/friends_timeline.vue' -import TagTimeline from '../components/tag_timeline/tag_timeline.vue' -import ConversationPage from '../components/conversation-page/conversation-page.vue' -import Mentions from '../components/mentions/mentions.vue' -import DMs from '../components/dm_timeline/dm_timeline.vue' -import UserProfile from '../components/user_profile/user_profile.vue' -import Settings from '../components/settings/settings.vue' -import Registration from '../components/registration/registration.vue' -import UserSettings from '../components/user_settings/user_settings.vue' -import FollowRequests from '../components/follow_requests/follow_requests.vue' -import OAuthCallback from '../components/oauth_callback/oauth_callback.vue' -import UserSearch from '../components/user_search/user_search.vue' const afterStoreSetup = ({ store, i18n }) => { window.fetch('/api/statusnet/config.json') @@ -26,12 +13,17 @@ const afterStoreSetup = ({ store, i18n }) => { store.dispatch('setInstanceOption', { name: 'name', value: name }) store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') }) store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) }) - store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadlimit.uploadlimit) }) - store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadlimit.avatarlimit) }) - store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadlimit.backgroundlimit) }) - store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadlimit.bannerlimit) }) store.dispatch('setInstanceOption', { name: 'server', value: server }) + // TODO: default values for this stuff, added if to not make it break on + // my dev config out of the box. + if (uploadlimit) { + store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadlimit.uploadlimit) }) + store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadlimit.avatarlimit) }) + store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadlimit.backgroundlimit) }) + store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadlimit.bannerlimit) }) + } + if (data.nsfwCensorImage) { store.dispatch('setInstanceOption', { name: 'nsfwCensorImage', value: data.nsfwCensorImage }) } @@ -102,35 +94,10 @@ const afterStoreSetup = ({ store, i18n }) => { store.dispatch('disableChat') } - const routes = [ - { name: 'root', - path: '/', - redirect: to => { - return (store.state.users.currentUser - ? store.state.instance.redirectRootLogin - : store.state.instance.redirectRootNoLogin) || '/main/all' - }}, - { path: '/main/all', component: PublicAndExternalTimeline }, - { path: '/main/public', component: PublicTimeline }, - { path: '/main/friends', component: FriendsTimeline }, - { path: '/tag/:tag', component: TagTimeline }, - { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, - { name: 'user-profile', path: '/users/:id', component: UserProfile }, - { name: 'mentions', path: '/:username/mentions', component: Mentions }, - { name: 'dms', path: '/:username/dms', component: DMs }, - { name: 'settings', path: '/settings', component: Settings }, - { name: 'registration', path: '/registration', component: Registration }, - { name: 'registration', path: '/registration/:token', component: Registration }, - { name: 'friend-requests', path: '/friend-requests', component: FollowRequests }, - { name: 'user-settings', path: '/user-settings', component: UserSettings }, - { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, - { name: 'user-search', path: '/user-search', component: UserSearch, props: (route) => ({ query: route.query.query }) } - ] - const router = new VueRouter({ mode: 'history', - routes, - scrollBehavior: (to, from, savedPosition) => { + routes: routes(store), + scrollBehavior: (to, _from, savedPosition) => { if (to.matched.some(m => m.meta.dontScroll)) { return false } diff --git a/src/boot/routes.js b/src/boot/routes.js new file mode 100644 index 00000000..8ea8cc7f --- /dev/null +++ b/src/boot/routes.js @@ -0,0 +1,67 @@ +import PublicTimeline from 'components/public_timeline/public_timeline.vue' +import PublicAndExternalTimeline from 'components/public_and_external_timeline/public_and_external_timeline.vue' +import FriendsTimeline from 'components/friends_timeline/friends_timeline.vue' +import TagTimeline from 'components/tag_timeline/tag_timeline.vue' +import ConversationPage from 'components/conversation-page/conversation-page.vue' +import Mentions from 'components/mentions/mentions.vue' +import DMs from 'components/dm_timeline/dm_timeline.vue' +import UserProfile from 'components/user_profile/user_profile.vue' +import Settings from 'components/settings/settings.vue' +import Registration from 'components/registration/registration.vue' +import UserSettings from 'components/user_settings/user_settings.vue' +import FollowRequests from 'components/follow_requests/follow_requests.vue' +import OAuthCallback from 'components/oauth_callback/oauth_callback.vue' +import UserSearch from 'components/user_search/user_search.vue' + +export default (store) => { + return [ + { name: 'root', + path: '/', + redirect: _to => { + return (store.state.users.currentUser + ? store.state.instance.redirectRootLogin + : store.state.instance.redirectRootNoLogin) || '/~/main/all' + } + }, + { name: 'public-external-timeline', path: '/~/main/all', component: PublicAndExternalTimeline }, + { name: 'public-timeline', path: '/~/main/public', component: PublicTimeline }, + { name: 'friends', path: '/~/main/friends', component: FriendsTimeline }, + // Beginning of temporary redirects + { path: '/main/:route', + redirect: to => { + const { params } = to + const route = params.route ? params.route : 'all' + + return { path: `/~/main/${route}` } + } + }, + { path: '/tag/:tag', + redirect: to => { + const { params } = to + + return { path: `/~/tag/${params.tag}` } + } + }, + { path: '/notice/:id', + redirect: to => { + const { params } = to + + return { path: `/~/notice/${params.id}` } + } + }, + // End of temporary redirects + { name: 'tag-timeline', path: '/~/tag/:tag', component: TagTimeline }, + { name: 'conversation', path: '/~/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, + { name: 'user-profile', path: '/:name', component: UserProfile }, + { name: 'external-user-profile', path: '/~/users/:id', component: UserProfile }, + { name: 'mentions', path: '/:username/mentions', component: Mentions }, + { name: 'dms', path: '/:username/dms', component: DMs }, + { name: 'settings', path: '/~/settings', component: Settings }, + { name: 'registration', path: '/~/registration', component: Registration }, + { name: 'registration', path: '/~/registration/:token', component: Registration }, + { name: 'friend-requests', path: '/~/friend-requests', component: FollowRequests }, + { name: 'user-settings', path: '/~/user-settings', component: UserSettings }, + { name: 'oauth-callback', path: '/~/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, + { name: 'user-search', path: '/~/user-search', component: UserSearch, props: (route) => ({ query: route.query.query }) } + ] +} diff --git a/src/components/chat_panel/chat_panel.js b/src/components/chat_panel/chat_panel.js index d8736d17..e175e90c 100644 --- a/src/components/chat_panel/chat_panel.js +++ b/src/components/chat_panel/chat_panel.js @@ -1,3 +1,5 @@ +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' + const chatPanel = { data () { return { @@ -18,6 +20,9 @@ const chatPanel = { }, togglePanel () { this.collapsed = !this.collapsed + }, + userProfileLink (user) { + return generateProfileLink(user.id, user.screen_name) } } } diff --git a/src/components/chat_panel/chat_panel.vue b/src/components/chat_panel/chat_panel.vue index f174319a..1b9c63ff 100644 --- a/src/components/chat_panel/chat_panel.vue +++ b/src/components/chat_panel/chat_panel.vue @@ -13,8 +13,10 @@ <img :src="message.author.avatar" /> </span> <div class="chat-content"> - <router-link class="chat-name" :to="{ name: 'user-profile', params: { id: message.author.id } }"> - {{message.author.username}} + <router-link + class="chat-name" + :to="userProfileLink(message.author)"> + {{message.author.username}} </router-link> <br> <span class="chat-text"> @@ -67,9 +69,6 @@ overflow-x: hidden; } -.chat-name { -} - .chat-message { display: flex; padding: 0.2em 0.5em diff --git a/src/components/delete_button/delete_button.vue b/src/components/delete_button/delete_button.vue index b458b0dc..f4c91cfd 100644 --- a/src/components/delete_button/delete_button.vue +++ b/src/components/delete_button/delete_button.vue @@ -1,7 +1,7 @@ <template> <div v-if="canDelete"> <a href="#" v-on:click.prevent="deleteStatus()"> - <i class='icon-cancel delete-status'></i> + <i class='button-icon icon-cancel delete-status'></i> </a> </div> </template> diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue index 1decd070..05ce6bd0 100644 --- a/src/components/favorite_button/favorite_button.vue +++ b/src/components/favorite_button/favorite_button.vue @@ -1,10 +1,10 @@ <template> <div v-if="loggedIn"> - <i :class='classes' class='favorite-button fav-active' @click.prevent='favorite()' :title="$t('tool_tip.favorite')"/> + <i :class='classes' class='button-icon favorite-button fav-active' @click.prevent='favorite()' :title="$t('tool_tip.favorite')"/> <span v-if='!hidePostStatsLocal && status.fave_num > 0'>{{status.fave_num}}</span> </div> <div v-else> - <i :class='classes' class='favorite-button' :title="$t('tool_tip.favorite')"/> + <i :class='classes' class='button-icon favorite-button' :title="$t('tool_tip.favorite')"/> <span v-if='!hidePostStatsLocal && status.fave_num > 0'>{{status.fave_num}}</span> </div> </template> diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index 49868aed..81426b44 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -32,7 +32,7 @@ const LoginForm = { .then((result) => { this.$store.commit('setToken', result.access_token) this.$store.dispatch('loginUser', result.access_token) - this.$router.push('/main/friends') + this.$router.push('/~/main/friends') }) }) } diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index b224c5f3..c52d1e52 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -3,7 +3,7 @@ <div class="panel panel-default"> <ul> <li v-if='currentUser'> - <router-link @click.native="activatePanel('timeline')" to='/main/friends'> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'friends' }"> {{ $t("nav.timeline") }} </router-link> </li> @@ -18,17 +18,17 @@ </router-link> </li> <li v-if='currentUser && currentUser.locked'> - <router-link @click.native="activatePanel('timeline')" to='/friend-requests'> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'friend-requests' }"> {{ $t("nav.friend_requests") }} </router-link> </li> <li> - <router-link @click.native="activatePanel('timeline')" to='/main/public'> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'public-timeline' }"> {{ $t("nav.public_tl") }} </router-link> </li> <li> - <router-link @click.native="activatePanel('timeline')" to='/main/all'> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'public-external-timeline' }"> {{ $t("nav.twkn") }} </router-link> </li> diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 345fe3ee..9ab870b6 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -2,6 +2,7 @@ import Status from '../status/status.vue' import StillImage from '../still-image/still-image.vue' import UserCardContent from '../user_card_content/user_card_content.vue' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const Notification = { data () { @@ -20,6 +21,9 @@ const Notification = { methods: { toggleUserExpanded () { this.userExpanded = !this.userExpanded + }, + userProfileLink (user) { + return generateProfileLink(user.id, user.screen_name) } }, computed: { diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index e84ce0b6..1c89cbb7 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -28,7 +28,9 @@ <small class="timeago"><router-link @click.native="activatePanel('timeline')" v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }"><timeago :since="notification.action.created_at" :auto-update="240"></timeago></router-link></small> </span> <div class="follow-text" v-if="notification.type === 'follow'"> - <router-link @click.native="activatePanel('timeline')" :to="{ name: 'user-profile', params: { id: notification.action.user.id } }">@{{notification.action.user.screen_name}}</router-link> + <router-link @click.native="activatePanel('timeline')" :to="userProfileLink(notification.action.user)"> + @{{notification.action.user.screen_name}} + </router-link> </div> <template v-else> <status :activatePanel="activatePanel" v-if="notification.status" class="faint" :compact="true" :statusoid="notification.status" :noHeading="true"></status> diff --git a/src/components/oauth_callback/oauth_callback.js b/src/components/oauth_callback/oauth_callback.js index 7a5132ad..18e24159 100644 --- a/src/components/oauth_callback/oauth_callback.js +++ b/src/components/oauth_callback/oauth_callback.js @@ -11,7 +11,7 @@ const oac = { }).then((result) => { this.$store.commit('setToken', result.access_token) this.$store.dispatch('loginUser', result.access_token) - this.$router.push('/main/friends') + this.$router.push('/~/main/friends') }) } } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 4776c819..398f1871 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -7,7 +7,7 @@ path="post_status.account_not_locked_warning" tag="p" class="visibility-notice"> - <router-link to="/user-settings">{{ $t('post_status.account_not_locked_warning_link') }}</router-link> + <router-link :to="{ name: 'user-settings' }">{{ $t('post_status.account_not_locked_warning_link') }}</router-link> </i18n> <p v-if="this.newStatus.visibility == 'direct'" class="visibility-notice">{{ $t('post_status.direct_warning') }}</p> <input @@ -75,11 +75,11 @@ </div> <div class='alert error' v-if="error"> Error: {{ error }} - <i class="icon-cancel" @click="clearError"></i> + <i class="button-icon icon-cancel" @click="clearError"></i> </div> <div class="attachments"> <div class="media-upload-wrapper" v-for="file in newStatus.files"> - <i class="fa icon-cancel" @click="removeMediaFile(file)"></i> + <i class="fa button-icon icon-cancel" @click="removeMediaFile(file)"></i> <div class="media-upload-container attachment"> <img class="thumbnail media-upload" :src="file.image" v-if="type(file) === 'image'"></img> <video v-if="type(file) === 'video'" :src="file.image" controls></video> diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index a6c79e87..65b2fb9b 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -28,7 +28,7 @@ const registration = { }, created () { if ((!this.registrationOpen && !this.token) || this.signedIn) { - this.$router.push('/main/all') + this.$router.push('/~/main/all') } this.setCaptcha() @@ -56,7 +56,7 @@ const registration = { if (!this.$v.$invalid) { try { await this.signUp(this.user) - this.$router.push('/main/friends') + this.$router.push('/~/main/friends') } catch (error) { console.warn('Registration failed: ' + error) } diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue index c957fb77..6370f9dc 100644 --- a/src/components/retweet_button/retweet_button.vue +++ b/src/components/retweet_button/retweet_button.vue @@ -1,15 +1,15 @@ <template> <div v-if="loggedIn"> <template v-if="visibility !== 'private' && visibility !== 'direct'"> - <i :class='classes' class='retweet-button icon-retweet rt-active' v-on:click.prevent='retweet()' :title="$t('tool_tip.repeat')"></i> + <i :class='classes' class='button-icon retweet-button icon-retweet rt-active' v-on:click.prevent='retweet()' :title="$t('tool_tip.repeat')"></i> <span v-if='!hidePostStatsLocal && status.repeat_num > 0'>{{status.repeat_num}}</span> </template> <template v-else> - <i :class='classes' class='icon-lock' :title="$t('timeline.no_retweet_hint')"></i> + <i :class='classes' class='button-icon icon-lock' :title="$t('timeline.no_retweet_hint')"></i> </template> </div> <div v-else-if="!loggedIn"> - <i :class='classes' class='icon-retweet' :title="$t('tool_tip.repeat')"></i> + <i :class='classes' class='button-icon icon-retweet' :title="$t('tool_tip.repeat')"></i> <span v-if='!hidePostStatsLocal && status.repeat_num > 0'>{{status.repeat_num}}</span> </div> </template> diff --git a/src/components/status/status.js b/src/components/status/status.js index 9a63d047..47a62fdf 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -7,6 +7,7 @@ import UserCardContent from '../user_card_content/user_card_content.vue' import StillImage from '../still-image/still-image.vue' import { filter, find } from 'lodash' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const Status = { name: 'Status', @@ -286,6 +287,9 @@ const Status = { }, replyLeave () { this.showPreview = false + }, + userProfileLink (id, name) { + return generateProfileLink(id, name) } }, watch: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 067980ac..1b102d93 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -2,9 +2,13 @@ <div class="status-el" v-if="!hideReply && !deleted" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"> <template v-if="muted && !noReplyLinks"> <div class="media status container muted"> - <small><router-link @click.native="activatePanel('timeline')" :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small> + <small> + <router-link @click.native="activatePanel('timeline')" :to="userProfileLink(status.user.id, status.user.screen_name)"> + {{status.user.screen_name}} + </router-link> + </small> <small class="muteWords">{{muteWordHits.join(', ')}}</small> - <a href="#" class="unmute" @click.prevent="toggleMute"><i class="icon-eye-off"></i></a> + <a href="#" class="unmute" @click.prevent="toggleMute"><i class="button-icon icon-eye-off"></i></a> </div> </template> <template v-else> @@ -34,15 +38,17 @@ <h4 class="user-name" v-if="status.user.name_html" v-html="status.user.name_html"></h4> <h4 class="user-name" v-else>{{status.user.name}}</h4> <span class="links"> - <router-link @click.native="activatePanel('timeline')" :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link> + <router-link @click.native="activatePanel('timeline')" :to="userProfileLink(status.user.id, status.user.screen_name)"> + {{status.user.screen_name}} + </router-link> <span v-if="status.in_reply_to_screen_name" class="faint reply-info"> <i class="icon-right-open"></i> - <router-link @click.native="activatePanel('timeline')" :to="{ name: 'user-profile', params: { id: status.in_reply_to_user_id } }"> + <router-link @click.native="activatePanel('timeline')" :to="userProfileLink(status.in_reply_to_user_id, status.in_reply_to_screen_name)"> {{status.in_reply_to_screen_name}} </router-link> </span> <a v-if="isReply && !noReplyLinks" href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)" :title="$t('tool_tip.reply')"> - <i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i> + <i class="button-icon icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i> </a> </span> </div> @@ -57,18 +63,18 @@ <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"> + <div class="button-icon visibility-icon" v-if="status.visibility"> <i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i> </div> <a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url" title="Source"> - <i class="icon-link-ext-alt"></i> + <i class="button-icon icon-link-ext-alt"></i> </a> <template v-if="expandable"> <a href="#" @click.prevent="toggleExpanded" title="Expand"> - <i class="icon-plus-squared"></i> + <i class="button-icon icon-plus-squared"></i> </a> </template> - <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="icon-eye-off"></i></a> + <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="button-icon icon-eye-off"></i></a> </div> </div> @@ -95,7 +101,7 @@ <div v-if="!noHeading && !noReplyLinks" class='status-actions media-body'> <div v-if="loggedIn"> <a href="#" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')"> - <i class="icon-reply" :class="{'icon-reply-active': replying}"></i> + <i class="button-icon icon-reply" :class="{'icon-reply-active': replying}"></i> </a> </div> <retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button> @@ -474,7 +480,7 @@ } } -.avatar { +.avatar.still-image { width: 48px; height: 48px; box-shadow: var(--avatarStatusShadow); diff --git a/src/components/style_switcher/preview.vue b/src/components/style_switcher/preview.vue index 09a136e9..634f5b35 100644 --- a/src/components/style_switcher/preview.vue +++ b/src/components/style_switcher/preview.vue @@ -37,10 +37,10 @@ </i18n> <div class="icons"> - <i style="color: var(--cBlue)" class="icon-reply"/> - <i style="color: var(--cGreen)" class="icon-retweet"/> - <i style="color: var(--cOrange)" class="icon-star"/> - <i style="color: var(--cRed)" class="icon-cancel"/> + <i style="color: var(--cBlue)" class="button-icon icon-reply"/> + <i style="color: var(--cGreen)" class="button-icon icon-retweet"/> + <i style="color: var(--cOrange)" class="button-icon icon-star"/> + <i style="color: var(--cRed)" class="button-icon icon-cancel"/> </div> </div> </div> diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index e674627a..f0fff335 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -1,5 +1,6 @@ import UserCardContent from '../user_card_content/user_card_content.vue' import StillImage from '../still-image/still-image.vue' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const UserCard = { props: [ @@ -30,6 +31,9 @@ const UserCard = { denyUser () { this.$store.state.api.backendInteractor.denyUser(this.user.id) this.$store.dispatch('removeFollowRequest', this.user) + }, + userProfileLink (user) { + return generateProfileLink(user.id, user.screen_name) } } } diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index a16960cd..cf69606d 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -19,7 +19,8 @@ {{ 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 } }"> + + <router-link class='user-screen-name' :to="userProfileLink(user)"> @{{user.screen_name}} </router-link> </div> diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 206dba88..75185053 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -1,5 +1,6 @@ import StillImage from '../still-image/still-image.vue' import { hex2rgb } from '../../services/color_convert/color_convert.js' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' export default { props: [ 'user', 'switcher', 'selected', 'hideBio', 'activatePanel' ], @@ -177,6 +178,9 @@ export default { if (target.tagName === 'A') { window.open(target.href, '_blank') } + }, + userProfileLink (user) { + return generateProfileLink(user.id, user.screen_name) } } } diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index f80e30ff..c5222519 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -2,20 +2,20 @@ <div id="heading" class="profile-panel-background" :style="headingStyle"> <div class="panel-heading text-center"> <div class='user-info'> - <router-link @click.native="activatePanel && activatePanel('timeline')" to='/user-settings' style="float: right; margin-top:16px;" v-if="!isOtherUser"> - <i class="icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i> + <router-link @click.native="activatePanel && activatePanel('timeline')" :to="{ name: 'user-settings' }" style="float: right; margin-top:16px;" v-if="!isOtherUser"> + <i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i> </router-link> <a :href="user.statusnet_profile_url" target="_blank" class="floater" v-if="isOtherUser"> <i class="icon-link-ext usersettings"></i> </a> <div class='container'> - <router-link @click.native="activatePanel && activatePanel('timeline')" :to="{ name: 'user-profile', params: { id: user.id } }"> + <router-link @click.native="activatePanel && activatePanel('timeline')" :to="userProfileLink(user)"> <StillImage class="avatar" :class='{ "better-shadow": betterShadow }' :src="user.profile_image_url_original"/> </router-link> <div class="name-and-screen-name"> <div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div> <div :title="user.name" class='user-name' v-else>{{user.name}}</div> - <router-link @click.native="activatePanel && activatePanel('timeline')" class='user-screen-name':to="{ name: 'user-profile', params: { id: user.id } }"> + <router-link @click.native="activatePanel && activatePanel('timeline')" class='user-screen-name' :to="userProfileLink(user)"> <span>@{{user.screen_name}}</span><span v-if="user.locked"><i class="icon icon-lock"></i></span> <span v-if="!hideUserStatsLocal" class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span> </router-link> @@ -25,7 +25,7 @@ <div v-if="user.follows_you && loggedIn && isOtherUser" class="following"> {{ $t('user_card.follows_you') }} </div> - <div class="floater" v-if="switcher || isOtherUser"> + <div class="floater" v-if="isOtherUser && (loggedIn || !switcher)"> <!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to --> <input class="userHighlightText" type="text" :id="'userHighlightColorTx'+user.id" v-if="userHighlightType !== 'disabled'" v-model="userHighlightColor"/> <input class="userHighlightCl" type="color" :id="'userHighlightColor'+user.id" v-if="userHighlightType !== 'disabled'" v-model="userHighlightColor"/> @@ -67,7 +67,7 @@ </button> </span> </div> - <div class='mute' v-if='isOtherUser'> + <div class='mute' v-if='isOtherUser && loggedIn'> <span v-if='user.muted'> <button @click="toggleMute" class="pressed"> {{ $t('user_card.muted') }} diff --git a/src/components/user_finder/user_finder.vue b/src/components/user_finder/user_finder.vue index 9efdfab7..eeb76c35 100644 --- a/src/components/user_finder/user_finder.vue +++ b/src/components/user_finder/user_finder.vue @@ -7,7 +7,7 @@ <button class="btn search-button" @click="findUser(username)"> <i class="icon-search"/> </button> - <i class="icon-cancel user-finder-icon" @click.prevent.stop="toggleHidden"/> + <i class="button-icon icon-cancel user-finder-icon" @click.prevent.stop="toggleHidden"/> </template> </div> </template> @@ -29,7 +29,8 @@ height: 29px; } .user-finder-input { - max-width: 80%; + // TODO: do this properly without a rough guesstimate of 2 icons + paddings + max-width: calc(100% - 30px - 30px - 20px); } .search-button { diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 8ff0daad..deee77dd 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -4,32 +4,40 @@ import Timeline from '../timeline/timeline.vue' const UserProfile = { created () { + debugger this.$store.commit('clearTimeline', { timeline: 'user' }) - this.$store.dispatch('startFetching', ['user', this.userId]) - if (!this.$store.state.users.usersObject[this.userId]) { - this.$store.dispatch('fetchUser', this.userId) + this.$store.dispatch('startFetching', ['user', this.fetchBy]) + if (!this.user) { + this.$store.dispatch('fetchUser', this.fetchBy) } }, destroyed () { this.$store.dispatch('stopFetching', 'user') }, computed: { - timeline () { return this.$store.state.statuses.timelines.user }, - friends () { - return this.user.friends - }, - followers () { - return this.user.followers + timeline () { + return this.$store.state.statuses.timelines.user }, userId () { return this.$route.params.id }, + userName () { + return this.$route.params.name + }, user () { if (this.timeline.statuses[0]) { return this.timeline.statuses[0].user } else { - return this.$store.state.users.usersObject[this.userId] || false + return Object.values(this.$store.state.users.usersObject).filter(user => { + return (this.isExternal ? user.id === this.userId : user.screen_name === this.userName) + })[0] || false } + }, + fetchBy () { + return this.isExternal ? this.userId : this.userName + }, + isExternal () { + return this.$route.name === 'external-user-profile' } }, methods: { @@ -43,7 +51,18 @@ const UserProfile = { } }, watch: { + userName () { + if (this.isExternal) { + return + } + this.$store.dispatch('stopFetching', 'user') + this.$store.commit('clearTimeline', { timeline: 'user' }) + this.$store.dispatch('startFetching', ['user', this.userName]) + }, userId () { + if (!this.isExternal) { + return + } this.$store.dispatch('stopFetching', 'user') this.$store.commit('clearTimeline', { timeline: 'user' }) this.$store.dispatch('startFetching', ['user', this.userId]) diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index ee989f71..d5fac17d 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -257,7 +257,7 @@ const UserSettings = { .then((res) => { if (res.status === 'success') { this.$store.dispatch('logout') - this.$router.push('/main/all') + this.$router.push('/~/main/all') } else { this.deleteAccountError = res.error } diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index b238571d..4bc2eeec 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -49,7 +49,7 @@ <button class="btn btn-default" v-else-if="avatarPreview" @click="submitAvatar">{{$t('general.submit')}}</button> <div class='alert error' v-if="avatarUploadError"> Error: {{ avatarUploadError }} - <i class="icon-cancel" @click="clearUploadError('avatar')"></i> + <i class="button-icon icon-cancel" @click="clearUploadError('avatar')"></i> </div> </div> <div class="setting-item"> @@ -66,7 +66,7 @@ <button class="btn btn-default" v-else-if="bannerPreview" @click="submitBanner">{{$t('general.submit')}}</button> <div class='alert error' v-if="bannerUploadError"> Error: {{ bannerUploadError }} - <i class="icon-cancel" @click="clearUploadError('banner')"></i> + <i class="button-icon icon-cancel" @click="clearUploadError('banner')"></i> </div> </div> <div class="setting-item"> @@ -81,7 +81,7 @@ <button class="btn btn-default" v-else-if="backgroundPreview" @click="submitBg">{{$t('general.submit')}}</button> <div class='alert error' v-if="backgroundUploadError"> Error: {{ backgroundUploadError }} - <i class="icon-cancel" @click="clearUploadError('background')"></i> + <i class="button-icon icon-cancel" @click="clearUploadError('background')"></i> </div> </div> </div> diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js index 49b8f5b6..c2df6899 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.js +++ b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -1,4 +1,5 @@ import apiService from '../../services/api/api.service.js' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' function showWhoToFollow (panel, reply) { var users = reply @@ -93,6 +94,11 @@ const WhoToFollowPanel = { return this.$store.state.instance.suggestionsEnabled } }, + methods: { + userProfileLink (id, name) { + return generateProfileLink(id, name) + } + }, watch: { user: function (user, oldUser) { if (this.suggestionsEnabled) { diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.vue b/src/components/who_to_follow_panel/who_to_follow_panel.vue index d031318d..a62e8360 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.vue +++ b/src/components/who_to_follow_panel/who_to_follow_panel.vue @@ -8,9 +8,9 @@ </div> <div class="panel-body who-to-follow"> <p> - <img v-bind:src="img1"/> <router-link :to="{ name: 'user-profile', params: { id: id1 } }">{{ name1 }}</router-link><br> - <img v-bind:src="img2"/> <router-link :to="{ name: 'user-profile', params: { id: id2 } }">{{ name2 }}</router-link><br> - <img v-bind:src="img3"/> <router-link :to="{ name: 'user-profile', params: { id: id3 } }">{{ name3 }}</router-link><br> + <img v-bind:src="img1"/> <router-link :to="userProfileLink(id1, name1)">{{ name1 }}</router-link><br> + <img v-bind:src="img2"/> <router-link :to="userProfileLink(id2, name2)">{{ name2 }}</router-link><br> + <img v-bind:src="img3"/> <router-link :to="userProfileLink(id3, name3)">{{ name3 }}</router-link><br> <img v-bind:src="$store.state.instance.logo"> <a v-bind:href="moreUrl" target="_blank">{{$t('who_to_follow.more')}}</a> </p> </div> diff --git a/src/modules/instance.js b/src/modules/instance.js index ab88306f..342bc9ac 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -12,8 +12,8 @@ const defaultState = { logo: '/static/logo.png', logoMask: true, logoMargin: '.2em', - redirectRootNoLogin: '/main/all', - redirectRootLogin: '/main/friends', + redirectRootNoLogin: '/~/main/all', + redirectRootLogin: '/~/main/friends', showInstanceSpecificPanel: false, scopeOptionsEnabled: true, formattingOptionsEnabled: false, diff --git a/src/services/new_api/oauth.js b/src/services/new_api/oauth.js index 9e656507..97dec94f 100644 --- a/src/services/new_api/oauth.js +++ b/src/services/new_api/oauth.js @@ -5,7 +5,7 @@ const getOrCreateApp = ({oauth, instance}) => { const form = new window.FormData() form.append('client_name', `PleromaFE_${Math.random()}`) - form.append('redirect_uris', `${window.location.origin}/oauth-callback`) + form.append('redirect_uris', `${window.location.origin}/~/oauth-callback`) form.append('scopes', 'read write follow') return window.fetch(url, { @@ -64,7 +64,7 @@ const getToken = ({app, instance, code}) => { form.append('client_secret', app.client_secret) form.append('grant_type', 'authorization_code') form.append('code', code) - form.append('redirect_uri', `${window.location.origin}/oauth-callback`) + form.append('redirect_uri', `${window.location.origin}/~/oauth-callback`) return window.fetch(url, { method: 'POST', diff --git a/src/services/user_profile_link_generator/user_profile_link_generator.js b/src/services/user_profile_link_generator/user_profile_link_generator.js new file mode 100644 index 00000000..3367eb8a --- /dev/null +++ b/src/services/user_profile_link_generator/user_profile_link_generator.js @@ -0,0 +1,10 @@ +const generateProfileLink = (id, screenName) => { + return { + name: (isExternal(screenName) ? 'external-user-profile' : 'user-profile'), + params: (isExternal(screenName) ? { id } : { name: screenName }) + } +} + +const isExternal = screenName => screenName.includes('@') + +export default generateProfileLink |
