forked from AkkomaGang/akkoma-fe
Compare commits
6 commits
0a56cf37a9
...
f5215990bd
Author | SHA1 | Date | |
---|---|---|---|
f5215990bd | |||
4d44030ca7 | |||
|
c047646188 | ||
|
383a8b1907 | ||
|
8c122cfaf7 | ||
|
28d180e944 |
9 changed files with 166 additions and 8 deletions
15
README.md
15
README.md
|
@ -1,3 +1,18 @@
|
|||
A soft fork of Akkoma-FE fixing a few minor but longstanding annoyances.
|
||||
|
||||
Hopefully most of these will be merged upstream when I learn how to do PRs.
|
||||
|
||||
## Comprehensive List of Fixes and Changes
|
||||
|
||||
### Added
|
||||
|
||||
- Approve and deny follow request buttons to the user profile page
|
||||
|
||||
### Fixed
|
||||
|
||||
- Settings backup when the word filters contain non-ASCII characters
|
||||
- The hover color of the checkmark icon on follow request notifications
|
||||
|
||||
# Akkoma-FE
|
||||
|
||||
![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet)
|
||||
|
|
|
@ -194,11 +194,11 @@ const ExtraButtons = {
|
|||
}
|
||||
},
|
||||
showFediLinks () {
|
||||
return this.$store.getters.mergedConfig.showFediLinks === true && this.status.visibility != 'direct'
|
||||
return this.$store.getters.mergedConfig.showFediLinks === true
|
||||
},
|
||||
fediLinkURL () {
|
||||
try {
|
||||
return this.statusLink.replace(/^https?/, 'web+ap')
|
||||
return this.status.external_uri.replace(/^https?/, 'web+ap')
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -65,8 +65,8 @@
|
|||
|
||||
.follow-request-accept {
|
||||
&:hover {
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
color: $fallback--cGreen;
|
||||
color: var(--cGreen, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import RichContent from 'src/components/rich_content/rich_content.jsx'
|
|||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import facActivityPub from '../../assets/icons/activity-pub'
|
||||
import { notificationsFromStore } from 'src/services/notification_utils/notification_utils.js'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -36,7 +37,9 @@ export default {
|
|||
return {
|
||||
followRequestInProgress: false,
|
||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
|
||||
showingConfirmMute: false
|
||||
showingConfirmMute: false,
|
||||
showingApproveConfirmDialog: false,
|
||||
showingDenyConfirmDialog: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
@ -129,6 +132,12 @@ export default {
|
|||
shouldConfirmMute () {
|
||||
return this.mergedConfig.modalOnMute
|
||||
},
|
||||
shouldConfirmApprove () {
|
||||
return this.mergedConfig.modalOnApproveFollow
|
||||
},
|
||||
shouldConfirmDeny () {
|
||||
return this.mergedConfig.modalOnDenyFollow
|
||||
},
|
||||
...mapGetters(['mergedConfig'])
|
||||
},
|
||||
components: {
|
||||
|
@ -205,6 +214,58 @@ export default {
|
|||
},
|
||||
mentionUser () {
|
||||
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
||||
},
|
||||
showApproveConfirmDialog () {
|
||||
this.showingApproveConfirmDialog = true
|
||||
},
|
||||
hideApproveConfirmDialog () {
|
||||
this.showingApproveConfirmDialog = false
|
||||
},
|
||||
showDenyConfirmDialog () {
|
||||
this.showingDenyConfirmDialog = true
|
||||
},
|
||||
hideDenyConfirmDialog () {
|
||||
this.showingDenyConfirmDialog = false
|
||||
},
|
||||
approveUser () {
|
||||
if (this.shouldConfirmApprove) {
|
||||
this.showApproveConfirmDialog()
|
||||
} else {
|
||||
this.doApprove()
|
||||
}
|
||||
},
|
||||
doApprove () {
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
this.$store.dispatch('approveUser', this.user.id)
|
||||
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
|
||||
this.$store.dispatch('updateNotification', {
|
||||
id: notifId,
|
||||
updater: notification => {
|
||||
notification.type = 'follow'
|
||||
}
|
||||
})
|
||||
this.hideApproveConfirmDialog()
|
||||
},
|
||||
denyUser () {
|
||||
if (this.shouldConfirmDeny) {
|
||||
this.showDenyConfirmDialog()
|
||||
} else {
|
||||
this.doDeny()
|
||||
}
|
||||
},
|
||||
doDeny () {
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
this.$store.dispatch('denyUser', this.user.id)
|
||||
.then(() => {
|
||||
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
||||
})
|
||||
this.hideDenyConfirmDialog()
|
||||
},
|
||||
findFollowRequestNotificationId () {
|
||||
const notif = notificationsFromStore(this.$store).find(
|
||||
(notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request'
|
||||
)
|
||||
return notif && notif.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,6 +254,12 @@
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.requested_by {
|
||||
.btn {
|
||||
margin-right: .25em;
|
||||
}
|
||||
}
|
||||
|
||||
.highlighter {
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
|
|
|
@ -68,8 +68,32 @@
|
|||
<div v-if="relationship.followed_by && loggedIn && isOtherUser" class="following">
|
||||
{{ $t('user_card.follows_you') }}
|
||||
</div>
|
||||
<div v-if="relationship.requested_by && loggedIn && isOtherUser" class="requested_by">
|
||||
<div
|
||||
v-if="relationship.requested_by && loggedIn && isOtherUser"
|
||||
class="requested_by"
|
||||
style="white-space: nowrap;"
|
||||
>
|
||||
{{ $t('user_card.requested_by') }}
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('tool_tip.accept_follow_request')"
|
||||
@click="approveUser()"
|
||||
>
|
||||
<FAIcon
|
||||
icon="check"
|
||||
class="fa-scale-110 fa-old-padding follow-request-accept"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('tool_tip.reject_follow_request')"
|
||||
@click="denyUser()"
|
||||
>
|
||||
<FAIcon
|
||||
icon="times"
|
||||
class="fa-scale-110 fa-old-padding follow-request-reject"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="isOtherUser && (loggedIn || !switcher)" class="highlighter">
|
||||
<!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
|
||||
|
@ -161,6 +185,26 @@
|
|||
</template>
|
||||
</i18n-t>
|
||||
</confirm-modal>
|
||||
<confirm-modal
|
||||
v-if="showingApproveConfirmDialog"
|
||||
:title="$t('user_card.approve_confirm_title')"
|
||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
||||
@accepted="doApprove"
|
||||
@cancelled="hideApproveConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
||||
</confirm-modal>
|
||||
<confirm-modal
|
||||
v-if="showingDenyConfirmDialog"
|
||||
:title="$t('user_card.deny_confirm_title')"
|
||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
||||
@accepted="doDeny"
|
||||
@cancelled="hideDenyConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
||||
</confirm-modal>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -54,6 +54,26 @@ const unblockUser = (store, id) => {
|
|||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
}
|
||||
|
||||
const approveUser = (store, id) => {
|
||||
const predictedRelationship = store.state.relationships[id] || { id }
|
||||
predictedRelationship.requested_by = false
|
||||
predictedRelationship.followed_by = true
|
||||
store.commit('updateUserRelationship', [predictedRelationship])
|
||||
|
||||
return store.rootState.api.backendInteractor.approveUser({ id })
|
||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
}
|
||||
|
||||
const denyUser = (store, id) => {
|
||||
const predictedRelationship = store.state.relationships[id] || { id }
|
||||
predictedRelationship.requested_by = false
|
||||
predictedRelationship.followed_by = false
|
||||
store.commit('updateUserRelationship', [predictedRelationship])
|
||||
|
||||
return store.rootState.api.backendInteractor.denyUser({ id })
|
||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
}
|
||||
|
||||
const removeUserFromFollowers = (store, id) => {
|
||||
return store.rootState.api.backendInteractor.removeUserFromFollowers({ id })
|
||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
|
@ -344,6 +364,12 @@ const users = {
|
|||
unblockUser (store, id) {
|
||||
return unblockUser(store, id)
|
||||
},
|
||||
approveUser (store, id) {
|
||||
return approveUser(store, id)
|
||||
},
|
||||
denyUser (store, id) {
|
||||
return denyUser(store, id)
|
||||
},
|
||||
removeUserFromFollowers (store, id) {
|
||||
return removeUserFromFollowers(store, id)
|
||||
},
|
||||
|
|
|
@ -316,6 +316,7 @@ export const parseStatus = (data) => {
|
|||
|
||||
output.summary_raw_html = escape(data.spoiler_text)
|
||||
output.external_url = data.url
|
||||
output.external_uri = data.uri
|
||||
output.poll = data.poll
|
||||
if (output.poll) {
|
||||
output.poll.options = (output.poll.options || []).map(field => ({
|
||||
|
|
|
@ -4,11 +4,14 @@ export const newExporter = ({
|
|||
}) => ({
|
||||
exportData () {
|
||||
const stringified = JSON.stringify(getExportedObject(), null, 2) // Pretty-print and indent with 2 spaces
|
||||
const bytes = new TextEncoder().encode(stringified)
|
||||
const ascii = Array.from(bytes, (x) => String.fromCodePoint(x)).join("")
|
||||
const data = window.btoa(ascii)
|
||||
|
||||
// Create an invisible link with a data url and simulate a click
|
||||
const e = document.createElement('a')
|
||||
e.setAttribute('download', `${filename}.json`)
|
||||
e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified))
|
||||
e.setAttribute('href', 'data:application/json;base64,' + data)
|
||||
e.style.display = 'none'
|
||||
|
||||
document.body.appendChild(e)
|
||||
|
@ -33,7 +36,9 @@ export const newImporter = ({
|
|||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
try {
|
||||
const parsed = JSON.parse(target.result)
|
||||
const bytes = Uint8Array.from(target.result, (x) => x.codePointAt(0))
|
||||
const data = new TextDecoder().decode(bytes)
|
||||
const parsed = JSON.parse(data)
|
||||
const validationResult = validator(parsed)
|
||||
if (validationResult === true) {
|
||||
onImport(parsed)
|
||||
|
|
Loading…
Reference in a new issue