diff --git a/src/components/conversation-page/conversation-page.js b/src/components/conversation-page/conversation-page.js index beffa5bb..8f1ac3d9 100644 --- a/src/components/conversation-page/conversation-page.js +++ b/src/components/conversation-page/conversation-page.js @@ -1,5 +1,5 @@ import Conversation from '../conversation/conversation.vue' -import { find, toInteger } from 'lodash' +import { find } from 'lodash' const conversationPage = { components: { @@ -7,7 +7,7 @@ const conversationPage = { }, computed: { statusoid () { - const id = toInteger(this.$route.params.id) + const id = this.$route.params.id const statuses = this.$store.state.statuses.allStatuses const status = find(statuses, {id}) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 9d9f7bbe..9bf5e136 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,9 +1,8 @@ import { reduce, filter, sortBy } from 'lodash' -import { statusType } from '../../modules/statuses.js' import Status from '../status/status.vue' const sortAndFilterConversation = (conversation) => { - conversation = filter(conversation, (status) => statusType(status) !== 'retweet') + conversation = filter(conversation, (status) => status.type !== 'retweet') return sortBy(conversation, 'id') } @@ -18,10 +17,12 @@ const conversation = { 'collapsable' ], computed: { - status () { return this.statusoid }, + status () { + return this.statusoid + }, conversation () { if (!this.status) { - return false + return [] } const conversationId = this.status.statusnet_conversation_id @@ -32,7 +33,9 @@ const conversation = { replies () { let i = 1 return reduce(this.conversation, (result, {id, in_reply_to_status_id}) => { - const irid = Number(in_reply_to_status_id) + /* eslint-disable camelcase */ + const irid = in_reply_to_status_id + /* eslint-enable camelcase */ if (irid) { result[irid] = result[irid] || [] result[irid].push({ @@ -69,7 +72,6 @@ const conversation = { } }, getReplies (id) { - id = Number(id) return this.replies[id] || [] }, focused (id) { @@ -80,7 +82,7 @@ const conversation = { } }, setHighlight (id) { - this.highlight = Number(id) + this.highlight = id } } } diff --git a/src/components/status/status.js b/src/components/status/status.js index 7d6acbac..105a736b 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -129,7 +129,7 @@ const Status = { if (this.status.user.id === this.$store.state.users.currentUser.id) { return false } - if (this.status.activity_type === 'repeat') { + if (this.status.type === 'retweet') { return false } var checkFollowing = this.$store.state.config.replyVisibility === 'following' @@ -258,7 +258,7 @@ const Status = { }, replyEnter (id, event) { this.showPreview = true - const targetId = Number(id) + const targetId = id const statuses = this.$store.state.statuses.allStatuses if (!this.preview) { @@ -283,7 +283,6 @@ const Status = { }, watch: { 'highlight': function (id) { - id = Number(id) if (this.status.id === id) { let rect = this.$el.getBoundingClientRect() if (rect.top < 100) { diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index 2f362c4d..9038733c 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -6,18 +6,26 @@ export default Vue.component('tab-switcher', { name: 'TabSwitcher', data () { return { - active: 0 + active: this.$slots.default.findIndex(_ => _.tag) } }, methods: { - activateTab(index) { - return () => this.active = index; + activateTab (index) { + return () => { + this.active = index + } } }, - render(h) { + beforeUpdate () { + const currentSlot = this.$slots.default[this.active] + if (!currentSlot.tag) { + this.active = this.$slots.default.findIndex(_ => _.tag) + } + }, + render (h) { const tabs = this.$slots.default - .filter(slot => slot.data) .map((slot, index) => { + if (!slot.tag) return const classesTab = ['tab'] const classesWrapper = ['tab-wrapper'] @@ -25,20 +33,24 @@ export default Vue.component('tab-switcher', { classesTab.push('active') classesWrapper.push('active') } + return (
) - }); - const contents = this.$slots.default.filter(_=>_.data).map(( slot, index ) => { + }) + + const contents = this.$slots.default.map((slot, index) => { + if (!slot.tag) return const active = index === this.active return (
{slot}
) - }); + }) + return (
diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index bde20707..c9197a1c 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -5,24 +5,33 @@ import Timeline from '../timeline/timeline.vue' const UserProfile = { created () { this.$store.commit('clearTimeline', { timeline: 'user' }) + this.$store.commit('clearTimeline', { timeline: 'favorites' }) this.$store.dispatch('startFetching', ['user', this.fetchBy]) + this.$store.dispatch('startFetching', ['favorites', this.fetchBy]) if (!this.user.id) { this.$store.dispatch('fetchUser', this.fetchBy) } }, destroyed () { this.$store.dispatch('stopFetching', 'user') + this.$store.dispatch('stopFetching', 'favorites') }, computed: { timeline () { return this.$store.state.statuses.timelines.user }, + favorites () { + return this.$store.state.statuses.timelines.favorites + }, userId () { return this.$route.params.id || this.user.id }, userName () { return this.$route.params.name || this.user.screen_name }, + isUs () { + return this.userId === this.$store.state.users.currentUser.id + }, friends () { return this.user.friends }, @@ -62,21 +71,28 @@ const UserProfile = { } }, watch: { + // TODO get rid of this copypasta userName () { if (this.isExternal) { return } this.$store.dispatch('stopFetching', 'user') + this.$store.dispatch('stopFetching', 'favorites') this.$store.commit('clearTimeline', { timeline: 'user' }) + this.$store.commit('clearTimeline', { timeline: 'favorites' }) this.$store.dispatch('startFetching', ['user', this.fetchBy]) + this.$store.dispatch('startFetching', ['favorites', this.fetchBy]) }, userId () { if (!this.isExternal) { return } this.$store.dispatch('stopFetching', 'user') + this.$store.dispatch('stopFetching', 'favorites') this.$store.commit('clearTimeline', { timeline: 'user' }) + this.$store.commit('clearTimeline', { timeline: 'favorites' }) this.$store.dispatch('startFetching', ['user', this.fetchBy]) + this.$store.dispatch('startFetching', ['favorites', this.fetchBy]) }, user () { if (this.user.id && !this.user.followers) { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 50619026..d64ce277 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -20,6 +20,7 @@
+
diff --git a/src/i18n/en.json b/src/i18n/en.json index 1dd3462b..3c29a17e 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -324,6 +324,7 @@ "block": "Block", "blocked": "Blocked!", "deny": "Deny", + "favorites": "Favorites", "follow": "Follow", "follow_sent": "Request sent!", "follow_progress": "Requesting…", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 249eab69..0887bb59 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -279,6 +279,7 @@ "user_card": { "block": "Заблокировать", "blocked": "Заблокирован", + "favorites": "Понравившиеся", "follow": "Читать", "follow_sent": "Запрос отправлен!", "follow_progress": "Запрашиваем…", diff --git a/src/modules/statuses.js b/src/modules/statuses.js index f92239a9..83cf7256 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,8 +1,8 @@ -import { includes, remove, slice, sortBy, toInteger, each, find, flatten, maxBy, minBy, merge, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' -const emptyTl = (userId = 0) => ({ +const emptyTl = () => ({ statuses: [], statusesObject: {}, faves: [], @@ -14,8 +14,8 @@ const emptyTl = (userId = 0) => ({ loading: false, followers: [], friends: [], - flushMarker: 0, - userId + userId: 0, + flushMarker: 0 }) export const defaultState = { @@ -36,6 +36,7 @@ export const defaultState = { mentions: emptyTl(), public: emptyTl(), user: emptyTl(), + favorites: emptyTl(), publicAndExternal: emptyTl(), friends: emptyTl(), tag: emptyTl(), @@ -43,20 +44,7 @@ export const defaultState = { } } -const isNsfw = (status) => { - const nsfwRegex = /#nsfw/i - return includes(status.tags, 'nsfw') || !!status.text.match(nsfwRegex) -} - export const prepareStatus = (status) => { - // Parse nsfw tags - if (status.nsfw === undefined) { - status.nsfw = isNsfw(status) - if (status.retweeted_status) { - status.nsfw = status.retweeted_status.nsfw - } - } - // Set deleted flag status.deleted = false @@ -75,35 +63,6 @@ const visibleNotificationTypes = (rootState) => { ].filter(_ => _) } -export const statusType = (status) => { - if (status.is_post_verb) { - return 'status' - } - - if (status.retweeted_status) { - return 'retweet' - } - - if ((typeof status.uri === 'string' && status.uri.match(/(fave|objectType=Favourite)/)) || - (typeof status.text === 'string' && status.text.match(/favorited/))) { - return 'favorite' - } - - if (status.text.match(/deleted notice {{tag/) || status.qvitter_delete_notice) { - return 'deletion' - } - - if (status.text.match(/started following/) || status.activity_type === 'follow') { - return 'follow' - } - - return 'unknown' -} - -export const findMaxId = (...args) => { - return (maxBy(flatten(args), 'id') || {}).id -} - const mergeOrAdd = (arr, obj, item) => { const oldItem = obj[item.id] @@ -122,9 +81,11 @@ const mergeOrAdd = (arr, obj, item) => { } } +const sortById = (a, b) => a.id > b.id ? -1 : 1 + const sortTimeline = (timeline) => { - timeline.visibleStatuses = sortBy(timeline.visibleStatuses, ({id}) => -id) - timeline.statuses = sortBy(timeline.statuses, ({id}) => -id) + timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById) + timeline.statuses = timeline.statuses.sort(sortById) timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id return timeline } @@ -153,13 +114,13 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us return } - const addStatus = (status, showImmediately, addToTimeline = true) => { - const result = mergeOrAdd(allStatuses, allStatusesObject, status) - status = result.item + const addStatus = (data, showImmediately, addToTimeline = true) => { + const result = mergeOrAdd(allStatuses, allStatusesObject, data) + const status = result.item if (result.new) { // We are mentioned in a post - if (statusType(status) === 'status' && find(status.attentions, { id: user.id })) { + if (status.type === 'status' && find(status.attentions, { id: user.id })) { const mentions = state.timelines.mentions // Add the mention to the mentions timeline @@ -200,7 +161,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us } const favoriteStatus = (favorite, counter) => { - const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) }) + const status = find(allStatuses, { id: favorite.in_reply_to_status_id }) if (status) { // This is our favorite, so the relevant bit. if (favorite.user.id === user.id) { @@ -263,6 +224,9 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us remove(timelineObject.visibleStatuses, { uri }) } }, + 'follow': (follow) => { + // NOOP, it is known status but we don't do anything about it for now + }, 'default': (unknown) => { console.log('unknown status type') console.log(unknown) @@ -270,7 +234,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us } each(statuses, (status) => { - const type = statusType(status) + const type = status.type const processor = processors[type] || processors['default'] processor(status) }) @@ -288,42 +252,36 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot const allStatuses = state.allStatuses const allStatusesObject = state.allStatusesObject each(notifications, (notification) => { - const result = mergeOrAdd(allStatuses, allStatusesObject, notification.notice) - const action = result.item + notification.action = mergeOrAdd(allStatuses, allStatusesObject, notification.action).item + notification.status = notification.status && mergeOrAdd(allStatuses, allStatusesObject, notification.status).item + // Only add a new notification if we don't have one for the same action - if (!find(state.notifications.data, (oldNotification) => oldNotification.action.id === action.id)) { - state.notifications.maxId = Math.max(notification.id, state.notifications.maxId) - state.notifications.minId = Math.min(notification.id, state.notifications.minId) + if (!state.notifications.idStore.hasOwnProperty(notification.id)) { + state.notifications.maxId = notification.id > state.notifications.maxId + ? notification.id + : state.notifications.maxId + state.notifications.minId = notification.id < state.notifications.minId + ? notification.id + : state.notifications.minId - const fresh = !notification.is_seen - const status = notification.ntype === 'like' - ? action.favorited_status - : action - - const result = { - type: notification.ntype, - status, - action, - seen: !fresh - } - - state.notifications.data.push(result) - state.notifications.idStore[notification.id] = result + state.notifications.data.push(notification) + state.notifications.idStore[notification.id] = notification if ('Notification' in window && window.Notification.permission === 'granted') { + const notifObj = {} + const action = notification.action const title = action.user.name - const result = {} - result.icon = action.user.profile_image_url - result.body = action.text // there's a problem that it doesn't put a space before links tho + notifObj.icon = action.user.profile_image_url + notifObj.body = action.text // there's a problem that it doesn't put a space before links tho // Shows first attached non-nsfw image, if any. Should add configuration for this somehow... if (action.attachments && action.attachments.length > 0 && !action.nsfw && action.attachments[0].mimetype.startsWith('image/')) { - result.image = action.attachments[0].url + notifObj.image = action.attachments[0].url } - if (fresh && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.ntype)) { - let notification = new window.Notification(title, result) + if (notification.fresh && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.ntype)) { + let notification = new window.Notification(title, notifObj) // Chrome is known for not closing notifications automatically // according to MDN, anyway. setTimeout(notification.close.bind(notification), 5000) @@ -346,7 +304,7 @@ export const mutations = { each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status }) }, clearTimeline (state, { timeline }) { - state.timelines[timeline] = emptyTl(state.timelines[timeline].userId) + state.timelines[timeline] = emptyTl() }, setFavorited (state, { status, value }) { const newStatus = state.allStatusesObject[status.id] diff --git a/src/modules/users.js b/src/modules/users.js index adbd37dd..d83f0dd8 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -68,6 +68,7 @@ export const mutations = { }, setUserForNotification (state, notification) { notification.action.user = state.usersObject[notification.action.user.id] + notification.from_profile = state.usersObject[notification.action.user.id] }, setColor (state, { user: { id }, highlighted }) { const user = state.usersObject[id] @@ -149,8 +150,8 @@ const users = { }) }, addNewNotifications (store, { notifications }) { - const users = compact(map(notifications, 'from_profile')) - const notificationIds = compact(notifications.map(_ => String(_.id))) + const users = map(notifications, 'from_profile') + const notificationIds = notifications.map(_ => _.id) store.commit('addNewUsers', users) const notificationsObject = store.rootState.statuses.notifications.idStore @@ -206,39 +207,38 @@ const users = { const commit = store.commit commit('beginLogin') store.rootState.api.backendInteractor.verifyCredentials(accessToken) - .then((response) => { - if (response.ok) { - response.json() - .then((user) => { - // user.credentials = userCredentials - user.credentials = accessToken - commit('setCurrentUser', user) - commit('addNewUsers', [user]) + .then((data) => { + if (!data.error) { + const user = data + // user.credentials = userCredentials + user.credentials = accessToken + commit('setCurrentUser', user) + commit('addNewUsers', [user]) - getNotificationPermission() - .then(permission => commit('setNotificationPermission', permission)) + getNotificationPermission() + .then(permission => commit('setNotificationPermission', permission)) - // Set our new backend interactor - commit('setBackendInteractor', backendInteractorService(accessToken)) + // Set our new backend interactor + commit('setBackendInteractor', backendInteractorService(accessToken)) - if (user.token) { - store.dispatch('initializeSocket', user.token) - } + if (user.token) { + store.dispatch('initializeSocket', user.token) + } - // Start getting fresh tweets. - store.dispatch('startFetching', 'friends') + // Start getting fresh tweets. + store.dispatch('startFetching', 'friends') - // Get user mutes and follower info - store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => { - each(mutedUsers, (user) => { user.muted = true }) - store.commit('addNewUsers', mutedUsers) - }) + // Get user mutes and follower info + store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => { + each(mutedUsers, (user) => { user.muted = true }) + store.commit('addNewUsers', mutedUsers) + }) - // Fetch our friends - store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) - .then((friends) => commit('addNewUsers', friends)) - }) + // Fetch our friends + store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) + .then((friends) => commit('addNewUsers', friends)) } else { + const response = data.error // Authentication failed commit('endLogin') if (response.status === 401) { @@ -250,11 +250,11 @@ const users = { commit('endLogin') resolve() }) - .catch((error) => { - console.log(error) - commit('endLogin') - reject('Failed to connect to server, try again') - }) + .catch((error) => { + console.log(error) + commit('endLogin') + reject('Failed to connect to server, try again') + }) }) } } diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 4ee95bd1..2b1ef5df 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -41,7 +41,10 @@ const APPROVE_USER_URL = '/api/pleroma/friendships/approve' const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' +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' const oldfetch = window.fetch @@ -70,6 +73,7 @@ const updateAvatar = ({credentials, params}) => { form.append(key, value) } }) + return fetch(url, { headers: authHeaders(credentials), method: 'POST', @@ -87,6 +91,7 @@ const updateBg = ({credentials, params}) => { form.append(key, value) } }) + return fetch(url, { headers: authHeaders(credentials), method: 'POST', @@ -110,6 +115,7 @@ const updateBanner = ({credentials, params}) => { form.append(key, value) } }) + return fetch(url, { headers: authHeaders(credentials), method: 'POST', @@ -237,24 +243,28 @@ const fetchUser = ({id, credentials}) => { let url = `${USER_URL}?user_id=${id}` return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) + .then((data) => parseUser(data)) } const fetchFriends = ({id, credentials}) => { let url = `${FRIENDS_URL}?user_id=${id}` return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) + .then((data) => data.map(parseUser)) } const fetchFollowers = ({id, credentials}) => { let url = `${FOLLOWERS_URL}?user_id=${id}` return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) + .then((data) => data.map(parseUser)) } const fetchAllFollowing = ({username, credentials}) => { const url = `${ALL_FOLLOWING_URL}/${username}.json` return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) + .then((data) => data.map(parseUser)) } const fetchFollowRequests = ({credentials}) => { @@ -266,13 +276,27 @@ 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()) + .then((data) => data.map(parseStatus)) } const fetchStatus = ({id, credentials}) => { let url = `${STATUS_URL}/${id}.json` return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => { + if (data.ok) { + return data + } + throw new Error('Error fetching timeline', data) + }) .then((data) => data.json()) + .then((data) => parseStatus(data)) } const setUserMute = ({id, credentials, muted = true}) => { @@ -300,13 +324,14 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use notifications: QVITTER_USER_NOTIFICATIONS_URL, 'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL, user: QVITTER_USER_TIMELINE_URL, + favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, tag: TAG_TIMELINE_URL } + const isNotifications = timeline === 'notifications' + const params = [] let url = timelineUrls[timeline] - let params = [] - if (since) { params.push(['since_id', since]) } @@ -330,9 +355,10 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use if (data.ok) { return data } - throw new Error('Error fetching timeline') + throw new Error('Error fetching timeline', data) }) .then((data) => data.json()) + .then((data) => data.map(isNotifications ? parseNotification : parseStatus)) } const verifyCredentials = (user) => { @@ -340,6 +366,16 @@ const verifyCredentials = (user) => { method: 'POST', headers: authHeaders(user) }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) + .then((data) => data.error ? data : parseUser(data)) } const favorite = ({ id, credentials }) => { @@ -391,6 +427,16 @@ const postStatus = ({credentials, status, spoilerText, visibility, sensitive, me method: 'POST', headers: authHeaders(credentials) }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) + .then((data) => data.error ? data : parseStatus(data)) } const deleteStatus = ({ id, credentials }) => { diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js new file mode 100644 index 00000000..6fc6d152 --- /dev/null +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -0,0 +1,263 @@ +const qvitterStatusType = (status) => { + if (status.is_post_verb) { + return 'status' + } + + if (status.retweeted_status) { + return 'retweet' + } + + if ((typeof status.uri === 'string' && status.uri.match(/(fave|objectType=Favourite)/)) || + (typeof status.text === 'string' && status.text.match(/favorited/))) { + return 'favorite' + } + + if (status.text.match(/deleted notice {{tag/) || status.qvitter_delete_notice) { + return 'deletion' + } + + if (status.text.match(/started following/) || status.activity_type === 'follow') { + return 'follow' + } + + return 'unknown' +} + +export const parseUser = (data) => { + const output = {} + const masto = data.hasOwnProperty('acct') + // case for users in "mentions" property for statuses in MastoAPI + const mastoShort = masto && !data.hasOwnProperty('avatar') + + output.id = String(data.id) + + if (masto) { + output.screen_name = data.acct + + // There's nothing else to get + if (mastoShort) { + return output + } + + output.name = null // missing + output.name_html = data.display_name + + output.description = null // missing + output.description_html = data.note + + // Utilize avatar_static for gif avatars? + output.profile_image_url = data.avatar + output.profile_image_url_original = data.avatar + + // Same, utilize header_static? + output.cover_photo = data.header + + output.friends_count = data.following_count + + output.bot = data.bot + + output.statusnet_profile_url = data.url + + if (data.pleroma) { + const pleroma = data.pleroma + output.follows_you = pleroma.follows_you + output.statusnet_blocking = pleroma.statusnet_blocking + output.muted = pleroma.muted + } + + // Missing, trying to recover + output.is_local = !output.screen_name.includes('@') + } else { + output.screen_name = data.screen_name + + output.name = data.name + output.name_html = data.name_html + + output.description = data.description + output.description_html = data.description_html + + output.profile_image_url = data.profile_image_url + output.profile_image_url_original = data.profile_image_url_original + + output.cover_photo = data.cover_photo + + output.friends_count = data.friends_count + + output.bot = null // missing + + output.statusnet_profile_url = data.statusnet_profile_url + + output.statusnet_blocking = data.statusnet_blocking + + output.is_local = data.is_local + + output.follows_you = data.follows_you + + output.muted = data.muted + + // QVITTER ONLY FOR NOW + // Really only applies to logged in user, really.. I THINK + output.rights = data.rights + output.no_rich_text = data.no_rich_text + output.default_scope = data.default_scope + output.hide_network = data.hide_network + output.background_image = data.background_image + } + + output.created_at = new Date(data.created_at) + output.locked = data.locked + output.followers_count = data.followers_count + output.statuses_count = data.statuses_count + + return output +} + +const parseAttachment = (data) => { + const output = {} + const masto = !data.hasOwnProperty('oembed') + + if (masto) { + // Not exactly same... + output.mimetype = data.type + output.meta = data.meta // not present in BE yet + } else { + output.mimetype = data.mimetype + output.meta = null // missing + } + + output.url = data.url + output.description = data.description + + return output +} + +export const parseStatus = (data) => { + const output = {} + const masto = data.hasOwnProperty('account') + + if (masto) { + output.favorited = data.favourited + output.fave_num = data.favourites_count + + output.repeated = data.reblogged + output.repeat_num = data.reblogs_count + + output.type = data.reblog ? 'retweet' : 'status' + output.nsfw = data.sensitive + + output.statusnet_html = data.content + + // Not exactly the same but works? + output.text = data.content + + output.in_reply_to_status_id = data.in_reply_to_id + output.in_reply_to_user_id = data.in_reply_to_account_id + + // Missing!! fix in UI? + output.in_reply_to_screen_name = null + + // Not exactly the same but works + output.statusnet_conversation_id = data.id + + if (output.type === 'retweet') { + output.retweeted_status = parseStatus(data.reblog) + } + + output.summary = data.spoiler_text + output.external_url = data.url + + // FIXME missing!! + output.is_local = false + } else { + output.favorited = data.favorited + output.fave_num = data.fave_num + + output.repeated = data.repeated + output.repeat_num = data.repeat_num + + // catchall, temporary + // Object.assign(output, data) + + output.type = qvitterStatusType(data) + + if (data.nsfw === undefined) { + output.nsfw = isNsfw(data) + if (data.retweeted_status) { + output.nsfw = data.retweeted_status.nsfw + } + } else { + output.nsfw = data.nsfw + } + + output.statusnet_html = data.statusnet_html + output.text = data.text + + output.in_reply_to_status_id = data.in_reply_to_status_id + output.in_reply_to_user_id = data.in_reply_to_user_id + output.in_reply_to_screen_name = data.in_reply_to_screen_name + + output.statusnet_conversation_id = data.statusnet_conversation_id + + if (output.type === 'retweet') { + output.retweeted_status = parseStatus(data.retweeted_status) + } + + output.summary = data.summary + output.external_url = data.external_url + output.is_local = data.is_local + } + + output.id = String(data.id) + output.visibility = data.visibility + output.created_at = new Date(data.created_at) + + output.user = parseUser(masto ? data.account : data.user) + + output.attentions = ((masto ? data.mentions : data.attentions) || []).map(parseUser) + + output.attachments = ((masto ? data.media_attachments : data.attachments) || []) + .map(parseAttachment) + + const retweetedStatus = masto ? data.reblog : data.retweeted_status + if (retweetedStatus) { + output.retweeted_status = parseStatus(retweetedStatus) + } + + return output +} + +export const parseNotification = (data) => { + const mastoDict = { + 'favourite': 'like', + 'reblog': 'repeat' + } + const masto = !data.hasOwnProperty('ntype') + const output = {} + + if (masto) { + output.type = mastoDict[data.type] || data.type + output.seen = null // missing + output.status = parseStatus(data.status) + output.action = output.status // not sure + output.from_profile = parseUser(data.account) + } else { + const parsedNotice = parseStatus(data.notice) + output.type = data.ntype + output.seen = Boolean(data.is_seen) + output.status = output.type === 'like' + ? parseStatus(data.notice.favorited_status) + : parsedNotice + output.action = parsedNotice + output.from_profile = parseUser(data.from_profile) + } + + output.created_at = new Date(data.created_at) + output.id = String(data.id) + + return output +} + +const isNsfw = (status) => { + const nsfwRegex = /#nsfw/i + return (status.tags || []).includes('nsfw') || !!status.text.match(nsfwRegex) +} diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index f5ac0d47..c3879677 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -10,8 +10,8 @@ export const visibleTypes = store => ([ ].filter(_ => _)) export const visibleNotificationsFromStore = store => { - // Don't know why, but sortBy([seen, -action.id]) doesn't work. - let sortedNotifications = sortBy(notificationsFromStore(store), ({action}) => -action.id) + // map is just to clone the array since sort mutates it and it causes some issues + let sortedNotifications = notificationsFromStore(store).map(_ => _).sort((a, b) => a.action.id > b.action.id ? -1 : 1) sortedNotifications = sortBy(sortedNotifications, 'seen') return sortedNotifications.filter((notification) => visibleTypes(store).includes(notification.type)) } diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js index 1e20d336..f1932bb6 100644 --- a/src/services/status_poster/status_poster.service.js +++ b/src/services/status_poster/status_poster.service.js @@ -5,7 +5,6 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, media = const mediaIds = map(media, 'id') return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks: store.state.instance.noAttachmentLinks}) - .then((data) => data.json()) .then((data) => { if (!data.error) { store.dispatch('addNewStatuses', { diff --git a/test/fixtures/mastoapi.json b/test/fixtures/mastoapi.json new file mode 100644 index 00000000..858d7a0d --- /dev/null +++ b/test/fixtures/mastoapi.json @@ -0,0 +1,1582 @@ +[{ + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@sampo god i wish i was there", + "created_at": "2019-01-17T16:29:23.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10423476", + "in_reply_to_account_id": "14660", + "in_reply_to_id": "10423197", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "sampo@pleroma.soykaf.com", + "id": "14660", + "url": "https://pleroma.soykaf.com/users/sampo", + "username": "sampo" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639", + "url": "https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@feld
Jotaro - YES YES YES YES YES-sq…", + "created_at": "2019-01-17T16:29:12.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10423465", + "in_reply_to_account_id": "982", + "in_reply_to_id": "10423446", + "language": null, + "media_attachments": [{ + "description": "Jotaro - YES YES YES YES YES-sq_Fm7qfRQk.webm", + "id": "1104859167", + "preview_url": "https://shigusegubu.club/media/4596ab2f-28e6-4f26-83db-0f26f29848cd/Jotaro - YES YES YES YES YES-sq_Fm7qfRQk.webm", + "remote_url": "https://shigusegubu.club/media/4596ab2f-28e6-4f26-83db-0f26f29848cd/Jotaro - YES YES YES YES YES-sq_Fm7qfRQk.webm", + "text_url": "https://shigusegubu.club/media/4596ab2f-28e6-4f26-83db-0f26f29848cd/Jotaro - YES YES YES YES YES-sq_Fm7qfRQk.webm", + "type": "video", + "url": "https://shigusegubu.club/media/4596ab2f-28e6-4f26-83db-0f26f29848cd/Jotaro - YES YES YES YES YES-sq_Fm7qfRQk.webm" + }], + "mentions": [{ + "acct": "feld@bikeshed.party", + "id": "982", + "url": "https://bikeshed.party/users/feld", + "username": "feld" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 1, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/6634d32b-96a8-4852-a3db-ac8730715779", + "url": "https://shigusegubu.club/objects/6634d32b-96a8-4852-a3db-ac8730715779", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "heh
scrot_17-01-19_18-16-40.png", + "created_at": "2019-01-17T16:28:49.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10423449", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": "scrot_17-01-19_18-16-40.png", + "id": "-804793492", + "preview_url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png", + "remote_url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png", + "text_url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png", + "type": "image", + "url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png" + }], + "mentions": [{ + "acct": "sampo@pleroma.soykaf.com", + "id": "14660", + "url": "https://pleroma.soykaf.com/users/sampo", + "username": "sampo" + }], + "muted": false, + "reblog": { + "account": { + "acct": "sampo@pleroma.soykaf.com", + "avatar": "https://pleroma.soykaf.com/media/f820f4cb-0fc8-4e4d-9521-c896ad2f93b5/c6e8d2e6d471616b82031e8348782e057aee7f1ed3214fe1522415e65694aa07.png", + "avatar_static": "https://pleroma.soykaf.com/media/f820f4cb-0fc8-4e4d-9521-c896ad2f93b5/c6e8d2e6d471616b82031e8348782e057aee7f1ed3214fe1522415e65694aa07.png", + "bot": false, + "created_at": "2018-04-02T21:19:46.000Z", + "display_name": "sampo", + "emojis": [], + "fields": [], + "followers_count": 23, + "following_count": 7, + "header": "https://pleroma.soykaf.com/media/23c3c6e1-863f-4aa2-9711-ab25bfeadd10/6aa0c00c79010d9a735c3e25d7e53da5a9fb4231720b4bd5b6904cd963b4c1cd.png", + "header_static": "https://pleroma.soykaf.com/media/23c3c6e1-863f-4aa2-9711-ab25bfeadd10/6aa0c00c79010d9a735c3e25d7e53da5a9fb4231720b4bd5b6904cd963b4c1cd.png", + "id": "14660", + "locked": false, + "note": "welcome to binlan 🇫🇮 prööh", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 17647, + "url": "https://pleroma.soykaf.com/users/sampo", + "username": "sampo" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "heh
scrot_17-01-19_18-16-40.png", + "created_at": "2019-01-17T16:20:06.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 7, + "id": "10423197", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": "scrot_17-01-19_18-16-40.png", + "id": "-804793492", + "preview_url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png", + "remote_url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png", + "text_url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png", + "type": "image", + "url": "https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png" + }], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 2, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://pleroma.soykaf.com/objects/bf7e43d4-5048-4176-8519-58e3e1014f8b", + "url": "https://pleroma.soykaf.com/objects/bf7e43d4-5048-4176-8519-58e3e1014f8b", + "visibility": "public" + }, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://pleroma.soykaf.com/objects/bf7e43d4-5048-4176-8519-58e3e1014f8b", + "url": "https://pleroma.soykaf.com/objects/bf7e43d4-5048-4176-8519-58e3e1014f8b", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@feld
Uhh No.avi-Xrne2-gOoqU.webm", + "created_at": "2019-01-17T16:27:29.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10423391", + "in_reply_to_account_id": "982", + "in_reply_to_id": "10423380", + "language": null, + "media_attachments": [{ + "description": "Uhh No.avi-Xrne2-gOoqU.webm", + "id": "483179103", + "preview_url": "https://shigusegubu.club/media/65de40a9-a99b-4207-97d0-40dd836532db/Uhh No.avi-Xrne2-gOoqU.webm", + "remote_url": "https://shigusegubu.club/media/65de40a9-a99b-4207-97d0-40dd836532db/Uhh No.avi-Xrne2-gOoqU.webm", + "text_url": "https://shigusegubu.club/media/65de40a9-a99b-4207-97d0-40dd836532db/Uhh No.avi-Xrne2-gOoqU.webm", + "type": "video", + "url": "https://shigusegubu.club/media/65de40a9-a99b-4207-97d0-40dd836532db/Uhh No.avi-Xrne2-gOoqU.webm" + }], + "mentions": [{ + "acct": "feld@bikeshed.party", + "id": "982", + "url": "https://bikeshed.party/users/feld", + "username": "feld" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/0f963ca1-a263-41ca-a43c-b5d26d0a08e9", + "url": "https://shigusegubu.club/objects/0f963ca1-a263-41ca-a43c-b5d26d0a08e9", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@fedebot lewd", + "created_at": "2019-01-17T16:17:46.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10423144", + "in_reply_to_account_id": "16633", + "in_reply_to_id": "10423114", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "fedebot@social.super-niche.club", + "id": "16633", + "url": "https://social.super-niche.club/user/31", + "username": "fedebot" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/3f809bd8-656f-4a29-81d4-80eed6916eb0", + "url": "https://shigusegubu.club/objects/3f809bd8-656f-4a29-81d4-80eed6916eb0", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "#animeirl https://social.super-niche.club/attachment/368974", + "created_at": "2019-01-17T14:20:42.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10419966", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": null, + "id": "1373175917", + "preview_url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg", + "remote_url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg", + "text_url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg", + "type": "image", + "url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg" + }], + "mentions": [{ + "acct": "fedebot@social.super-niche.club", + "id": "16633", + "url": "https://social.super-niche.club/user/31", + "username": "fedebot" + }], + "muted": false, + "reblog": { + "account": { + "acct": "fedebot@social.super-niche.club", + "avatar": "https://social.super-niche.club/avatar/31-300-20180411174535.jpeg", + "avatar_static": "https://social.super-niche.club/avatar/31-300-20180411174535.jpeg", + "bot": false, + "created_at": "2018-04-11T21:08:46.000Z", + "display_name": "Federation Bot", + "emojis": [], + "fields": [], + "followers_count": 6, + "following_count": 135, + "header": "https://shigusegubu.club/images/banner.png", + "header_static": "https://shigusegubu.club/images/banner.png", + "id": "16633", + "locked": false, + "note": "", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 6335, + "url": "https://social.super-niche.club/user/31", + "username": "fedebot" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "#animeirl https://social.super-niche.club/attachment/368974", + "created_at": "2019-01-17T14:11:08.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 4, + "id": "10419912", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": null, + "id": "1373175917", + "preview_url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg", + "remote_url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg", + "text_url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg", + "type": "image", + "url": "https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg" + }], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 3, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [{ + "name": "animeirl", + "url": "/tag/animeirl" + }], + "uri": "tag:social.super-niche.club,2019-01-17:noticeId=2353002:objectType=note", + "url": "https://social.super-niche.club/notice/2353002", + "visibility": "public" + }, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [{ + "name": "animeirl", + "url": "/tag/animeirl" + }], + "uri": "tag:social.super-niche.club,2019-01-17:noticeId=2353002:objectType=note", + "url": "tag:social.super-niche.club,2019-01-17:noticeId=2353002:objectType=note", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "ruin a distro in 3 words
image.png", + "created_at": "2019-01-17T14:13:36.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10419787", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": "image.png", + "id": "-948580066", + "preview_url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png", + "remote_url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png", + "text_url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png", + "type": "image", + "url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png" + }], + "mentions": [{ + "acct": "ivesen@miniwa.moe", + "id": "39686", + "url": "https://miniwa.moe/users/ivesen", + "username": "ivesen" + }], + "muted": false, + "reblog": { + "account": { + "acct": "ivesen@miniwa.moe", + "avatar": "https://miniwa.moe/media/745353b1-89b0-4597-bd76-3ed6ec122c03/504e655c84a6a6d61d34f8daaf3baa6627088f73427763a044729dbf85cff4b8.png", + "avatar_static": "https://miniwa.moe/media/745353b1-89b0-4597-bd76-3ed6ec122c03/504e655c84a6a6d61d34f8daaf3baa6627088f73427763a044729dbf85cff4b8.png", + "bot": false, + "created_at": "2018-09-08T17:55:27.000Z", + "display_name": "tsumiki :bun:", + "emojis": [{ + "shortcode": "bun", + "static_url": "https://miniwa.moe/emoji/custom/bun.png", + "url": "https://miniwa.moe/emoji/custom/bun.png", + "visible_in_picker": false + }], + "fields": [], + "followers_count": 8, + "following_count": 3, + "header": "https://miniwa.moe/media/bff7489f-22bb-4170-a68b-ba0e1c52116e/cf69fbec904a109f8c41b97ec03bfa7dbe49992fd7d61528a933740789b55eb1.gif", + "header_static": "https://miniwa.moe/media/bff7489f-22bb-4170-a68b-ba0e1c52116e/cf69fbec904a109f8c41b97ec03bfa7dbe49992fd7d61528a933740789b55eb1.gif", + "id": "39686", + "locked": false, + "note": "native resident of the black lizard planet trying to understand hoomanslocated near niflheimr xmpp: ivesen@xmpp.xyz | matrix: no | email: anything you could imagine @ ivesen.moe", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 13174, + "url": "https://miniwa.moe/users/ivesen", + "username": "ivesen" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "ruin a distro in 3 words
image.png", + "created_at": "2019-01-17T13:41:35.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 3, + "id": "10418865", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": "image.png", + "id": "-948580066", + "preview_url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png", + "remote_url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png", + "text_url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png", + "type": "image", + "url": "https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png" + }], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 6, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://miniwa.moe/objects/448e2944-0ecd-457f-92c3-cb454f2b0fab", + "url": "https://miniwa.moe/objects/448e2944-0ecd-457f-92c3-cb454f2b0fab", + "visibility": "public" + }, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://miniwa.moe/objects/448e2944-0ecd-457f-92c3-cb454f2b0fab", + "url": "https://miniwa.moe/objects/448e2944-0ecd-457f-92c3-cb454f2b0fab", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@eal thanks, alex jacobson :denton:", + "created_at": "2019-01-17T14:12:42.000Z", + "emojis": [{ + "shortcode": "denton", + "static_url": "https://shigusegubu.club/emoji/sgsgb/denton.png", + "url": "https://shigusegubu.club/emoji/sgsgb/denton.png", + "visible_in_picker": false + }], + "favourited": false, + "favourites_count": 0, + "id": "10419752", + "in_reply_to_account_id": "570", + "in_reply_to_id": "10419729", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "eal@social.sakamoto.gq", + "id": "570", + "url": "https://social.sakamoto.gq/users/eal", + "username": "eal" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/38b1bc44-15d8-40dd-b1aa-937e0ff4a86d", + "url": "https://shigusegubu.club/objects/38b1bc44-15d8-40dd-b1aa-937e0ff4a86d", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@pony yeah", + "created_at": "2019-01-17T14:11:01.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10419697", + "in_reply_to_account_id": "4395", + "in_reply_to_id": "10419694", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "pony@blovice.bahnhof.cz", + "id": "4395", + "url": "https://blovice.bahnhof.cz/users/pony", + "username": "pony" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/fbff5da4-a517-42a9-bca9-17cae8cf2542", + "url": "https://shigusegubu.club/objects/fbff5da4-a517-42a9-bca9-17cae8cf2542", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "i probably shouldn't be watchng 4-hour stream vods of joel playing doom", + "created_at": "2019-01-17T14:10:34.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10419684", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/4007d659-27c6-4577-be10-fd134f5e4e7e", + "url": "https://shigusegubu.club/objects/4007d659-27c6-4577-be10-fd134f5e4e7e", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "i got out of bed.", + "created_at": "2019-01-17T14:10:18.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10419671", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/59912d51-1cc6-4dc7-828c-f167e6c8b391", + "url": "https://shigusegubu.club/objects/59912d51-1cc6-4dc7-828c-f167e6c8b391", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "New day!", + "created_at": "2019-01-17T13:25:56.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 2, + "id": "10418390", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 1, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/62690bce-3f49-4047-9c8e-8941f2f79e10", + "url": "https://shigusegubu.club/objects/62690bce-3f49-4047-9c8e-8941f2f79e10", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@lanodan you either use nvidia-blob or don't use nvidia. Unless it's a very VERY old GPU.", + "created_at": "2019-01-16T22:21:10.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10395196", + "in_reply_to_account_id": "27194", + "in_reply_to_id": "10394997", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "lanodan@queer.hacktivis.me", + "id": "27194", + "url": "https://queer.hacktivis.me/users/lanodan", + "username": "lanodan" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/818f3dd0-2ff8-4def-a170-e4d4c405f387", + "url": "https://shigusegubu.club/objects/818f3dd0-2ff8-4def-a170-e4d4c405f387", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@lain
1. 🔪
2. trigger trypophobia
3. sprinkle with spices
4. pee on it
5. pat pat
6. blanket time
7. bring in the cocaine
8. ?????????????
9. ???? hitler ???????
10. ???????????
11. ???? Adobe After Effects ????
12. !!!!!!!!!!!!!!BREAK IT!!!!!!!!!!!
13. !!!kindar suprize!!
14. (time skip for two hours, realisation that using cocaine was a bad idea)
15. (still not over from cocaine)
16. куличики! Плов, огурцы, это еще что бля
17. fuck it, play it cool, just put meat on the mounds
18. note to self: never use cocaine in culinary ever again", + "created_at": "2019-01-16T22:20:50.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10395183", + "in_reply_to_account_id": "58", + "in_reply_to_id": "10394990", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "lain@pleroma.soykaf.com", + "id": "58", + "url": "https://pleroma.soykaf.com/users/lain", + "username": "lain" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/0783a193-c097-488d-8944-47df9372cd6e", + "url": "https://shigusegubu.club/objects/0783a193-c097-488d-8944-47df9372cd6e", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@cathode me too", + "created_at": "2019-01-16T22:09:28.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10394697", + "in_reply_to_account_id": "55339", + "in_reply_to_id": "10394677", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "cathode@pleroma.site", + "id": "55339", + "url": "https://pleroma.site/users/cathode", + "username": "cathode" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/145d5252-7b8e-467d-9f36-1db0818f452f", + "url": "https://shigusegubu.club/objects/145d5252-7b8e-467d-9f36-1db0818f452f", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "I love pleroma and lain", + "created_at": "2019-01-16T22:09:24.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10394690", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "cathode@pleroma.site", + "id": "55339", + "url": "https://pleroma.site/users/cathode", + "username": "cathode" + }], + "muted": false, + "reblog": { + "account": { + "acct": "cathode@pleroma.site", + "avatar": "https://pleroma.site/media/cc874883-fafb-4f25-80f2-e16677e83621/c50a4ea55a99fdf3b69bf66ce2768bc2c48c6135332914bb49445aab1211d794.gif", + "avatar_static": "https://pleroma.site/media/cc874883-fafb-4f25-80f2-e16677e83621/c50a4ea55a99fdf3b69bf66ce2768bc2c48c6135332914bb49445aab1211d794.gif", + "bot": false, + "created_at": "2018-12-14T21:47:14.000Z", + "display_name": "gay hug bug loves you", + "emojis": [], + "fields": [], + "followers_count": 0, + "following_count": 0, + "header": "https://pleroma.site/media/8170fac2-fc17-4b9c-8624-52491d56f769/8cb558d3960254180b10860012a519253baaa1db88a0476da02be6a558015b34.jpg", + "header_static": "https://pleroma.site/media/8170fac2-fc17-4b9c-8624-52491d56f769/8cb558d3960254180b10860012a519253baaa1db88a0476da02be6a558015b34.jpg", + "id": "55339", + "locked": false, + "note": "he/him but you can call me anything really

welcome to my profile :333

https://cathode.neocities.org/", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 16, + "url": "https://pleroma.site/users/cathode", + "username": "cathode" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "I love pleroma and lain", + "created_at": "2019-01-16T22:08:50.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 4, + "id": "10394677", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 2, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://pleroma.site/objects/3076c055-0e34-4cf9-86ca-2d148b9b694a", + "url": "https://pleroma.site/objects/3076c055-0e34-4cf9-86ca-2d148b9b694a", + "visibility": "public" + }, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://pleroma.site/objects/3076c055-0e34-4cf9-86ca-2d148b9b694a", + "url": "https://pleroma.site/objects/3076c055-0e34-4cf9-86ca-2d148b9b694a", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@lain that trick he does in the very beginning with that smile oh my god", + "created_at": "2019-01-16T22:08:15.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 2, + "id": "10394647", + "in_reply_to_account_id": "58", + "in_reply_to_id": "10394582", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "lain@pleroma.soykaf.com", + "id": "58", + "url": "https://pleroma.soykaf.com/users/lain", + "username": "lain" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/d4eb7c46-02f9-4b1f-83af-926cefa21f33", + "url": "https://shigusegubu.club/objects/d4eb7c46-02f9-4b1f-83af-926cefa21f33", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": ".", + "created_at": "2019-01-16T22:08:01.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 0, + "id": "10394636", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": null, + "id": "-1089888084", + "preview_url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm", + "remote_url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm", + "text_url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm", + "type": "video", + "url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm" + }], + "mentions": [{ + "acct": "lain@pleroma.soykaf.com", + "id": "58", + "url": "https://pleroma.soykaf.com/users/lain", + "username": "lain" + }], + "muted": false, + "reblog": { + "account": { + "acct": "lain@pleroma.soykaf.com", + "avatar": "https://pleroma.soykaf.com/media/57abd080-73da-4541-92c7-506d0008bce3/04c76c19e87b23f759fac1118db5e5c4fe0b0ba8d67942a9a8f4e88108543abd.jpeg", + "avatar_static": "https://pleroma.soykaf.com/media/57abd080-73da-4541-92c7-506d0008bce3/04c76c19e87b23f759fac1118db5e5c4fe0b0ba8d67942a9a8f4e88108543abd.jpeg", + "bot": false, + "created_at": "2017-12-17T22:26:31.000Z", + "display_name": "you're looking at an ace", + "emojis": [], + "fields": [], + "followers_count": 29, + "following_count": 1389, + "header": "https://pleroma.soykaf.com/images/banner.png", + "header_static": "https://pleroma.soykaf.com/images/banner.png", + "id": "58", + "locked": false, + "note": "blushy-crushy fediverse idol + pleroma dev.
let's be friends
ぷれろまの生徒会長。謎の外人。日本語OK.
公主病.
I invented the internet.
xmpp: lain@trashserver.net
matrix: lambadalambda@matrix.heldscal.la
pgp: 58B48DE582EE103C964735A1F9C6698E14CCE33C", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 35042, + "url": "https://pleroma.soykaf.com/users/lain", + "username": "lain" + }, + "application": { + "name": "Web", + "website": null + }, + "content": ".", + "created_at": "2019-01-16T22:06:35.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 2, + "id": "10394582", + "in_reply_to_account_id": null, + "in_reply_to_id": null, + "language": null, + "media_attachments": [{ + "description": null, + "id": "-1089888084", + "preview_url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm", + "remote_url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm", + "text_url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm", + "type": "video", + "url": "https://pleroma.soykaf.com/media/d985e90d-b079-4f6a-a5bc-9b5e26b1fa5a/Tusky_1547676391813_2VJ7ITWJ3T.webm" + }], + "mentions": [], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 1, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://pleroma.soykaf.com/objects/338b6bd2-3c2d-40fe-93a3-28b688782733", + "url": "https://pleroma.soykaf.com/objects/338b6bd2-3c2d-40fe-93a3-28b688782733", + "visibility": "public" + }, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://pleroma.soykaf.com/objects/338b6bd2-3c2d-40fe-93a3-28b688782733", + "url": "https://pleroma.soykaf.com/objects/338b6bd2-3c2d-40fe-93a3-28b688782733", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@proxeus good song", + "created_at": "2019-01-16T22:04:23.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10394506", + "in_reply_to_account_id": "17544", + "in_reply_to_id": "10393849", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "proxeus@iscute.moe", + "id": "17544", + "url": "https://iscute.moe/users/proxeus", + "username": "proxeus" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/f472f4ed-8b0b-492f-9d53-d69eda79629d", + "url": "https://shigusegubu.club/objects/f472f4ed-8b0b-492f-9d53-d69eda79629d", + "visibility": "public" +}, { + "account": { + "acct": "hj", + "avatar": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "avatar_static": "https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png", + "bot": false, + "created_at": "2017-12-17T21:54:14.000Z", + "display_name": "whatever whatever whatever witch", + "emojis": [], + "fields": [], + "followers_count": 705, + "following_count": 326, + "header": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "header_static": "https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png", + "id": "1", + "locked": false, + "note": "Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it's truly private matter and you're instance's admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.", + "pleroma": { + "confirmation_pending": false, + "tags": null + }, + "source": { + "note": "", + "privacy": "public", + "sensitive": false + }, + "statuses_count": 41775, + "url": "https://shigusegubu.club/users/hj", + "username": "hj" + }, + "application": { + "name": "Web", + "website": null + }, + "content": "@cdmnky olivia been repeating \"hey i moved\" post like once every day or so", + "created_at": "2019-01-16T21:56:05.000Z", + "emojis": [], + "favourited": false, + "favourites_count": 1, + "id": "10393859", + "in_reply_to_account_id": "60913", + "in_reply_to_id": "10393808", + "language": null, + "media_attachments": [], + "mentions": [{ + "acct": "cdmnky", + "id": "60913", + "url": "https://shigusegubu.club/users/cdmnky", + "username": "cdmnky" + }], + "muted": false, + "reblog": null, + "reblogged": false, + "reblogs_count": 0, + "replies_count": 0, + "sensitive": false, + "spoiler_text": "", + "tags": [], + "uri": "https://shigusegubu.club/objects/d6fb4fd2-1f6a-4446-a1a6-5edd34050096", + "url": "https://shigusegubu.club/objects/d6fb4fd2-1f6a-4446-a1a6-5edd34050096", + "visibility": "public" +}] diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 7d403312..33628b9b 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -1,76 +1,31 @@ import { cloneDeep } from 'lodash' -import { defaultState, mutations, findMaxId, prepareStatus, statusType } from '../../../../src/modules/statuses.js' +import { defaultState, mutations, prepareStatus } from '../../../../src/modules/statuses.js' // eslint-disable-next-line camelcase -const makeMockStatus = ({id, text, is_post_verb = true}) => { +const makeMockStatus = ({id, text, type = 'status'}) => { return { id, - user: {id: 0}, + user: {id: '0'}, name: 'status', text: text || `Text number ${id}`, fave_num: 0, uri: '', - is_post_verb, + type, attentions: [] } } -describe('Statuses.statusType', () => { - it('identifies favorites', () => { - const fav = { - uri: 'tag:soykaf.com,2016-08-21:fave:2558:note:339495:2016-08-21T16:54:04+00:00' - } - - const mastoFav = { - uri: 'tag:mastodon.social,2016-11-27:objectId=73903:objectType=Favourite' - } - - expect(statusType(fav)).to.eql('favorite') - expect(statusType(mastoFav)).to.eql('favorite') - }) -}) - describe('Statuses.prepareStatus', () => { - it('sets nsfw for statuses with the #nsfw tag', () => { - const safe = makeMockStatus({id: 1, text: 'Hello oniichan'}) - const nsfw = makeMockStatus({id: 1, text: 'Hello oniichan #nsfw'}) - - expect(prepareStatus(safe).nsfw).to.eq(false) - expect(prepareStatus(nsfw).nsfw).to.eq(true) - }) - - it('leaves existing nsfw settings alone', () => { - const nsfw = makeMockStatus({id: 1, text: 'Hello oniichan #nsfw'}) - nsfw.nsfw = false - - expect(prepareStatus(nsfw).nsfw).to.eq(false) - }) - it('sets deleted flag to false', () => { - const aStatus = makeMockStatus({id: 1, text: 'Hello oniichan'}) + const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'}) expect(prepareStatus(aStatus).deleted).to.eq(false) }) }) -describe('Statuses.findMaxId', () => { - it('returns the largest id in any of the given arrays', () => { - const statusesOne = [{ id: 100 }, { id: 2 }] - const statusesTwo = [{ id: 3 }] - - const maxId = findMaxId(statusesOne, statusesTwo) - expect(maxId).to.eq(100) - }) - - it('returns undefined for empty arrays', () => { - const maxId = findMaxId([], []) - expect(maxId).to.eq(undefined) - }) -}) - describe('The Statuses module', () => { it('adds the status to allStatuses and to the given timeline', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) @@ -82,7 +37,7 @@ describe('The Statuses module', () => { it('counts the status as new if it has not been seen on this timeline', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' }) @@ -100,7 +55,7 @@ describe('The Statuses module', () => { it('add the statuses to allStatuses if no timeline is given', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status] }) @@ -112,7 +67,7 @@ describe('The Statuses module', () => { it('adds the status to allStatuses and to the given timeline, directly visible', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -124,10 +79,10 @@ describe('The Statuses module', () => { it('removes statuses by tag on deletion', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const otherStatus = makeMockStatus({id: 3}) + const status = makeMockStatus({id: '1'}) + const otherStatus = makeMockStatus({id: '3'}) status.uri = 'xxx' - const deletion = makeMockStatus({id: 2, is_post_verb: false}) + const deletion = makeMockStatus({id: '2', type: 'deletion'}) deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' deletion.uri = 'xxx' @@ -137,36 +92,36 @@ describe('The Statuses module', () => { expect(state.allStatuses).to.eql([otherStatus]) expect(state.timelines.public.statuses).to.eql([otherStatus]) expect(state.timelines.public.visibleStatuses).to.eql([otherStatus]) - expect(state.timelines.public.maxId).to.eql(3) + expect(state.timelines.public.maxId).to.eql('3') }) it('does not update the maxId when the noIdUpdate flag is set', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const secondStatus = makeMockStatus({id: 2}) + const status = makeMockStatus({id: '1'}) + const secondStatus = makeMockStatus({id: '2'}) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.maxId).to.eql(1) + expect(state.timelines.public.maxId).to.eql('1') mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true }) expect(state.timelines.public.statuses).to.eql([secondStatus, status]) expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status]) - expect(state.timelines.public.maxId).to.eql(1) + expect(state.timelines.public.maxId).to.eql('1') }) it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { const state = cloneDeep(defaultState) - const nonVisibleStatus = makeMockStatus({id: 1}) - const status = makeMockStatus({id: 3}) - const statusTwo = makeMockStatus({id: 2}) - const statusThree = makeMockStatus({id: 4}) + const nonVisibleStatus = makeMockStatus({id: '1'}) + const status = makeMockStatus({id: '3'}) + const statusTwo = makeMockStatus({id: '2'}) + const statusThree = makeMockStatus({id: '4'}) mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' }) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.minVisibleId).to.equal(2) + expect(state.timelines.public.minVisibleId).to.equal('2') mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' }) @@ -176,9 +131,9 @@ describe('The Statuses module', () => { it('splits retweets from their status and links them', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const retweet = makeMockStatus({id: 2, is_post_verb: false}) - const modStatus = makeMockStatus({id: 1, text: 'something else'}) + const status = makeMockStatus({id: '1'}) + const retweet = makeMockStatus({id: '2', type: 'retweet'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) retweet.retweeted_status = status @@ -187,22 +142,22 @@ describe('The Statuses module', () => { expect(state.timelines.public.visibleStatuses).to.have.length(1) expect(state.timelines.public.statuses).to.have.length(1) expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].id).to.equal(1) - expect(state.allStatuses[1].id).to.equal(2) + expect(state.allStatuses[0].id).to.equal('1') + expect(state.allStatuses[1].id).to.equal('2') // It refers to the modified status. mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' }) expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].id).to.equal(1) + expect(state.allStatuses[0].id).to.equal('1') expect(state.allStatuses[0].text).to.equal(modStatus.text) - expect(state.allStatuses[1].id).to.equal(2) + expect(state.allStatuses[1].id).to.equal('2') expect(retweet.retweeted_status.text).to.eql(modStatus.text) }) it('replaces existing statuses with the same id', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const modStatus = makeMockStatus({id: 1, text: 'something else'}) + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) // Add original status mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -218,9 +173,9 @@ describe('The Statuses module', () => { it('replaces existing statuses with the same id, coming from a retweet', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const modStatus = makeMockStatus({id: 1, text: 'something else'}) - const retweet = makeMockStatus({id: 2, is_post_verb: false}) + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + const retweet = makeMockStatus({id: '2', type: 'retweet'}) retweet.retweeted_status = modStatus // Add original status @@ -239,15 +194,15 @@ describe('The Statuses module', () => { it('handles favorite actions', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) const favorite = { - id: 2, - is_post_verb: false, + id: '2', + type: 'favorite', in_reply_to_status_id: '1', // The API uses strings here... uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', text: 'a favorited something by b', - user: { id: 99 } + user: { id: '99' } } mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -266,12 +221,12 @@ describe('The Statuses module', () => { // If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request. const user = { - id: 1 + id: '1' } const ownFavorite = { - id: 3, - is_post_verb: false, + id: '3', + type: 'favorite', in_reply_to_status_id: '1', // The API uses strings here... uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', text: 'a favorited something by b', @@ -287,16 +242,16 @@ describe('The Statuses module', () => { describe('notifications', () => { it('removes a notification when the notice gets removed', () => { - const user = { id: 1 } + const user = { id: '1' } const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const otherStatus = makeMockStatus({id: 3}) - const mentionedStatus = makeMockStatus({id: 2}) + const status = makeMockStatus({id: '1'}) + const otherStatus = makeMockStatus({id: '3'}) + const mentionedStatus = makeMockStatus({id: '2'}) mentionedStatus.attentions = [user] mentionedStatus.uri = 'xxx' otherStatus.attentions = [user] - const deletion = makeMockStatus({id: 4, is_post_verb: false}) + const deletion = makeMockStatus({id: '4', type: 'deletion'}) deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' deletion.uri = 'xxx' @@ -305,10 +260,12 @@ describe('The Statuses module', () => { state, { notifications: [{ - ntype: 'mention', + from_profile: { id: '2' }, + id: '998', + type: 'mention', status: otherStatus, - notice: otherStatus, - is_seen: false + action: otherStatus, + seen: false }] }) @@ -317,10 +274,12 @@ describe('The Statuses module', () => { state, { notifications: [{ - ntype: 'mention', + from_profile: { id: '2' }, + id: '999', + type: 'mention', status: mentionedStatus, - notice: mentionedStatus, - is_seen: false + action: mentionedStatus, + seen: false }] }) diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js index af60c9b3..b0f3c51e 100644 --- a/test/unit/specs/modules/users.spec.js +++ b/test/unit/specs/modules/users.spec.js @@ -6,8 +6,8 @@ describe('The users module', () => { describe('mutations', () => { it('adds new users to the set, merging in new information for old users', () => { const state = cloneDeep(defaultState) - const user = { id: 1, name: 'Guy' } - const modUser = { id: 1, name: 'Dude' } + const user = { id: '1', name: 'Guy' } + const modUser = { id: '1', name: 'Dude' } mutations.addNewUsers(state, [user]) expect(state.users).to.have.length(1) @@ -21,7 +21,7 @@ describe('The users module', () => { it('sets a mute bit on users', () => { const state = cloneDeep(defaultState) - const user = { id: 1, name: 'Guy' } + const user = { id: '1', name: 'Guy' } mutations.addNewUsers(state, [user]) mutations.setMuted(state, {user, muted: true}) @@ -38,11 +38,11 @@ describe('The users module', () => { it('returns user with matching screen_name', () => { const state = { users: [ - { screen_name: 'Guy', id: 1 } + { screen_name: 'Guy', id: '1' } ] } const name = 'Guy' - const expected = { screen_name: 'Guy', id: 1 } + const expected = { screen_name: 'Guy', id: '1' } expect(getters.userByName(state)(name)).to.eql(expected) }) }) @@ -51,11 +51,11 @@ describe('The users module', () => { it('returns user with matching id', () => { const state = { users: [ - { screen_name: 'Guy', id: 1 } + { screen_name: 'Guy', id: '1' } ] } - const id = 1 - const expected = { screen_name: 'Guy', id: 1 } + const id = '1' + const expected = { screen_name: 'Guy', id: '1' } expect(getters.userById(state)(id)).to.eql(expected) }) }) diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js new file mode 100644 index 00000000..703fecf1 --- /dev/null +++ b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js @@ -0,0 +1,270 @@ +import { parseStatus, parseUser, parseNotification } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js' +import mastoapidata from '../../../../fixtures/mastoapi.json' +import qvitterapidata from '../../../../fixtures/statuses.json' + +const makeMockStatusQvitter = (overrides = {}) => { + return Object.assign({ + activity_type: 'post', + attachments: [], + attentions: [], + created_at: 'Tue Jan 15 13:57:56 +0000 2019', + external_url: 'https://ap.example/whatever', + fave_num: 1, + favorited: false, + id: '10335970', + in_reply_to_ostatus_uri: null, + in_reply_to_profileurl: null, + in_reply_to_screen_name: null, + in_reply_to_status_id: null, + in_reply_to_user_id: null, + is_local: false, + is_post_verb: true, + possibly_sensitive: false, + repeat_num: 0, + repeated: false, + statusnet_conversation_id: '16300488', + statusnet_html: '

haha benis

', + summary: null, + tags: [], + text: 'haha benis', + uri: 'https://ap.example/whatever', + user: makeMockUserQvitter(), + visibility: 'public' + }, overrides) +} + +const makeMockUserQvitter = (overrides = {}) => { + return Object.assign({ + background_image: null, + cover_photo: '', + created_at: 'Mon Jan 14 13:56:51 +0000 2019', + default_scope: 'public', + description: 'ebin', + description_html: '

ebin

', + favourites_count: 0, + fields: [], + followers_count: 1, + following: true, + follows_you: true, + friends_count: 1, + id: '60717', + is_local: false, + locked: false, + name: 'Spurdo :ebin:', + name_html: 'Spurdo ', + no_rich_text: false, + pleroma: { confirmation_pending: false, tags: [] }, + profile_image_url: 'https://ap.example/whatever', + profile_image_url_https: 'https://ap.example/whatever', + profile_image_url_original: 'https://ap.example/whatever', + profile_image_url_profile_size: 'https://ap.example/whatever', + rights: { delete_others_notice: false }, + screen_name: 'spurdo@ap.example', + statuses_count: 46, + statusnet_blocking: false, + statusnet_profile_url: '' + }, overrides) +} + +const makeMockUserMasto = (overrides = {}) => { + return Object.assign({ + acct: 'hj', + avatar: + 'https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png', + avatar_static: + 'https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png', + bot: false, + created_at: '2017-12-17T21:54:14.000Z', + display_name: 'whatever whatever whatever witch', + emojis: [], + fields: [], + followers_count: 705, + following_count: 326, + header: + 'https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png', + header_static: + 'https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png', + id: '1', + locked: false, + note: + 'Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is @hj:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it\'s truly private matter and you\'re instance\'s admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.', + pleroma: { confirmation_pending: false, tags: null }, + source: { note: '', privacy: 'public', sensitive: false }, + statuses_count: 41775, + url: 'https://shigusegubu.club/users/hj', + username: 'hj' + }, overrides) +} + +const makeMockStatusMasto = (overrides = {}) => { + return Object.assign({ + account: makeMockUserMasto(), + application: { name: 'Web', website: null }, + content: + '@sampo god i wish i was there', + created_at: '2019-01-17T16:29:23.000Z', + emojis: [], + favourited: false, + favourites_count: 1, + id: '10423476', + in_reply_to_account_id: '14660', + in_reply_to_id: '10423197', + language: null, + media_attachments: [], + mentions: [ + { + acct: 'sampo@pleroma.soykaf.com', + id: '14660', + url: 'https://pleroma.soykaf.com/users/sampo', + username: 'sampo' + } + ], + muted: false, + reblog: null, + reblogged: false, + reblogs_count: 0, + replies_count: 0, + sensitive: false, + spoiler_text: '', + tags: [], + uri: 'https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639', + url: 'https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639', + visibility: 'public' + }, overrides) +} + +const makeMockNotificationQvitter = (overrides = {}) => { + return Object.assign({ + notice: makeMockStatusQvitter(), + ntype: 'follow', + from_profile: makeMockUserQvitter(), + is_seen: 0, + id: 123 + }, overrides) +} + +parseNotification +parseUser +parseStatus +makeMockStatusQvitter +makeMockUserQvitter + +describe('API Entities normalizer', () => { + describe('parseStatus', () => { + describe('QVitter preprocessing', () => { + it('doesn\'t blow up', () => { + const parsed = qvitterapidata.map(parseStatus) + expect(parsed.length).to.eq(qvitterapidata.length) + }) + + it('identifies favorites', () => { + const fav = { + uri: 'tag:soykaf.com,2016-08-21:fave:2558:note:339495:2016-08-21T16:54:04+00:00', + is_post_verb: false + } + + const mastoFav = { + uri: 'tag:mastodon.social,2016-11-27:objectId=73903:objectType=Favourite', + is_post_verb: false + } + + expect(parseStatus(makeMockStatusQvitter(fav))).to.have.property('type', 'favorite') + expect(parseStatus(makeMockStatusQvitter(mastoFav))).to.have.property('type', 'favorite') + }) + + it('processes repeats correctly', () => { + const post = makeMockStatusQvitter({ retweeted_status: null, id: 'deadbeef' }) + const repeat = makeMockStatusQvitter({ retweeted_status: post, is_post_verb: false, id: 'foobar' }) + + const parsedPost = parseStatus(post) + const parsedRepeat = parseStatus(repeat) + + expect(parsedPost).to.have.property('type', 'status') + expect(parsedRepeat).to.have.property('type', 'retweet') + expect(parsedRepeat).to.have.property('retweeted_status') + expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef') + }) + + it('sets nsfw for statuses with the #nsfw tag', () => { + const safe = makeMockStatusQvitter({id: '1', text: 'Hello oniichan'}) + const nsfw = makeMockStatusQvitter({id: '1', text: 'Hello oniichan #nsfw'}) + + expect(parseStatus(safe).nsfw).to.eq(false) + expect(parseStatus(nsfw).nsfw).to.eq(true) + }) + + it('leaves existing nsfw settings alone', () => { + const nsfw = makeMockStatusQvitter({id: '1', text: 'Hello oniichan #nsfw', nsfw: false}) + + expect(parseStatus(nsfw).nsfw).to.eq(false) + }) + }) + + describe('Mastoapi preprocessing and converting', () => { + it('doesn\'t blow up', () => { + const parsed = mastoapidata.map(parseStatus) + expect(parsed.length).to.eq(mastoapidata.length) + }) + + it('processes repeats correctly', () => { + const post = makeMockStatusMasto({ reblog: null, id: 'deadbeef' }) + const repeat = makeMockStatusMasto({ reblog: post, id: 'foobar' }) + + const parsedPost = parseStatus(post) + const parsedRepeat = parseStatus(repeat) + + expect(parsedPost).to.have.property('type', 'status') + expect(parsedRepeat).to.have.property('type', 'retweet') + expect(parsedRepeat).to.have.property('retweeted_status') + expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef') + }) + }) + }) + + // Statuses generally already contain some info regarding users and there's nearly 1:1 mapping, so very little to test + describe('parseUsers (MastoAPI)', () => { + it('sets correct is_local for users depending on their screen_name', () => { + const local = makeMockUserMasto({ acct: 'foo' }) + const remote = makeMockUserMasto({ acct: 'foo@bar.baz' }) + + expect(parseUser(local)).to.have.property('is_local', true) + expect(parseUser(remote)).to.have.property('is_local', false) + }) + }) + + // We currently use QvitterAPI notifications only, and especially due to MastoAPI lacking is_seen, support for MastoAPI + // is more of an afterthought + describe('parseNotifications (QvitterAPI)', () => { + it('correctly normalizes data to FE\'s format', () => { + const notif = makeMockNotificationQvitter({ + id: 123, + notice: makeMockStatusQvitter({ id: 444 }), + from_profile: makeMockUserQvitter({ id: 'spurdo' }) + }) + expect(parseNotification(notif)).to.have.property('id', '123') + expect(parseNotification(notif)).to.have.property('seen', false) + expect(parseNotification(notif)).to.have.deep.property('status.id', '444') + expect(parseNotification(notif)).to.have.deep.property('action.id', '444') + expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo') + }) + + it('correctly normalizes favorite notifications', () => { + const notif = makeMockNotificationQvitter({ + id: 123, + ntype: 'like', + notice: makeMockStatusQvitter({ + id: 444, + favorited_status: makeMockStatusQvitter({ id: 4412 }) + }), + is_seen: 1, + from_profile: makeMockUserQvitter({ id: 'spurdo' }) + }) + expect(parseNotification(notif)).to.have.property('id', '123') + expect(parseNotification(notif)).to.have.property('type', 'like') + expect(parseNotification(notif)).to.have.property('seen', true) + expect(parseNotification(notif)).to.have.deep.property('status.id', '4412') + expect(parseNotification(notif)).to.have.deep.property('action.id', '444') + expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo') + }) + }) +}) diff --git a/test/unit/specs/services/notification_utils/notification_utils.spec.js b/test/unit/specs/services/notification_utils/notification_utils.spec.js index c44b8c9e..e945459e 100644 --- a/test/unit/specs/services/notification_utils/notification_utils.spec.js +++ b/test/unit/specs/services/notification_utils/notification_utils.spec.js @@ -9,15 +9,15 @@ describe('NotificationUtils', () => { notifications: { data: [ { - action: { id: 1 }, + action: { id: '1' }, type: 'like' }, { - action: { id: 2 }, + action: { id: '2' }, type: 'mention' }, { - action: { id: 3 }, + action: { id: '3' }, type: 'repeat' } ] @@ -34,11 +34,11 @@ describe('NotificationUtils', () => { } const expected = [ { - action: { id: 3 }, + action: { id: '3' }, type: 'repeat' }, { - action: { id: 1 }, + action: { id: '1' }, type: 'like' } ] @@ -54,12 +54,12 @@ describe('NotificationUtils', () => { notifications: { data: [ { - action: { id: 1 }, + action: { id: '1' }, type: 'like', seen: false }, { - action: { id: 2 }, + action: { id: '2' }, type: 'mention', seen: true } @@ -77,7 +77,7 @@ describe('NotificationUtils', () => { } const expected = [ { - action: { id: 1 }, + action: { id: '1' }, type: 'like', seen: false }