From 2e59ab738b6991a0e6b0a0b9a6dafba41e16c929 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 7 Mar 2019 19:49:41 +0200 Subject: [PATCH 01/83] updates normalizer for proper user handling and adds support for friends tl via mastoapi --- src/services/api/api.service.js | 4 ++-- .../entity_normalizer.service.js | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026e..fe2cb1c8b 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -1,6 +1,5 @@ /* eslint-env browser */ const LOGIN_URL = '/api/account/verify_credentials.json' -const FRIENDS_TIMELINE_URL = '/api/statuses/friends_timeline.json' const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const PUBLIC_TIMELINE_URL = '/api/statuses/public_timeline.json' const PUBLIC_AND_EXTERNAL_TIMELINE_URL = '/api/statuses/public_and_external_timeline.json' @@ -43,6 +42,7 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -342,7 +342,7 @@ const setUserMute = ({id, credentials, muted = true}) => { const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => { const timelineUrls = { public: PUBLIC_TIMELINE_URL, - friends: FRIENDS_TIMELINE_URL, + friends: MASTODON_USER_HOME_TIMELINE_URL, mentions: MENTIONS_URL, dms: DM_TIMELINE_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index d20ce77f7..70f6d693a 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -60,9 +60,18 @@ export const parseUser = (data) => { if (data.pleroma) { const pleroma = data.pleroma - output.follows_you = pleroma.follows_you - output.statusnet_blocking = pleroma.statusnet_blocking - output.muted = pleroma.muted + const relationship = pleroma.relationship + output.follows_you = relationship.followed_by + output.statusnet_blocking = relationship.blocking + output.muted = relationship.muting + output.following = relationship.following + + // Unused: + // domain_blocking + // endorsed + // muting_notifications + // requested + // showing_reblogs } // Missing, trying to recover From c038d0c12a8cc2af9fd3d3c6f9e338ee0d7c4765 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 7 Mar 2019 20:04:29 +0200 Subject: [PATCH 02/83] undo this change since BE returns empty object for relationship, add in a separate MR --- .../entity_normalizer.service.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 70f6d693a..d20ce77f7 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -60,18 +60,9 @@ export const parseUser = (data) => { if (data.pleroma) { const pleroma = data.pleroma - const relationship = pleroma.relationship - output.follows_you = relationship.followed_by - output.statusnet_blocking = relationship.blocking - output.muted = relationship.muting - output.following = relationship.following - - // Unused: - // domain_blocking - // endorsed - // muting_notifications - // requested - // showing_reblogs + output.follows_you = pleroma.follows_you + output.statusnet_blocking = pleroma.statusnet_blocking + output.muted = pleroma.muted } // Missing, trying to recover From 8522063b2c4a35f0926517a3b75376c81131dd79 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 7 Mar 2019 20:16:35 +0200 Subject: [PATCH 03/83] switch public and TWKN to MastoAPI --- src/services/api/api.service.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026e..f9b556c0e 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -2,8 +2,6 @@ const LOGIN_URL = '/api/account/verify_credentials.json' const FRIENDS_TIMELINE_URL = '/api/statuses/friends_timeline.json' const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' -const PUBLIC_TIMELINE_URL = '/api/statuses/public_timeline.json' -const PUBLIC_AND_EXTERNAL_TIMELINE_URL = '/api/statuses/public_and_external_timeline.json' const TAG_TIMELINE_URL = '/api/statusnet/tags/timeline' const FAVORITE_URL = '/api/favorites/create' const UNFAVORITE_URL = '/api/favorites/destroy' @@ -43,6 +41,7 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_PUBLIC_TIMELINE = '/api/v1/timelines/public' import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -341,12 +340,12 @@ const setUserMute = ({id, credentials, muted = true}) => { const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => { const timelineUrls = { - public: PUBLIC_TIMELINE_URL, + public: MASTODON_PUBLIC_TIMELINE, friends: FRIENDS_TIMELINE_URL, mentions: MENTIONS_URL, dms: DM_TIMELINE_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL, - 'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL, + 'publicAndExternal': MASTODON_PUBLIC_TIMELINE, user: QVITTER_USER_TIMELINE_URL, media: QVITTER_USER_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, @@ -372,6 +371,12 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use if (timeline === 'media') { params.push(['only_media', 1]) } + if (timeline === 'public') { + params.push(['local', true]) + } + if (timeline === 'public' || timeline === 'publicAndExternal') { + params.push(['only_media', false]) + } params.push(['count', 20]) From 6e2946f35290380cbd9e2147a570469f1be18ab6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 7 Mar 2019 20:21:07 +0200 Subject: [PATCH 04/83] switch direct messages to mastoapi --- src/services/api/api.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026e..65d0916f2 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -15,7 +15,6 @@ const STATUS_URL = '/api/statuses/show' const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload' const CONVERSATION_URL = '/api/statusnet/conversation' const MENTIONS_URL = '/api/statuses/mentions.json' -const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' const FOLLOWERS_URL = '/api/statuses/followers.json' const FRIENDS_URL = '/api/statuses/friends.json' const BLOCKS_URL = '/api/statuses/blocks.json' @@ -43,6 +42,7 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_DIRECT_MESSAGES_TIMELINE_URL = '/api/v1/timelines/direct' import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -344,7 +344,7 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use public: PUBLIC_TIMELINE_URL, friends: FRIENDS_TIMELINE_URL, mentions: MENTIONS_URL, - dms: DM_TIMELINE_URL, + dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL, 'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL, user: QVITTER_USER_TIMELINE_URL, From 49b0f0a04a74039a1b82fbde731828e599123e93 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sat, 9 Mar 2019 18:33:49 +0200 Subject: [PATCH 05/83] Fetching convos via MastoAPI. Had to change conversation component a bit for better support, since MastoAPI doesn't have coversation ids --- src/components/conversation/conversation.js | 23 +++++++------- src/services/api/api.service.js | 35 ++++++++++++++------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 48b8aaaa5..fd4303ca5 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -25,7 +25,8 @@ const sortAndFilterConversation = (conversation) => { const conversation = { data () { return { - highlight: null + highlight: null, + relevantIds: [] } }, props: [ @@ -48,9 +49,11 @@ const conversation = { return [] } - const conversationId = this.status.statusnet_conversation_id - const statuses = this.$store.state.statuses.allStatuses - const conversation = filter(statuses, { statusnet_conversation_id: conversationId }) + const statusesObject = this.$store.state.statuses.allStatusesObject + const conversation = this.relevantIds.reduce((acc, id) => { + acc.push(statusesObject[id]) + return acc + }, []) return sortAndFilterConversation(conversation) }, replies () { @@ -83,15 +86,13 @@ const conversation = { methods: { fetchConversation () { if (this.status) { - const conversationId = this.status.statusnet_conversation_id + const conversationId = this.status.id this.$store.state.api.backendInteractor.fetchConversation({id: conversationId}) - .then((statuses) => this.$store.dispatch('addNewStatuses', { statuses })) + .then((statuses) => { + this.$store.dispatch('addNewStatuses', { statuses }) + statuses.forEach(status => this.relevantIds.push(status.id)) + }) .then(() => this.setHighlight(this.statusId)) - } else { - const id = this.$route.params.id - this.$store.state.api.backendInteractor.fetchStatus({id}) - .then((status) => this.$store.dispatch('addNewStatuses', { statuses: [status] })) - .then(() => this.fetchConversation()) } }, getReplies (id) { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026e..a273f32e2 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -11,9 +11,7 @@ const RETWEET_URL = '/api/statuses/retweet' const UNRETWEET_URL = '/api/statuses/unretweet' const STATUS_UPDATE_URL = '/api/statuses/update.json' const STATUS_DELETE_URL = '/api/statuses/destroy' -const STATUS_URL = '/api/statuses/show' const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload' -const CONVERSATION_URL = '/api/statusnet/conversation' const MENTIONS_URL = '/api/statuses/mentions.json' const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' const FOLLOWERS_URL = '/api/statuses/followers.json' @@ -43,6 +41,8 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}` +const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -298,20 +298,31 @@ const fetchFollowRequests = ({credentials}) => { } const fetchConversation = ({id, credentials}) => { - let url = `${CONVERSATION_URL}/${id}.json?count=100` - return fetch(url, { headers: authHeaders(credentials) }) - .then((data) => { - if (data.ok) { - return data - } - throw new Error('Error fetching timeline', data) - }) - .then((data) => data.json()) + let url = MASTODON_STATUS_URL(id) + let urlContext = MASTODON_STATUS_CONTEXT_URL(id) + return Promise.all([ + fetch(url, { headers: authHeaders(credentials) }) + .then((data) => { + if (data.ok) { + return data + } + throw new Error('Error fetching timeline', data) + }) + .then((data) => data.json()), + fetch(urlContext, { headers: authHeaders(credentials) }) + .then((data) => { + if (data.ok) { + return data + } + throw new Error('Error fetching timeline', data) + }) + .then((data) => data.json())]) + .then(([status, context]) => [...context.ancestors, status, ...context.descendants]) .then((data) => data.map(parseStatus)) } const fetchStatus = ({id, credentials}) => { - let url = `${STATUS_URL}/${id}.json` + let url = MASTODON_STATUS_URL(id) return fetch(url, { headers: authHeaders(credentials) }) .then((data) => { if (data.ok) { From 0b1506a4c00c1482dd31ee43b17ee673e9f57bde Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 10 Mar 2019 18:05:51 +0200 Subject: [PATCH 06/83] wip support for follower/following, a bit broken and with regression --- src/services/api/api.service.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026e..9cee75b9a 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -16,8 +16,6 @@ const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload' const CONVERSATION_URL = '/api/statusnet/conversation' const MENTIONS_URL = '/api/statuses/mentions.json' const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' -const FOLLOWERS_URL = '/api/statuses/followers.json' -const FRIENDS_URL = '/api/statuses/friends.json' const BLOCKS_URL = '/api/statuses/blocks.json' const FOLLOWING_URL = '/api/friendships/create.json' const UNFOLLOWING_URL = '/api/friendships/destroy.json' @@ -43,6 +41,8 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_FOLLOWING_URL = id => `/api/v1/accounts/${id}/following` +const MASTODON_FOLLOWERS_URL = id => `/api/v1/accounts/${id}/followers` import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -258,9 +258,9 @@ const fetchUser = ({id, credentials}) => { } const fetchFriends = ({id, page, credentials}) => { - let url = `${FRIENDS_URL}?user_id=${id}` + let url = MASTODON_FOLLOWING_URL(id) if (page) { - url = url + `&page=${page}` + url = url + `?page=${page}` } return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) @@ -268,16 +268,16 @@ const fetchFriends = ({id, page, credentials}) => { } const exportFriends = ({id, credentials}) => { - let url = `${FRIENDS_URL}?user_id=${id}&all=true` + let url = MASTODON_FOLLOWING_URL(id) + `?all=true` return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) .then((data) => data.map(parseUser)) } const fetchFollowers = ({id, page, credentials}) => { - let url = `${FOLLOWERS_URL}?user_id=${id}` + let url = MASTODON_FOLLOWERS_URL(id) if (page) { - url = url + `&page=${page}` + url = url + `?page=${page}` } return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) From 4a5aef8883d3e92a58e272857cdfb303da61feb2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 10 Mar 2019 19:15:07 +0200 Subject: [PATCH 07/83] basic user and status actions implemented --- src/modules/statuses.js | 14 ------ src/services/api/api.service.js | 86 +++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 7571b62ab..a1ba87c41 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -429,13 +429,6 @@ const statuses = { // Optimistic favoriting... commit('setFavorited', { status, value: true }) apiService.favorite({ id: status.id, credentials: rootState.users.currentUser.credentials }) - .then(response => { - if (response.ok) { - return response.json() - } else { - return {} - } - }) .then(status => { commit('setFavoritedConfirm', { status }) }) @@ -444,13 +437,6 @@ const statuses = { // Optimistic favoriting... commit('setFavorited', { status, value: false }) apiService.unfavorite({ id: status.id, credentials: rootState.users.currentUser.credentials }) - .then(response => { - if (response.ok) { - return response.json() - } else { - return {} - } - }) .then(status => { commit('setFavoritedConfirm', { status }) }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026e..e2f44a2b8 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -5,12 +5,7 @@ const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const PUBLIC_TIMELINE_URL = '/api/statuses/public_timeline.json' const PUBLIC_AND_EXTERNAL_TIMELINE_URL = '/api/statuses/public_and_external_timeline.json' const TAG_TIMELINE_URL = '/api/statusnet/tags/timeline' -const FAVORITE_URL = '/api/favorites/create' -const UNFAVORITE_URL = '/api/favorites/destroy' -const RETWEET_URL = '/api/statuses/retweet' -const UNRETWEET_URL = '/api/statuses/unretweet' const STATUS_UPDATE_URL = '/api/statuses/update.json' -const STATUS_DELETE_URL = '/api/statuses/destroy' const STATUS_URL = '/api/statuses/show' const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload' const CONVERSATION_URL = '/api/statusnet/conversation' @@ -19,9 +14,6 @@ const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' const FOLLOWERS_URL = '/api/statuses/followers.json' const FRIENDS_URL = '/api/statuses/friends.json' const BLOCKS_URL = '/api/statuses/blocks.json' -const FOLLOWING_URL = '/api/friendships/create.json' -const UNFOLLOWING_URL = '/api/friendships/destroy.json' -const QVITTER_USER_PREF_URL = '/api/qvitter/set_profile_pref.json' const REGISTRATION_URL = '/api/account/register.json' const AVATAR_UPDATE_URL = '/api/qvitter/update_avatar.json' const BG_UPDATE_URL = '/api/qvitter/update_background_image.json' @@ -31,8 +23,6 @@ const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json' const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json' const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json' const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' -const BLOCKING_URL = '/api/blocks/create.json' -const UNBLOCKING_URL = '/api/blocks/destroy.json' const USER_URL = '/api/users/show.json' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' @@ -43,6 +33,17 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite` +const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite` +const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog` +const MASTODON_UNRETWEET_URL = id => `/api/v1/statuses/${id}/unreblog` +const MASTODON_DELETE_URL = id => `/api/v1/statuses/${id}` +const MASTODON_FOLLOW_URL = id => `/api/v1/accounts/${id}/follow` +const MASTODON_UNFOLLOW_URL = id => `/api/v1/accounts/${id}/unfollow` +const MASTODON_BLOCK_URL = id => `/api/v1/accounts/${id}/block` +const MASTODON_UNBLOCK_URL = id => `/api/v1/accounts/${id}/unblock` +const MASTODON_MUTE_URL = id => `/api/v1/accounts/${id}/mute` +const MASTODON_UNMUTE_URL = id => `/api/v1/accounts/${id}/unmute` import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -195,7 +196,7 @@ const externalProfile = ({profileUrl, credentials}) => { } const followUser = ({id, credentials}) => { - let url = `${FOLLOWING_URL}?user_id=${id}` + let url = MASTODON_FOLLOW_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -203,7 +204,7 @@ const followUser = ({id, credentials}) => { } const unfollowUser = ({id, credentials}) => { - let url = `${UNFOLLOWING_URL}?user_id=${id}` + let url = MASTODON_UNFOLLOW_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -211,7 +212,7 @@ const unfollowUser = ({id, credentials}) => { } const blockUser = ({id, credentials}) => { - let url = `${BLOCKING_URL}?user_id=${id}` + let url = MASTODON_BLOCK_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -219,7 +220,7 @@ const blockUser = ({id, credentials}) => { } const unblockUser = ({id, credentials}) => { - let url = `${UNBLOCKING_URL}?user_id=${id}` + let url = MASTODON_UNBLOCK_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -324,18 +325,11 @@ const fetchStatus = ({id, credentials}) => { } const setUserMute = ({id, credentials, muted = true}) => { - const form = new FormData() + const url = muted ? MASTODON_MUTE_URL(id) : MASTODON_UNMUTE_URL(id) - const muteInteger = muted ? 1 : 0 - - form.append('namespace', 'qvitter') - form.append('data', muteInteger) - form.append('topic', `mute:${id}`) - - return fetch(QVITTER_USER_PREF_URL, { + return fetch(url, { method: 'POST', - headers: authHeaders(credentials), - body: form + headers: authHeaders(credentials) }) } @@ -407,31 +401,63 @@ const verifyCredentials = (user) => { } const favorite = ({ id, credentials }) => { - return fetch(`${FAVORITE_URL}/${id}.json`, { + return fetch(MASTODON_FAVORITE_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const unfavorite = ({ id, credentials }) => { - return fetch(`${UNFAVORITE_URL}/${id}.json`, { + return fetch(MASTODON_UNFAVORITE_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const retweet = ({ id, credentials }) => { - return fetch(`${RETWEET_URL}/${id}.json`, { + return fetch(MASTODON_RETWEET_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const unretweet = ({ id, credentials }) => { - return fetch(`${UNRETWEET_URL}/${id}.json`, { + return fetch(MASTODON_UNRETWEET_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks}) => { @@ -468,9 +494,9 @@ const postStatus = ({credentials, status, spoilerText, visibility, sensitive, me } const deleteStatus = ({ id, credentials }) => { - return fetch(`${STATUS_DELETE_URL}/${id}.json`, { + return fetch(MASTODON_DELETE_URL(id), { headers: authHeaders(credentials), - method: 'POST' + method: 'DELETE' }) } From 4f455eefe5af2914d7dece40e027ed35ec8a21b3 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 11 Mar 2019 10:52:28 -0400 Subject: [PATCH 08/83] #433: do not remove the reply dialog --- src/components/conversation/conversation.js | 6 +++++- src/components/conversation/conversation.vue | 15 +++++++++----- src/components/status/status.js | 6 +++--- .../status_or_conversation.js | 6 +++++- .../status_or_conversation.vue | 20 +++++++++++++++++-- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 48b8aaaa5..95e484cda 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -30,7 +30,8 @@ const conversation = { }, props: [ 'statusoid', - 'collapsable' + 'collapsable', + 'replying' ], computed: { status () { @@ -102,6 +103,9 @@ const conversation = { }, setHighlight (id) { this.highlight = id + }, + toggleReplying () { + this.$emit('toggleReplying') } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 5528fef65..42d009c90 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -10,14 +10,19 @@
- + class="status-fadein" + />
diff --git a/src/components/status/status.js b/src/components/status/status.js index 9e18fe151..20ca86a6b 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -25,11 +25,11 @@ const Status = { 'replies', 'isPreview', 'noHeading', - 'inlineExpanded' + 'inlineExpanded', + 'replying' ], data () { return { - replying: false, expanded: false, unmuted: false, userExpanded: false, @@ -307,7 +307,7 @@ const Status = { } }, toggleReplying () { - this.replying = !this.replying + this.$emit('toggleReplying') }, gotoOriginal (id) { // only handled by conversation, not status_or_conversation diff --git a/src/components/status_or_conversation/status_or_conversation.js b/src/components/status_or_conversation/status_or_conversation.js index 441552ca6..749f76651 100644 --- a/src/components/status_or_conversation/status_or_conversation.js +++ b/src/components/status_or_conversation/status_or_conversation.js @@ -5,7 +5,8 @@ const statusOrConversation = { props: ['statusoid'], data () { return { - expanded: false + expanded: false, + replying: false } }, components: { @@ -15,6 +16,9 @@ const statusOrConversation = { methods: { toggleExpanded () { this.expanded = !this.expanded + }, + toggleReplying () { + this.replying = !this.replying } } } diff --git a/src/components/status_or_conversation/status_or_conversation.vue b/src/components/status_or_conversation/status_or_conversation.vue index 9647d5eb4..43a60c3a0 100644 --- a/src/components/status_or_conversation/status_or_conversation.vue +++ b/src/components/status_or_conversation/status_or_conversation.vue @@ -1,7 +1,23 @@ From 63d7c7bd80cf8028cdefee99c1cb75614385f96b Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 11 Mar 2019 16:24:37 -0400 Subject: [PATCH 09/83] #433: persistency of status form --- src/components/conversation/conversation.js | 36 +++++++++++++------ src/components/conversation/conversation.vue | 31 +++++++++++----- src/components/status/status.js | 7 ++-- .../status_or_conversation.js | 26 -------------- .../status_or_conversation.vue | 30 ---------------- src/components/timeline/timeline.js | 4 +-- src/components/timeline/timeline.vue | 8 ++++- 7 files changed, 60 insertions(+), 82 deletions(-) delete mode 100644 src/components/status_or_conversation/status_or_conversation.js delete mode 100644 src/components/status_or_conversation/status_or_conversation.vue diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 95e484cda..4cae0bdb7 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,4 +1,4 @@ -import { reduce, filter } from 'lodash' +import { reduce, filter, findIndex } from 'lodash' import Status from '../status/status.vue' const sortById = (a, b) => { @@ -25,13 +25,13 @@ const sortAndFilterConversation = (conversation) => { const conversation = { data () { return { - highlight: null + highlight: null, + expanded: false } }, props: [ 'statusoid', - 'collapsable', - 'replying' + 'collapsable' ], computed: { status () { @@ -49,9 +49,18 @@ const conversation = { return [] } + if (!this.expanded) { + return [this.status] + } + const conversationId = this.status.statusnet_conversation_id const statuses = this.$store.state.statuses.allStatuses const conversation = filter(statuses, { statusnet_conversation_id: conversationId }) + + const statusIndex = findIndex(conversation, { id: this.statusId }) + if (statusIndex !== -1) { + conversation[statusIndex] = this.status + } return sortAndFilterConversation(conversation) }, replies () { @@ -75,11 +84,13 @@ const conversation = { components: { Status }, - created () { - this.fetchConversation() - }, watch: { - '$route': 'fetchConversation' + '$route': 'fetchConversation', + expanded (value) { + if (value) { + this.fetchConversation() + } + } }, methods: { fetchConversation () { @@ -99,13 +110,16 @@ const conversation = { return this.replies[id] || [] }, focused (id) { - return id === this.statusId + return this.expanded && id === this.statusId }, setHighlight (id) { this.highlight = id }, - toggleReplying () { - this.$emit('toggleReplying') + getHighlight () { + return this.expanded ? this.highlight : null + }, + toggleExpanded () { + this.expanded = !this.expanded } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 42d009c90..b208d540d 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -1,9 +1,9 @@ + + diff --git a/src/components/status/status.js b/src/components/status/status.js index 20ca86a6b..8e489704f 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -25,11 +25,11 @@ const Status = { 'replies', 'isPreview', 'noHeading', - 'inlineExpanded', - 'replying' + 'inlineExpanded' ], data () { return { + replying: false, expanded: false, unmuted: false, userExpanded: false, @@ -307,10 +307,9 @@ const Status = { } }, toggleReplying () { - this.$emit('toggleReplying') + this.replying = !this.replying }, gotoOriginal (id) { - // only handled by conversation, not status_or_conversation if (this.inConversation) { this.$emit('goto', id) } diff --git a/src/components/status_or_conversation/status_or_conversation.js b/src/components/status_or_conversation/status_or_conversation.js deleted file mode 100644 index 749f76651..000000000 --- a/src/components/status_or_conversation/status_or_conversation.js +++ /dev/null @@ -1,26 +0,0 @@ -import Status from '../status/status.vue' -import Conversation from '../conversation/conversation.vue' - -const statusOrConversation = { - props: ['statusoid'], - data () { - return { - expanded: false, - replying: false - } - }, - components: { - Status, - Conversation - }, - methods: { - toggleExpanded () { - this.expanded = !this.expanded - }, - toggleReplying () { - this.replying = !this.replying - } - } -} - -export default statusOrConversation diff --git a/src/components/status_or_conversation/status_or_conversation.vue b/src/components/status_or_conversation/status_or_conversation.vue deleted file mode 100644 index 43a60c3a0..000000000 --- a/src/components/status_or_conversation/status_or_conversation.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - - - diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index c45f89470..1da7d5cc5 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -1,6 +1,6 @@ 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 Conversation from '../conversation/conversation.vue' import { throttle } from 'lodash' const Timeline = { @@ -43,7 +43,7 @@ const Timeline = { }, components: { Status, - StatusOrConversation + Conversation }, created () { const store = this.$store diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 8f28d65c7..e0a34bd1b 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -16,7 +16,13 @@
- +
From 6e60873a3d9c709689eb02c05460b25a73ea0718 Mon Sep 17 00:00:00 2001 From: taehoon Date: Tue, 12 Mar 2019 14:18:20 -0400 Subject: [PATCH 10/83] Switch to mastoapi for user search --- src/services/new_api/user_search.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/new_api/user_search.js b/src/services/new_api/user_search.js index ce7da88e4..869afa9c9 100644 --- a/src/services/new_api/user_search.js +++ b/src/services/new_api/user_search.js @@ -1,13 +1,16 @@ import utils from './utils.js' +import { parseUser } from '../entity_normalizer/entity_normalizer.service.js' const search = ({query, store}) => { return utils.request({ store, - url: '/api/pleroma/search_user', + url: '/api/v1/accounts/search', params: { - query + q: query } - }).then((data) => data.json()) + }) + .then((data) => data.json()) + .then((data) => data.map(parseUser)) } const UserSearch = { search From 543cc0d285246c3353a05ca0fdda9d511af46276 Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 17 Mar 2019 16:59:08 -0400 Subject: [PATCH 11/83] #442 - clean up Bio placeholder text --- src/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index 01fe2fba2..0bf8b4e95 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -97,7 +97,7 @@ "new_captcha": "Click the image to get a new captcha", "username_placeholder": "e.g. lain", "fullname_placeholder": "e.g. Lain Iwakura", - "bio_placeholder": "e.g.\nHi, I'm Lain\nI’m an anime girl living in suburban Japan. You may know me from the Wired.", + "bio_placeholder": "e.g. \nHi, I'm Lain. \nI’m an anime girl living in suburban Japan. You may know me from the Wired.", "validations": { "username_required": "cannot be left blank", "fullname_required": "cannot be left blank", From 5717d971829c54e4387b05725658e652b69a8b17 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 18 Mar 2019 10:35:13 -0400 Subject: [PATCH 12/83] #442 - update placeholder linebreak --- src/components/registration/registration.js | 3 +++ src/components/registration/registration.vue | 2 +- src/i18n/en.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index ab6cd64d9..8dc004208 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -35,6 +35,9 @@ const registration = { }, computed: { token () { return this.$route.params.token }, + bioPlaceholder () { + return this.$t('registration.bio_placeholder').replace(/\s*\n\s*/g, ' \n') + }, ...mapState({ registrationOpen: (state) => state.instance.registrationOpen, signedIn: (state) => !!state.users.currentUser, diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index e22b308df..110b27bfc 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -45,7 +45,7 @@
- +
diff --git a/src/i18n/en.json b/src/i18n/en.json index 0bf8b4e95..e5139a4e0 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -97,7 +97,7 @@ "new_captcha": "Click the image to get a new captcha", "username_placeholder": "e.g. lain", "fullname_placeholder": "e.g. Lain Iwakura", - "bio_placeholder": "e.g. \nHi, I'm Lain. \nI’m an anime girl living in suburban Japan. You may know me from the Wired.", + "bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.", "validations": { "username_required": "cannot be left blank", "fullname_required": "cannot be left blank", From 1c6e5d36886ac383ae91059e6bc93274d896c54e Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 21 Mar 2019 13:06:37 -0400 Subject: [PATCH 13/83] #451 - long username on follow/follower tabs --- src/components/basic_user_card/basic_user_card.vue | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 9b80c72b8..37c345389 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -52,6 +52,14 @@ width: 16px; vertical-align: middle; } + + span { + display: inline-block; + max-width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } } &-expanded-content { From dfcdf4f32cf1779ecd85271dabc2919cdc51783a Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 21 Mar 2019 13:19:10 -0400 Subject: [PATCH 14/83] #452 - unfollow button issue --- src/components/follow_card/follow_card.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/follow_card/follow_card.vue b/src/components/follow_card/follow_card.vue index 9bd21cfd3..72a1de622 100644 --- a/src/components/follow_card/follow_card.vue +++ b/src/components/follow_card/follow_card.vue @@ -24,7 +24,7 @@ {{ $t('user_card.follow') }} - - @@ -78,12 +78,11 @@

{{$t('settings.profile_banner')}}

{{$t('settings.current_profile_banner')}}

- +

{{$t('settings.set_new_profile_banner')}}

- - +
- +
@@ -95,10 +94,9 @@

{{$t('settings.profile_background')}}

{{$t('settings.set_new_profile_background')}}

- - +
- +
@@ -174,7 +172,7 @@

{{$t('settings.follow_import')}}

{{$t('settings.import_followers_from_a_csv_file')}}

- +
From 8fe9101f0b134978212bf05ed6e73894f47c617e Mon Sep 17 00:00:00 2001 From: jared Date: Tue, 26 Mar 2019 14:53:27 -0400 Subject: [PATCH 65/83] #255 - clean up autocomplete form --- src/App.scss | 51 +++++++++++++++ src/components/emoji-input/emoji-input.vue | 51 --------------- .../post_status_form/post_status_form.vue | 65 ++++--------------- 3 files changed, 63 insertions(+), 104 deletions(-) diff --git a/src/App.scss b/src/App.scss index 244b34747..ae068e4fa 100644 --- a/src/App.scss +++ b/src/App.scss @@ -767,3 +767,54 @@ nav { .btn.btn-default { min-height: 28px; } + +.autocomplete { + &-panel { + position: relative; + + &-body { + margin: 0 0.5em 0 0.5em; + border-radius: $fallback--tooltipRadius; + border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + position: absolute; + z-index: 1; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); + // this doesn't match original but i don't care, making it uniform. + box-shadow: var(--popupShadow); + min-width: 75%; + background: $fallback--bg; + background: var(--bg, $fallback--bg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + } + } + + &-item { + cursor: pointer; + padding: 0.2em 0.4em 0.2em 0.4em; + border-bottom: 1px solid rgba(0, 0, 0, 0.4); + display: flex; + + img { + width: 24px; + height: 24px; + object-fit: contain; + } + + span { + line-height: 24px; + margin: 0 0.1em 0 0.2em; + } + + small { + margin-left: .5em; + color: $fallback--faint; + color: var(--faint, $fallback--faint); + } + + &.highlighted { + background-color: $fallback--fg; + background-color: var(--lightBg, $fallback--fg); + } + } +} \ No newline at end of file diff --git a/src/components/emoji-input/emoji-input.vue b/src/components/emoji-input/emoji-input.vue index 568bd080e..338b77cd0 100644 --- a/src/components/emoji-input/emoji-input.vue +++ b/src/components/emoji-input/emoji-input.vue @@ -61,55 +61,4 @@ width: 100%; } } - -.autocomplete { - &-panel { - position: relative; - - &-body { - margin: 0 0.5em 0 0.5em; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - position: absolute; - z-index: 1; - box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); - // this doesn't match original but i don't care, making it uniform. - box-shadow: var(--popupShadow); - min-width: 75%; - background: $fallback--bg; - background: var(--bg, $fallback--bg); - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } - } - - &-item { - cursor: pointer; - padding: 0.2em 0.4em 0.2em 0.4em; - border-bottom: 1px solid rgba(0, 0, 0, 0.4); - display: flex; - - img { - width: 24px; - height: 24px; - object-fit: contain; - } - - span { - line-height: 24px; - margin: 0 0.1em 0 0.2em; - } - - small { - margin-left: .5em; - color: $fallback--faint; - color: var(--faint, $fallback--faint); - } - - &.highlighted { - background-color: $fallback--fg; - background-color: var(--lightBg, $fallback--fg); - } - } -} diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index b2a1dc583..9f9f16baa 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -56,14 +56,18 @@
-
-
-
-
- - {{candidate.utf}} - {{candidate.screen_name}}{{candidate.name}} -
+
+
+
+ + {{candidate.utf}} + {{candidate.screen_name}}{{candidate.name}}
@@ -262,50 +266,5 @@ cursor: pointer; z-index: 4; } - - .autocomplete-panel { - margin: 0 0.5em 0 0.5em; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - position: absolute; - z-index: 1; - box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); - // this doesn't match original but i don't care, making it uniform. - box-shadow: var(--popupShadow); - min-width: 75%; - background: $fallback--bg; - background: var(--bg, $fallback--bg); - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } - - .autocomplete { - cursor: pointer; - padding: 0.2em 0.4em 0.2em 0.4em; - border-bottom: 1px solid rgba(0, 0, 0, 0.4); - display: flex; - - img { - width: 24px; - height: 24px; - object-fit: contain; - } - - span { - line-height: 24px; - margin: 0 0.1em 0 0.2em; - } - - small { - margin-left: .5em; - color: $fallback--faint; - color: var(--faint, $fallback--faint); - } - - &.highlighted { - background-color: $fallback--fg; - background-color: var(--lightBg, $fallback--fg); - } - } } From c50e64f8eecd780246e3ac47c2a54164cfc28b8f Mon Sep 17 00:00:00 2001 From: shpuld Date: Tue, 26 Mar 2019 22:11:45 +0200 Subject: [PATCH 66/83] Add tests for gesture service, fix bug with perpendicular directions --- .../gesture_service/gesture_service.js | 15 ++- .../gesture_service/gesture_service.spec.js | 120 ++++++++++++++++++ 2 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 test/unit/specs/services/gesture_service/gesture_service.spec.js diff --git a/src/services/gesture_service/gesture_service.js b/src/services/gesture_service/gesture_service.js index 2e39003c8..efc0ca78f 100644 --- a/src/services/gesture_service/gesture_service.js +++ b/src/services/gesture_service/gesture_service.js @@ -10,11 +10,14 @@ const touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY]) const vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1]) -const perpendicular = v => [v[1], v[0]] +const perpendicular = v => [v[1], -v[0]] const dotProduct = (v1, v2) => v1[0] * v2[0] + v1[1] * v2[1] -const vectorFlatten = (v1, v2) => [v1[0] * v2[0], v1[1] * v2[1]] +const project = (v1, v2) => { + const scalar = (dotProduct(v1, v2) / dotProduct(v2, v2)) + return [scalar * v2[0], scalar * v2[1]] +} // direction: either use the constants above or an arbitrary 2d vector. // threshold: how many Px to move from touch origin before checking if the @@ -46,12 +49,12 @@ const updateSwipe = (event, gesture) => { // movement is opposite from direction if (dotProduct(delta, gesture.direction) < 0) return // movement perpendicular to direction is too much - const towardsDir = vectorFlatten(gesture.direction, delta) + const towardsDir = project(delta, gesture.direction) const perpendicularDir = perpendicular(gesture.direction) - const towardsPerpendicular = vectorFlatten(perpendicularDir, delta) + const towardsPerpendicular = project(delta, perpendicularDir) if ( - vectorLength(towardsDir) < - gesture.perpendicularTolerance * vectorLength(towardsPerpendicular) + vectorLength(towardsDir) * gesture.perpendicularTolerance < + vectorLength(towardsPerpendicular) ) return gesture.onSwipe() diff --git a/test/unit/specs/services/gesture_service/gesture_service.spec.js b/test/unit/specs/services/gesture_service/gesture_service.spec.js new file mode 100644 index 000000000..4a1b009a2 --- /dev/null +++ b/test/unit/specs/services/gesture_service/gesture_service.spec.js @@ -0,0 +1,120 @@ +import GestureService from 'src/services/gesture_service/gesture_service.js' + +const mockTouchEvent = (x, y) => ({ + touches: [ + { + screenX: x, + screenY: y + } + ] +}) + +describe.only('GestureService', () => { + describe('swipeGesture', () => { + it('calls the callback on a successful swipe', () => { + let swiped = false + const callback = () => { swiped = true } + const gesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + callback + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(200, 100), gesture) + + expect(swiped).to.eql(true) + }) + + it('calls the callback only once per begin', () => { + let hits = 0 + const callback = () => { hits += 1 } + const gesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + callback + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(150, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(200, 100), gesture) + + expect(hits).to.eql(1) + }) + + it('doesn\'t call the callback on an opposite swipe', () => { + let swiped = false + const callback = () => { swiped = true } + const gesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + callback + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(0, 100), gesture) + + expect(swiped).to.eql(false) + }) + + it('doesn\'t call the callback on a swipe below threshold', () => { + let swiped = false + const callback = () => { swiped = true } + const gesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + callback, + 100 + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(150, 100), gesture) + + expect(swiped).to.eql(false) + }) + + it('doesn\'t call the callback on a perpendicular swipe', () => { + let swiped = false + const callback = () => { swiped = true } + const gesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + callback, + 30, + 0.5 + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(150, 200), gesture) + + expect(swiped).to.eql(false) + }) + + it('calls the callback on perpendicular swipe if within tolerance', () => { + let swiped = false + const callback = () => { swiped = true } + const gesture = GestureService.swipeGesture( + GestureService.DIRECTION_RIGHT, + callback, + 30, + 2.0 + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(150, 150), gesture) + + expect(swiped).to.eql(true) + }) + + it('works with any arbitrary 2d directions', () => { + let swiped = false + const callback = () => { swiped = true } + const gesture = GestureService.swipeGesture( + [-1, -1], + callback, + 30, + 0.1 + ) + + GestureService.beginSwipe(mockTouchEvent(100, 100), gesture) + GestureService.updateSwipe(mockTouchEvent(60, 60), gesture) + + expect(swiped).to.eql(true) + }) + }) +}) From ab99d5ef952998cfe277dfbb32dd9fe238c91f48 Mon Sep 17 00:00:00 2001 From: jasper Date: Tue, 26 Mar 2019 13:35:08 -0700 Subject: [PATCH 67/83] Fix warnings in user profile routing --- src/components/basic_user_card/basic_user_card.js | 1 + .../user_profile_link_generator.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js index 87085a282..a80e6cf0c 100644 --- a/src/components/basic_user_card/basic_user_card.js +++ b/src/components/basic_user_card/basic_user_card.js @@ -20,6 +20,7 @@ const BasicUserCard = { this.userExpanded = !this.userExpanded }, userProfileLink (user) { + console.log('22222222222222222222222222222') return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) } } 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 index a214ca486..5b430328f 100644 --- a/src/services/user_profile_link_generator/user_profile_link_generator.js +++ b/src/services/user_profile_link_generator/user_profile_link_generator.js @@ -3,8 +3,8 @@ import { includes } from 'lodash' const generateProfileLink = (id, screenName, restrictedNicknames) => { const complicated = (isExternal(screenName) || includes(restrictedNicknames, screenName)) return { - name: (complicated ? 'external-user-profile' : 'user-profile'), - params: (complicated ? { id } : { name: screenName }) + name: (!screenName || complicated ? 'external-user-profile' : 'user-profile'), + params: (!screenName || complicated ? { id } : { name: screenName }) } } From 26955af60be26cf7ecba5d9b6ae03c11e111c1a4 Mon Sep 17 00:00:00 2001 From: jasper Date: Tue, 26 Mar 2019 13:36:33 -0700 Subject: [PATCH 68/83] Remove console log --- src/components/basic_user_card/basic_user_card.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js index a80e6cf0c..87085a282 100644 --- a/src/components/basic_user_card/basic_user_card.js +++ b/src/components/basic_user_card/basic_user_card.js @@ -20,7 +20,6 @@ const BasicUserCard = { this.userExpanded = !this.userExpanded }, userProfileLink (user) { - console.log('22222222222222222222222222222') return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) } } From 2d05aef24b36d1c8be5cdad13ae69a2f0514c713 Mon Sep 17 00:00:00 2001 From: jasper Date: Tue, 26 Mar 2019 14:26:26 -0700 Subject: [PATCH 69/83] Add await to login action' --- src/boot/after_store.js | 2 +- src/lib/persisted_state.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index f5e84cbc6..f5add8ade 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -241,7 +241,7 @@ const afterStoreSetup = async ({ store, i18n }) => { // Now we have the server settings and can try logging in if (store.state.oauth.token) { - store.dispatch('loginUser', store.state.oauth.token) + await store.dispatch('loginUser', store.state.oauth.token) } const router = new VueRouter({ diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index 720ff706a..7ab89c123 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -60,9 +60,6 @@ export default function createPersistedState ({ merge({}, store.state, savedState) ) } - if (store.state.oauth.token) { - store.dispatch('loginUser', store.state.oauth.token) - } loaded = true } catch (e) { console.log("Couldn't load state") From 95459ff0bca7f095f0fdc01b38dd89626fe5c189 Mon Sep 17 00:00:00 2001 From: HJ <30-hj@users.noreply.git.pleroma.social> Date: Wed, 27 Mar 2019 08:47:54 +0000 Subject: [PATCH 70/83] Revert "Merge branch 'mastoapi/friends-tl' into 'develop'" This reverts merge request !647 --- src/services/api/api.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 4ebfb7b77..3a4f21a69 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -1,5 +1,6 @@ /* eslint-env browser */ const LOGIN_URL = '/api/account/verify_credentials.json' +const FRIENDS_TIMELINE_URL = '/api/statuses/friends_timeline.json' const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const PUBLIC_TIMELINE_URL = '/api/statuses/public_timeline.json' const PUBLIC_AND_EXTERNAL_TIMELINE_URL = '/api/statuses/public_and_external_timeline.json' @@ -32,7 +33,6 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' -const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}` const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` const MASTODON_USER_URL = '/api/v1/accounts' @@ -348,7 +348,7 @@ const fetchStatus = ({id, credentials}) => { const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => { const timelineUrls = { public: PUBLIC_TIMELINE_URL, - friends: MASTODON_USER_HOME_TIMELINE_URL, + friends: FRIENDS_TIMELINE_URL, mentions: MENTIONS_URL, dms: DM_TIMELINE_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL, From b3a6bec17b18f554caa12d35e9e875adddc206db Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 27 Mar 2019 11:15:57 +0200 Subject: [PATCH 71/83] Revert "Merge branch 'revert-987b5162' into 'develop'" This reverts commit 96753e6a5febff60e0c2cb6fac13d5e7865f0a94, reversing changes made to 987b5162a7530979e9fa887a24311eb1cd480fc5. --- src/services/api/api.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 8d7f9e5d3..aabbe80f7 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -1,6 +1,5 @@ /* eslint-env browser */ const LOGIN_URL = '/api/account/verify_credentials.json' -const FRIENDS_TIMELINE_URL = '/api/statuses/friends_timeline.json' const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const TAG_TIMELINE_URL = '/api/statusnet/tags/timeline' const FAVORITE_URL = '/api/favorites/create' @@ -32,6 +31,7 @@ const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_PUBLIC_TIMELINE = '/api/v1/timelines/public' +const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}` const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` const MASTODON_USER_URL = '/api/v1/accounts' @@ -347,7 +347,7 @@ const fetchStatus = ({id, credentials}) => { const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => { const timelineUrls = { public: MASTODON_PUBLIC_TIMELINE, - friends: FRIENDS_TIMELINE_URL, + friends: MASTODON_USER_HOME_TIMELINE_URL, mentions: MENTIONS_URL, dms: DM_TIMELINE_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL, From 43e97e590c98d1f1bb500f96d2b604b968fbbbb3 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 27 Mar 2019 16:00:54 -0400 Subject: [PATCH 72/83] #433 - sort conversation for retweets and clean up --- src/components/conversation/conversation.js | 18 +++++------------- src/components/status/status.vue | 4 ++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 8ad1f44dd..5357b67f7 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -3,19 +3,9 @@ import { set } from 'vue' import Status from '../status/status.vue' const sortById = (a, b) => { - const seqA = Number(a.id) - const seqB = Number(b.id) - const isSeqA = !Number.isNaN(seqA) - const isSeqB = !Number.isNaN(seqB) - if (isSeqA && isSeqB) { - return seqA < seqB ? -1 : 1 - } else if (isSeqA && !isSeqB) { - return -1 - } else if (!isSeqA && isSeqB) { - return 1 - } else { - return a.id < b.id ? -1 : 1 - } + const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id + const idB = b.type === 'retweet' ? b.retweeted_status.id : b.id + return idA < idB ? -1 : 1 } const sortAndFilterConversation = (conversation, statusoid) => { @@ -24,6 +14,8 @@ const sortAndFilterConversation = (conversation, statusoid) => { conversation, (status) => (status.type === 'retweet' || status.id !== statusoid.retweeted_status.id) ) + } else { + conversation = filter(conversation, (status) => status.type !== 'retweet') } return conversation.filter(_ => _).sort(sortById) } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 1f6d0325e..da329deb0 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -12,7 +12,7 @@