From e20a7be3aa4973400cd670347ff038a78eb12e4e Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 20 Feb 2019 10:13:28 -0500 Subject: #376: update status timeline when it's empty --- src/components/user_profile/user_profile.vue | 1 + 1 file changed, 1 insertion(+) (limited to 'src/components/user_profile/user_profile.vue') diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 79461291..09fb93de 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -10,6 +10,7 @@ Date: Thu, 21 Feb 2019 13:32:47 -0500 Subject: Show error message when visit profile page of invalid user --- src/components/user_profile/user_profile.js | 8 ++++++++ src/components/user_profile/user_profile.vue | 3 ++- src/i18n/en.json | 3 ++- src/modules/users.js | 2 +- src/services/api/api.service.js | 6 ++++++ 5 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src/components/user_profile/user_profile.vue') diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 37179ce1..44192e9a 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -4,6 +4,11 @@ import Timeline from '../timeline/timeline.vue' import FollowList from '../follow_list/follow_list.vue' const UserProfile = { + data () { + return { + error: false + } + }, created () { this.$store.commit('clearTimeline', { timeline: 'user' }) this.$store.commit('clearTimeline', { timeline: 'favorites' }) @@ -13,6 +18,9 @@ const UserProfile = { this.startFetchFavorites() if (!this.user.id) { this.$store.dispatch('fetchUser', this.fetchBy) + .catch(() => { + this.error = true + }) } }, destroyed () { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 09fb93de..ccebe20b 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -55,7 +55,8 @@
- + {{ $t('user_profile.profile_does_not_exist') }} +
diff --git a/src/i18n/en.json b/src/i18n/en.json index 64753f1d..c482ecb6 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -383,7 +383,8 @@ "mute_progress": "Muting..." }, "user_profile": { - "timeline_title": "User Timeline" + "timeline_title": "User Timeline", + "profile_does_not_exist": "Sorry, this profile does not exist." }, "who_to_follow": { "more": "More", diff --git a/src/modules/users.js b/src/modules/users.js index 77df7168..eabfe5ae 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -140,7 +140,7 @@ const users = { getters, actions: { fetchUser (store, id) { - store.rootState.api.backendInteractor.fetchUser({ id }) + return store.rootState.api.backendInteractor.fetchUser({ id }) .then((user) => store.commit('addNewUsers', [user])) }, fetchBlocks (store) { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 3d2e8823..d8716596 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -244,6 +244,12 @@ const denyUser = ({id, credentials}) => { const fetchUser = ({id, credentials}) => { let url = `${USER_URL}?user_id=${id}` return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => { + if (!data.ok) { + throw Error(data.statusText) + } + return data + }) .then((data) => data.json()) .then((data) => parseUser(data)) } -- cgit v1.2.3-70-g09d2 From b78227456ea6b1a80cd85988d3ef91cb654a881c Mon Sep 17 00:00:00 2001 From: taehoon Date: Tue, 26 Feb 2019 12:26:04 -0500 Subject: Better error handling --- src/components/user_profile/user_profile.js | 12 ++++++++++-- src/components/user_profile/user_profile.vue | 2 +- src/i18n/en.json | 3 ++- src/services/api/api.service.js | 15 +++++++++------ src/services/errors/errors.js | 14 ++++++++++++++ 5 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 src/services/errors/errors.js (limited to 'src/components/user_profile/user_profile.vue') diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 44192e9a..ebf6c61a 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -1,3 +1,4 @@ +import get from 'lodash/get' import UserCardContent from '../user_card_content/user_card_content.vue' import UserCard from '../user_card/user_card.vue' import Timeline from '../timeline/timeline.vue' @@ -18,8 +19,15 @@ const UserProfile = { this.startFetchFavorites() if (!this.user.id) { this.$store.dispatch('fetchUser', this.fetchBy) - .catch(() => { - this.error = true + .catch((reason) => { + const errorMessage = get(reason, 'error.error') + if (errorMessage === 'No user with such user_id') { // Known error + this.error = this.$t('user_profile.profile_does_not_exist') + } else if (errorMessage) { + this.error = errorMessage + } else { + this.error = this.$t('user_profile.profile_loading_error') + } }) } }, diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index ccebe20b..ba1a7760 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -55,7 +55,7 @@
- {{ $t('user_profile.profile_does_not_exist') }} + {{ error }}
diff --git a/src/i18n/en.json b/src/i18n/en.json index c482ecb6..9fdd1692 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -384,7 +384,8 @@ }, "user_profile": { "timeline_title": "User Timeline", - "profile_does_not_exist": "Sorry, this profile does not exist." + "profile_does_not_exist": "Sorry, this profile does not exist.", + "profile_loading_error": "Sorry, there was an error loading this profile." }, "who_to_follow": { "more": "More", diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index d8716596..fbe5afa0 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -47,6 +47,7 @@ const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' import 'whatwg-fetch' +import { StatusCodeError } from '../errors/errors' const oldfetch = window.fetch @@ -244,13 +245,15 @@ const denyUser = ({id, credentials}) => { const fetchUser = ({id, credentials}) => { let url = `${USER_URL}?user_id=${id}` return fetch(url, { headers: authHeaders(credentials) }) - .then((data) => { - if (!data.ok) { - throw Error(data.statusText) - } - return data + .then((response) => { + return new Promise((resolve, reject) => response.json() + .then((json) => { + if (!response.ok) { + return reject(new StatusCodeError(response.status, json, { url }, response)) + } + return resolve(json) + })) }) - .then((data) => data.json()) .then((data) => parseUser(data)) } diff --git a/src/services/errors/errors.js b/src/services/errors/errors.js new file mode 100644 index 00000000..548f3c68 --- /dev/null +++ b/src/services/errors/errors.js @@ -0,0 +1,14 @@ +export function StatusCodeError (statusCode, body, options, response) { + this.name = 'StatusCodeError' + this.statusCode = statusCode + this.message = statusCode + ' - ' + (JSON && JSON.stringify ? JSON.stringify(body) : body) + this.error = body // legacy attribute + this.options = options + this.response = response + + if (Error.captureStackTrace) { // required for non-V8 environments + Error.captureStackTrace(this) + } +} +StatusCodeError.prototype = Object.create(Error.prototype) +StatusCodeError.prototype.constructor = StatusCodeError -- cgit v1.2.3-70-g09d2 From 080786c9458ba8b9db1ea63732824a3e297e10dc Mon Sep 17 00:00:00 2001 From: taehoon Date: Mon, 25 Feb 2019 04:51:23 -0500 Subject: Rewrite FollowList using hocs --- src/components/follow_list/follow_list.js | 68 ---------------------------- src/components/follow_list/follow_list.vue | 33 -------------- src/components/user_profile/user_profile.js | 29 +++++++++++- src/components/user_profile/user_profile.vue | 10 +--- src/hocs/with_load_more/with_load_more.js | 2 + src/modules/users.js | 19 ++++++-- 6 files changed, 45 insertions(+), 116 deletions(-) delete mode 100644 src/components/follow_list/follow_list.js delete mode 100644 src/components/follow_list/follow_list.vue (limited to 'src/components/user_profile/user_profile.vue') diff --git a/src/components/follow_list/follow_list.js b/src/components/follow_list/follow_list.js deleted file mode 100644 index 9777c87e..00000000 --- a/src/components/follow_list/follow_list.js +++ /dev/null @@ -1,68 +0,0 @@ -import UserCard from '../user_card/user_card.vue' - -const FollowList = { - data () { - return { - loading: false, - bottomedOut: false, - error: false - } - }, - props: ['userId', 'showFollowers'], - created () { - window.addEventListener('scroll', this.scrollLoad) - if (this.entries.length === 0) { - this.fetchEntries() - } - }, - destroyed () { - window.removeEventListener('scroll', this.scrollLoad) - this.$store.dispatch('clearFriendsAndFollowers', this.userId) - }, - computed: { - user () { - return this.$store.getters.userById(this.userId) - }, - entries () { - return this.showFollowers ? this.user.followers : this.user.friends - }, - showFollowsYou () { - return !this.showFollowers || (this.showFollowers && this.userId !== this.$store.state.users.currentUser.id) - } - }, - methods: { - fetchEntries () { - if (!this.loading) { - const command = this.showFollowers ? 'addFollowers' : 'addFriends' - this.loading = true - this.$store.dispatch(command, this.userId).then(entries => { - this.error = false - this.loading = false - this.bottomedOut = entries.length === 0 - }).catch(() => { - this.error = true - this.loading = false - }) - } - }, - scrollLoad (e) { - const bodyBRect = document.body.getBoundingClientRect() - const height = Math.max(bodyBRect.height, -(bodyBRect.y)) - if (this.loading === false && - this.bottomedOut === false && - this.$el.offsetHeight > 0 && - (window.innerHeight + window.pageYOffset) >= (height - 750) - ) { - this.fetchEntries() - } - } - }, - watch: { - 'user': 'fetchEntries' - }, - components: { - UserCard - } -} - -export default FollowList diff --git a/src/components/follow_list/follow_list.vue b/src/components/follow_list/follow_list.vue deleted file mode 100644 index 27102edf..00000000 --- a/src/components/follow_list/follow_list.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - - - diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index ebf6c61a..9f9d43e4 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -1,8 +1,32 @@ +import { compose } from 'vue-compose' import get from 'lodash/get' import UserCardContent from '../user_card_content/user_card_content.vue' import UserCard from '../user_card/user_card.vue' import Timeline from '../timeline/timeline.vue' -import FollowList from '../follow_list/follow_list.vue' +import withLoadMore from '../../hocs/with_load_more/with_load_more' +import withList from '../../hocs/with_list/with_list' + +const FollowerList = compose( + withLoadMore({ + fetch: (props, $store) => $store.dispatch('addFollowers', props.userId), + select: (props, $store) => get($store.getters.userById(props.userId), 'followers', []), + destory: (props, $store) => $store.dispatch('clearFollowers', props.userId), + childPropName: 'entries', + additionalPropNames: ['userId'] + }), + withList({ getEntryProps: user => ({ user }) }) +)(UserCard) + +const FriendList = compose( + withLoadMore({ + fetch: (props, $store) => $store.dispatch('addFriends', props.userId), + select: (props, $store) => get($store.getters.userById(props.userId), 'friends', []), + destory: (props, $store) => $store.dispatch('clearFriends', props.userId), + childPropName: 'entries', + additionalPropNames: ['userId'] + }), + withList({ getEntryProps: user => ({ user }) }) +)(UserCard) const UserProfile = { data () { @@ -123,7 +147,8 @@ const UserProfile = { UserCardContent, UserCard, Timeline, - FollowList + FollowerList, + FriendList } } diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index ba1a7760..a3d2825f 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -18,16 +18,10 @@ :user-id="fetchBy" />
- -
- -
+
- -
- -
+
(WrappedComponent) => { @@ -58,6 +59,7 @@ const withLoadMore = ({ }, destroyed () { window.removeEventListener('scroll', this.scrollLoad) + destroy && destroy(this.$props, this.$store) }, methods: { fetchEntries () { diff --git a/src/modules/users.js b/src/modules/users.js index b2821a92..093af497 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -72,14 +72,20 @@ export const mutations = { }, // Because frontend doesn't have a reason to keep these stuff in memory // outside of viewing someones user profile. - clearFriendsAndFollowers (state, userKey) { - const user = state.usersObject[userKey] + clearFriends (state, userId) { + const user = state.usersObject[userId] if (!user) { return } user.friends = [] - user.followers = [] user.friendsPage = 0 + }, + clearFollowers (state, userId) { + const user = state.usersObject[userId] + if (!user) { + return + } + user.followers = [] user.followersPage = 0 }, addNewUsers (state, users) { @@ -197,8 +203,11 @@ const users = { return followers }) }, - clearFriendsAndFollowers ({ commit }, userKey) { - commit('clearFriendsAndFollowers', userKey) + clearFriends ({ commit }, userId) { + commit('clearFriends', userId) + }, + clearFollowers ({ commit }, userId) { + commit('clearFollowers', userId) }, registerPushNotifications (store) { const token = store.state.currentUser.credentials -- cgit v1.2.3-70-g09d2 From 3d30ad1dda8f31960586625bb6a432d6b3adde8e Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 3 Mar 2019 12:53:01 -0500 Subject: #417: refresh tab on user profile only --- src/components/tab_switcher/tab_switcher.js | 6 ++++-- src/components/user_profile/user_profile.vue | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/components/user_profile/user_profile.vue') diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index 1de936e8..03da8249 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -4,7 +4,7 @@ import './tab_switcher.scss' export default Vue.component('tab-switcher', { name: 'TabSwitcher', - props: ['renderOnlyFocused'], + props: ['refresh', 'renderOnlyFocused'], data () { return { active: this.$slots.default.findIndex(_ => _.tag) @@ -12,7 +12,9 @@ export default Vue.component('tab-switcher', { }, watch: { $route () { - this.activateTab(0) + if (this.refresh) { + this.active = 0 + } } }, methods: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index a3d2825f..54f1b97b 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -6,7 +6,7 @@ :switcher="true" :selected="timeline.viewing" /> - + Date: Sun, 3 Mar 2019 13:38:48 -0500 Subject: #417: reset tab from the outside --- src/components/tab_switcher/tab_switcher.js | 9 +-------- src/components/user_profile/user_profile.js | 3 +++ src/components/user_profile/user_profile.vue | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'src/components/user_profile/user_profile.vue') diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index 03da8249..423df258 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -4,19 +4,12 @@ import './tab_switcher.scss' export default Vue.component('tab-switcher', { name: 'TabSwitcher', - props: ['refresh', 'renderOnlyFocused'], + props: ['renderOnlyFocused'], data () { return { active: this.$slots.default.findIndex(_ => _.tag) } }, - watch: { - $route () { - if (this.refresh) { - this.active = 0 - } - } - }, methods: { activateTab (index) { return () => { diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 7708141c..cdf1cee9 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -141,6 +141,9 @@ const UserProfile = { } this.cleanUp() this.startUp() + }, + $route () { + this.$refs.tabSwitcher.activateTab(0)() } }, components: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 54f1b97b..8090efa5 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -6,7 +6,7 @@ :switcher="true" :selected="timeline.viewing" /> - +