Add hashtag following button #244
6 changed files with 126 additions and 5 deletions
|
@ -6,11 +6,13 @@ import TimelineMenuTabs from '../timeline_menu_tabs/timeline_menu_tabs.vue'
|
||||||
import TimelineQuickSettings from './timeline_quick_settings.vue'
|
import TimelineQuickSettings from './timeline_quick_settings.vue'
|
||||||
import { debounce, throttle, keyBy } from 'lodash'
|
import { debounce, throttle, keyBy } from 'lodash'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch, faCog, faPlus, faMinus } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faCircleNotch,
|
faCircleNotch,
|
||||||
faCog
|
faCog,
|
||||||
|
faPlus,
|
||||||
|
faMinus
|
||||||
)
|
)
|
||||||
|
|
||||||
const Timeline = {
|
const Timeline = {
|
||||||
|
@ -90,6 +92,15 @@ const Timeline = {
|
||||||
},
|
},
|
||||||
showPanelNavShortcuts () {
|
showPanelNavShortcuts () {
|
||||||
return this.$store.getters.mergedConfig.showPanelNavShortcuts
|
return this.$store.getters.mergedConfig.showPanelNavShortcuts
|
||||||
|
},
|
||||||
|
currentUser () {
|
||||||
|
return this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
tagData () {
|
||||||
|
return this.$store.state.tags.tags[this.tag]
|
||||||
|
},
|
||||||
|
tagFollowed () {
|
||||||
|
return this.$store.state.tags.tags[this.tag]?.following
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
@ -118,6 +129,10 @@ const Timeline = {
|
||||||
}
|
}
|
||||||
window.addEventListener('keydown', this.handleShortKey)
|
window.addEventListener('keydown', this.handleShortKey)
|
||||||
setTimeout(this.determineVisibleStatuses, 250)
|
setTimeout(this.determineVisibleStatuses, 250)
|
||||||
|
|
||||||
|
if (this.tag) {
|
||||||
|
this.$store.dispatch('getTag', this.tag)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
unmounted () {
|
unmounted () {
|
||||||
window.removeEventListener('scroll', this.handleScroll)
|
window.removeEventListener('scroll', this.handleScroll)
|
||||||
|
@ -232,6 +247,12 @@ const Timeline = {
|
||||||
}, 200),
|
}, 200),
|
||||||
handleVisibilityChange () {
|
handleVisibilityChange () {
|
||||||
this.unfocused = document.hidden
|
this.unfocused = document.hidden
|
||||||
|
},
|
||||||
|
followTag (tag) {
|
||||||
|
return this.$store.dispatch('followTag', tag)
|
||||||
|
},
|
||||||
|
unfollowTag (tag) {
|
||||||
|
return this.$store.dispatch('unfollowTag', tag)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -21,6 +21,36 @@
|
||||||
{{ $t('timeline.up_to_date') }}
|
{{ $t('timeline.up_to_date') }}
|
||||||
</div>
|
</div>
|
||||||
<TimelineQuickSettings v-if="!embedded" />
|
<TimelineQuickSettings v-if="!embedded" />
|
||||||
|
<div
|
||||||
|
v-if="currentUser && tag !== undefined && tagData && !tagFollowed"
|
||||||
|
class="followTag"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button-default"
|
||||||
|
:title="$t('timeline.follow_tag')"
|
||||||
|
@click="followTag(tag)"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
size="sm"
|
||||||
|
icon="plus"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="currentUser && tag !== undefined && tagData && tagFollowed"
|
||||||
|
class="followTag"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button-default"
|
||||||
|
:title="$t('timeline.unfollow_tag')"
|
||||||
|
@click="unfollowTag(tag)"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
size="sm"
|
||||||
|
icon="minus"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="classes.body">
|
<div :class="classes.body">
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1055,7 +1055,9 @@
|
||||||
"show_new": "Show new",
|
"show_new": "Show new",
|
||||||
"socket_broke": "Realtime connection lost: CloseEvent code {0}",
|
"socket_broke": "Realtime connection lost: CloseEvent code {0}",
|
||||||
"socket_reconnected": "Realtime connection established",
|
"socket_reconnected": "Realtime connection established",
|
||||||
"up_to_date": "Up-to-date"
|
"up_to_date": "Up-to-date",
|
||||||
|
"follow_tag": "Follow hashtag",
|
||||||
|
"unfollow_tag": "Unfollow hashtag"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"no_translation_target_set": "No translation target language set - this may fail. Please set a target language in your settings."
|
"no_translation_target_set": "No translation target language set - this may fail. Please set a target language in your settings."
|
||||||
|
|
|
@ -21,6 +21,7 @@ import postStatusModule from './modules/postStatus.js'
|
||||||
import announcementsModule from './modules/announcements.js'
|
import announcementsModule from './modules/announcements.js'
|
||||||
import editStatusModule from './modules/editStatus.js'
|
import editStatusModule from './modules/editStatus.js'
|
||||||
import statusHistoryModule from './modules/statusHistory.js'
|
import statusHistoryModule from './modules/statusHistory.js'
|
||||||
|
import tagModule from './modules/tags.js'
|
||||||
|
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
@ -96,7 +97,8 @@ const persistedStateOptions = {
|
||||||
postStatus: postStatusModule,
|
postStatus: postStatusModule,
|
||||||
announcements: announcementsModule,
|
announcements: announcementsModule,
|
||||||
editStatus: editStatusModule,
|
editStatus: editStatusModule,
|
||||||
statusHistory: statusHistoryModule
|
statusHistory: statusHistoryModule,
|
||||||
|
tags: tagModule
|
||||||
},
|
},
|
||||||
plugins,
|
plugins,
|
||||||
strict: false // Socket modifies itself, let's ignore this for now.
|
strict: false // Socket modifies itself, let's ignore this for now.
|
||||||
|
|
37
src/modules/tags.js
Normal file
37
src/modules/tags.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { merge } from 'lodash'
|
||||||
|
|
||||||
|
const tags = {
|
||||||
|
state: {
|
||||||
|
// Contains key = id, value = number of trackers for this poll
|
||||||
|
tags: {}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setTag (state, { name, data }) {
|
||||||
|
state.tags[name] = data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
getTag ({ rootState, commit }, tagName) {
|
||||||
|
return rootState.api.backendInteractor.getHashtag({ tag: tagName }).then(tag => {
|
||||||
|
commit('setTag', { name: tagName, data: tag })
|
||||||
|
return tag
|
||||||
|
})
|
||||||
|
},
|
||||||
|
followTag (store, tagName) {
|
||||||
|
return store.rootState.api.backendInteractor.followHashtag({ tag: tagName })
|
||||||
|
.then((resp) => {
|
||||||
|
store.commit('setTag', { name: tagName, data: resp })
|
||||||
|
return resp
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unfollowTag ({ rootState, commit }, tag) {
|
||||||
|
return rootState.api.backendInteractor.unfollowHashtag({ tag })
|
||||||
|
.then((resp) => {
|
||||||
|
commit('setTag', { name: tag, data: resp })
|
||||||
|
return resp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default tags
|
|
@ -108,6 +108,9 @@ const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements
|
||||||
const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
||||||
const AKKOMA_SETTING_PROFILE_URL = (name) => `/api/v1/akkoma/frontend_settings/pleroma-fe/${name}`
|
const AKKOMA_SETTING_PROFILE_URL = (name) => `/api/v1/akkoma/frontend_settings/pleroma-fe/${name}`
|
||||||
const AKKOMA_SETTING_PROFILE_LIST = `/api/v1/akkoma/frontend_settings/pleroma-fe`
|
const AKKOMA_SETTING_PROFILE_LIST = `/api/v1/akkoma/frontend_settings/pleroma-fe`
|
||||||
|
const MASTODON_TAG_URL = (name) => `/api/v1/tags/${name}`
|
||||||
|
const MASTODON_FOLLOW_TAG_URL = (name) => `/api/v1/tags/${name}/follow`
|
||||||
|
const MASTODON_UNFOLLOW_TAG_URL = (name) => `/api/v1/tags/${name}/unfollow`
|
||||||
|
|
||||||
const oldfetch = window.fetch
|
const oldfetch = window.fetch
|
||||||
|
|
||||||
|
@ -1549,6 +1552,29 @@ const listSettingsProfiles = ({ credentials }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getHashtag = ({ tag, credentials }) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_TAG_URL(tag),
|
||||||
|
credentials
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const followHashtag = ({ tag, credentials }) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_FOLLOW_TAG_URL(tag),
|
||||||
|
method: 'POST',
|
||||||
|
credentials
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const unfollowHashtag = ({ tag, credentials }) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_UNFOLLOW_TAG_URL(tag),
|
||||||
|
method: 'POST',
|
||||||
|
credentials
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => {
|
export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => {
|
||||||
return Object.entries({
|
return Object.entries({
|
||||||
...(credentials
|
...(credentials
|
||||||
|
@ -1784,7 +1810,10 @@ const apiService = {
|
||||||
getReports,
|
getReports,
|
||||||
updateReportStates,
|
updateReportStates,
|
||||||
addNoteToReport,
|
addNoteToReport,
|
||||||
deleteNoteFromReport
|
deleteNoteFromReport,
|
||||||
|
getHashtag,
|
||||||
|
followHashtag,
|
||||||
|
unfollowHashtag
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiService
|
export default apiService
|
||||||
|
|
Loading…
Reference in a new issue