logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe
commit: 3a3cf1d48a67c82bb2c94cafb0150b1fa8e4de1a
parent: 7651290e2be72ed35d7582757251eb151fdce953
Author: HJ <spam@hjkos.com>
Date:   Tue, 22 Jan 2019 17:46:08 +0000

Merge branch 'favorites' into 'develop'

Add Favorites TL to user profile, add some initial support for MastoAPI

Closes #265 and #262

See merge request pleroma/pleroma-fe!462

Diffstat:

Msrc/components/conversation-page/conversation-page.js4++--
Msrc/components/conversation/conversation.js16+++++++++-------
Msrc/components/status/status.js5++---
Msrc/components/tab_switcher/tab_switcher.jsx28++++++++++++++++++++--------
Msrc/components/user_profile/user_profile.js16++++++++++++++++
Msrc/components/user_profile/user_profile.vue1+
Msrc/i18n/en.json1+
Msrc/i18n/ru.json1+
Msrc/modules/statuses.js118++++++++++++++++++++++++++-----------------------------------------------------
Msrc/modules/users.js66+++++++++++++++++++++++++++++++++---------------------------------
Msrc/services/api/api.service.js52+++++++++++++++++++++++++++++++++++++++++++++++++---
Asrc/services/entity_normalizer/entity_normalizer.service.js263+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/services/notification_utils/notification_utils.js4++--
Msrc/services/status_poster/status_poster.service.js1-
Atest/fixtures/mastoapi.json1582+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/unit/specs/modules/statuses.spec.js153+++++++++++++++++++++++++++++--------------------------------------------------
Mtest/unit/specs/modules/users.spec.js16++++++++--------
Atest/unit/specs/services/entity_normalizer/entity_normalizer.spec.js270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/unit/specs/services/notification_utils/notification_utils.spec.js16++++++++--------
19 files changed, 2361 insertions(+), 252 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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 ( <div class={ classesWrapper.join(' ')}> <button onClick={this.activateTab(index)} class={ classesTab.join(' ') }>{slot.data.attrs.label}</button> </div> ) - }); - 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 ( <div class={active ? 'active' : 'hidden'}> {slot} </div> ) - }); + }) + return ( <div class="tab-switcher"> <div class="tabs"> diff --git 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 @@ -20,6 +20,7 @@ <i class="icon-spin3 animate-spin"></i> </div> </div> + <Timeline v-if="isUs" :label="$t('user_card.favorites')" :embedded="true" :title="$t('user_profile.favorites_title')" timeline-name="favorites" :timeline="favorites"/> </tab-switcher> </div> <div v-else class="panel user-profile-placeholder"> diff --git 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 @@ -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 @@ -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) - - const fresh = !notification.is_seen - const status = notification.ntype === 'like' - ? action.favorited_status - : action - - const result = { - type: notification.ntype, - status, - action, - seen: !fresh - } + 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 - 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"14660\" href=\"https://pleroma.soykaf.com/users/sampo\">@<span>sampo</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"982\" href=\"https://bikeshed.party/users/feld\">@<span>feld</span></a></span><br /><a href=\"https://shigusegubu.club/media/4596ab2f-28e6-4f26-83db-0f26f29848cd/Jotaro - YES YES YES YES YES-sq_Fm7qfRQk.webm\">Jotaro - YES YES YES YES YES-sq…</a>", + "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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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<br /><a href=\"https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png\">scrot_17-01-19_18-16-40.png</a>", + "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<br /><a href=\"https://pleroma.soykaf.com/media/fd13d009-2c1f-4a53-9762-257e592a7ff6/scrot_17-01-19_18-16-40.png\">scrot_17-01-19_18-16-40.png</a>", + "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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"982\" href=\"https://bikeshed.party/users/feld\">@<span>feld</span></a></span><br /><a href=\"https://shigusegubu.club/media/65de40a9-a99b-4207-97d0-40dd836532db/Uhh No.avi-Xrne2-gOoqU.webm\">Uhh No.avi-Xrne2-gOoqU.webm</a>", + "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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"16633\" href=\"https://social.super-niche.club/user/31\">@<span>fedebot</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "#<span><a href=\"https://social.super-niche.club/tag/animeirl\">animeirl</a></span> <a href=\"https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg\" title=\"https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg\">https://social.super-niche.club/attachment/368974</a>", + "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": "#<span><a href=\"https://social.super-niche.club/tag/animeirl\">animeirl</a></span> <a href=\"https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg\" title=\"https://social.super-niche.club/file/c366cae1e5820d68dc1e6bc7a06a2f2bc15166de1faabea4f060490d45fdf527.jpg\">https://social.super-niche.club/attachment/368974</a>", + "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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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<br /><a href=\"https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png\">image.png</a>", + "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<br /><a href=\"https://miniwa.moe/media/4076945a-4a3b-4fb3-8b62-04eb401697ff/image.png\">image.png</a>", + "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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"570\" href=\"https://social.sakamoto.gq/users/eal\">@<span>eal</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"4395\" href=\"https://blovice.bahnhof.cz/users/pony\">@<span>pony</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"27194\" href=\"https://queer.hacktivis.me/users/lanodan\">@<span>lanodan</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"58\" href=\"https://pleroma.soykaf.com/users/lain\">@<span>lain</span></a></span> <br />1. 🔪<br />2. trigger trypophobia<br />3. sprinkle with spices<br />4. pee on it<br />5. pat pat<br />6. blanket time<br />7. bring in the cocaine<br />8. ?????????????<br />9. ???? hitler ???????<br />10. ???????????<br />11. ???? Adobe After Effects ????<br />12. !!!!!!!!!!!!!!BREAK IT!!!!!!!!!!!<br />13. !!!kindar suprize!!<br />14. (time skip for two hours, realisation that using cocaine was a bad idea)<br />15. (still not over from cocaine)<br />16. куличики! Плов, огурцы, это еще что бля<br />17. fuck it, play it cool, just put meat on the mounds<br />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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"55339\" href=\"https://pleroma.site/users/cathode\">@<span>cathode</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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<br /><br />welcome to my profile :333<br /><br /><a href=\"https://cathode.neocities.org/\">https://cathode.neocities.org/</a>", + "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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"58\" href=\"https://pleroma.soykaf.com/users/lain\">@<span>lain</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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.<br />let's be friends <br />ぷれろまの生徒会長。謎の外人。日本語OK. <br />公主病.<br />I invented the internet.<br />xmpp: lain@trashserver.net<br />matrix: lambadalambda@matrix.heldscal.la<br />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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"17544\" href=\"https://iscute.moe/users/proxeus\">@<span>proxeus</span></a></span> 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 <span><a data-user=\"1\" href=\"https://shigusegubu.club/users/hj\">@<span>hj</span></a></span>: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": "<span><a data-user=\"60913\" href=\"https://shigusegubu.club/users/cdmnky\">@<span>cdmnky</span></a></span> 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 @@ -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 @@ -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 @@ -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: '<p>haha benis</p>', + 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: '<p>ebin</p>', + 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 <img src="whatever">', + 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 <span><a data-user="1" href="https://shigusegubu.club/users/hj">@<span>hj</span></a></span>: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: + '<span><a data-user="14660" href="https://pleroma.soykaf.com/users/sampo">@<span>sampo</span></a></span> 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 @@ -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 }