Compare commits

..

10 commits

Author SHA1 Message Date
Essem da8063b54b
Hack to fix streaming notification order 2024-02-11 23:18:23 -06:00
Essem 672f50eec1
screw it, just hardcode the target 2024-02-11 22:17:58 -06:00
Essem 0aa5ac09a2
Make notifications work
Unsure if marking single notifications as seen is possible with masto
2024-02-10 16:55:05 -06:00
Essem 430a9f789a
Link to Mastodon admin dashboard instead of AdminFE 2024-01-21 00:00:41 -06:00
Essem 17fd7e288b
Remove post previews
The way they work is incompatible with the Mastodon API.
2024-01-21 00:00:21 -06:00
Essem 1605035bb3
Attempt to block settings sync 2024-01-06 21:36:10 -06:00
Essem 35c4cad520
Use streaming API by default 2024-01-06 21:07:26 -06:00
Essem a3ae2aad5b
Fix emoji reactions 2024-01-06 21:07:15 -06:00
Essem 92eb414907
Convert conversation_id to statusnet_conversation_id 2024-01-06 19:51:40 -06:00
Essem dcda90373f
Make it work with wdw 2024-01-06 17:03:12 -06:00
37 changed files with 173 additions and 610 deletions

View file

@ -1 +0,0 @@
nodejs 20.12.2

View file

@ -1,12 +1,10 @@
labels:
platform: linux/amd64
steps:
platform: linux/amd64
pipeline:
lint:
when:
event:
- pull_request
image: node:20
image: node:18
commands:
- yarn
- yarn lint
@ -16,11 +14,11 @@ steps:
when:
event:
- pull_request
image: node:20
image: node:18
commands:
- apt update
- apt install firefox-esr -y --no-install-recommends
- yarn
- yarn
- yarn unit
build:
@ -30,7 +28,7 @@ steps:
branch:
- develop
- stable
image: node:20
image: node:18
commands:
- yarn
- yarn build
@ -42,15 +40,15 @@ steps:
branch:
- develop
- stable
image: node:20
image: node:18
secrets:
- SCW_ACCESS_KEY
- SCW_SECRET_KEY
- SCW_DEFAULT_ORGANIZATION_ID
commands:
- apt-get update && apt-get install -y rclone wget zip
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.30.0/scaleway-cli_2.30.0_linux_amd64
- mv scaleway-cli_2.30.0_linux_amd64 scaleway-cli
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
- chmod +x scaleway-cli
- ./scaleway-cli object config install type=rclone
- zip akkoma-fe.zip -r dist
@ -72,8 +70,8 @@ steps:
- SCW_DEFAULT_ORGANIZATION_ID
commands:
- apt-get update && apt-get install -y rclone wget git zip
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.30.0/scaleway-cli_2.30.0_linux_amd64
- mv scaleway-cli_2.30.0_linux_amd64 scaleway-cli
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
- chmod +x scaleway-cli
- ./scaleway-cli object config install type=rclone
- cd docs
@ -81,4 +79,4 @@ steps:
- mkdocs build
- zip -r docs.zip site/*
- cd site
- rclone copy . scaleway:akkoma-docs/frontend/$CI_COMMIT_BRANCH/
- rclone copy . scaleway:akkoma-docs/frontend/$CI_COMMIT_BRANCH/

View file

@ -64,11 +64,6 @@ export default {
'-' + this.layoutType
]
},
pageBackground () {
return this.mergedConfig.displayPageBackgrounds
? this.$store.state.users.displayBackground
: null
},
currentUser () { return this.$store.state.users.currentUser },
userBackground () { return this.currentUser.background_image },
instanceBackground () {
@ -76,7 +71,7 @@ export default {
? null
: this.$store.state.instance.background
},
background () { return this.pageBackground || this.userBackground || this.instanceBackground },
background () { return this.userBackground || this.instanceBackground },
bgStyle () {
if (this.background) {
return {

View file

@ -67,14 +67,26 @@ const resolveLanguage = (instanceLanguages) => {
const getInstanceConfig = async ({ store }) => {
try {
const res = await preloadFetch('/api/v1/instance')
const res = await preloadFetch('/api/v2/instance')
if (res.ok) {
const data = await res.json()
const textlimit = data.max_toot_chars
const vapidPublicKey = data.pleroma.vapid_public_key
const textlimit = data.configuration.statuses.max_characters
const vapidPublicKey = data.configuration.vapid.public_key
store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit })
store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required })
const uploadLimits = {
general: data.configuration.media_attachments.video_size_limit,
avatar: "2097152",
background: "2097152",
banner: "2097152"
}
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })
store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadLimits.avatar) })
store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadLimits.background) })
store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadLimits.banner) })
store.dispatch('setInstanceOption', { name: 'postFormats', value: data.configuration.statuses.supported_mime_types })
store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.registrations.approval_required })
// don't override cookie if set
if (!Cookies.get('userLanguage')) {
store.dispatch('setOption', { name: 'interfaceLanguage', value: resolveLanguage(data.languages) })
@ -83,6 +95,8 @@ const getInstanceConfig = async ({ store }) => {
if (vapidPublicKey) {
store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })
}
resolveStaffAccounts({ store, accounts: [data.contact.account.id] })
} else {
throw (res)
}
@ -269,42 +283,33 @@ const getNodeInfo = async ({ store }) => {
if (res.ok) {
const data = await res.json()
const metadata = data.metadata
const features = metadata.features
store.dispatch('setInstanceOption', { name: 'name', value: metadata.nodeName })
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') })
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') })
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: false }) // registration should be done through the default interface
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: false })
store.dispatch('setInstanceOption', { name: 'safeDM', value: false })
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: true })
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: true })
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
store.dispatch('setInstanceOption', { name: 'translationEnabled', value: features.includes('akkoma:machine_translation') })
store.dispatch('setInstanceOption', { name: 'translationEnabled', value: false }) // idk
const uploadLimits = metadata.uploadLimits
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })
store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadLimits.avatar) })
store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadLimits.background) })
store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadLimits.banner) })
store.dispatch('setInstanceOption', { name: 'fieldsLimits', value: metadata.fieldsLimits })
store.dispatch('setInstanceOption', { name: 'fieldsLimits', value: 6 }) // todo: expose this on the backend
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: [] })
const suggestions = metadata.suggestions
store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })
store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: true })
store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: true })
const software = data.software
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
const priv = metadata.private
store.dispatch('setInstanceOption', { name: 'private', value: priv })
store.dispatch('setInstanceOption', { name: 'private', value: false })
const frontendVersion = window.___pleromafe_commit_hash
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
const federation = metadata.federation
const federation = {}
store.dispatch('setInstanceOption', {
name: 'tagPolicyAvailable',
@ -314,7 +319,7 @@ const getNodeInfo = async ({ store }) => {
})
store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation })
store.dispatch('setInstanceOption', { name: 'localBubbleInstances', value: metadata.localBubbleInstances })
store.dispatch('setInstanceOption', { name: 'localBubbleInstances', value: [] })
store.dispatch('setInstanceOption', {
name: 'federating',
value: typeof federation.enabled === 'undefined'
@ -322,14 +327,11 @@ const getNodeInfo = async ({ store }) => {
: federation.enabled
})
store.dispatch('setInstanceOption', { name: 'publicTimelineVisibility', value: metadata.publicTimelineVisibility })
store.dispatch('setInstanceOption', { name: 'federatedTimelineAvailable', value: metadata.federatedTimelineAvailable })
store.dispatch('setInstanceOption', { name: 'publicTimelineVisibility', value: { bubble: false, local: true, federated: true } })
store.dispatch('setInstanceOption', { name: 'federatedTimelineAvailable', value: true })
const accountActivationRequired = metadata.accountActivationRequired
store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired })
const accounts = metadata.staffAccounts
resolveStaffAccounts({ store, accounts })
store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: true })
} else {
throw (res)
}
@ -367,7 +369,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
FaviconService.initFaviconService()
const overrides = window.___pleromafe_dev_overrides || {}
const overrides = window.___pleromafe_dev_overrides || { target: 'https://wetdry.world/' }
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
store.dispatch('setInstanceOption', { name: 'server', value: server })

View file

@ -44,20 +44,6 @@
>
{{ $t('announcements.mark_as_read_action') }}
</button>
<button
v-if="currentUser && currentUser.role === 'admin'"
class="btn button-default"
@click="enterEditMode"
>
{{ $t('announcements.edit_action') }}
</button>
<button
v-if="currentUser && currentUser.role === 'admin'"
class="btn button-default"
@click="deleteAnnouncement"
>
{{ $t('announcements.delete_action') }}
</button>
</div>
<div
v-else

View file

@ -6,46 +6,6 @@
</div>
</div>
<div class="panel-body">
<section
v-if="currentUser && currentUser.role === 'admin'"
>
<div class="post-form">
<div class="heading">
<h4>{{ $t('announcements.post_form_header') }}</h4>
</div>
<div class="body">
<announcement-editor
:announcement="newAnnouncement"
:disabled="posting"
/>
</div>
<div class="footer">
<button
class="btn button-default post-button"
:disabled="posting"
@click.prevent="postAnnouncement"
>
{{ $t('announcements.post_action') }}
</button>
<div
v-if="error"
class="alert error"
>
{{ $t('announcements.post_error', { error }) }}
<button
class="button-unstyled"
@click="clearError"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
:title="$t('announcements.close_error')"
/>
</button>
</div>
</div>
</div>
</section>
<section
v-for="announcement in announcements"
:key="announcement.id"

View file

@ -167,7 +167,7 @@
</button>
<a
v-if="currentUser && currentUser.role === 'admin'"
href="/pleroma/admin/#/login-pleroma"
:href="'https://' + $store.getters.instanceDomain + '/admin/dashboard'"
class="nav-icon"
target="_blank"
@click.stop

View file

@ -46,7 +46,7 @@ const FollowRequestCard = {
this.$store.dispatch('decrementFollowRequestsCount')
const notifId = this.findFollowRequestNotificationId()
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
//this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
this.$store.dispatch('updateNotification', {
id: notifId,
updater: notification => {

View file

@ -6,7 +6,6 @@ import UserCard from '../user_card/user_card.vue'
import Timeago from '../timeago/timeago.vue'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import StillImage from '../still-image/still-image.vue'
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -51,8 +50,7 @@ const Notification = {
Timeago,
Status,
RichContent,
ConfirmModal,
StillImage
ConfirmModal
},
methods: {
toggleUserExpanded () {
@ -89,7 +87,7 @@ const Notification = {
doApprove () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
//this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
this.$store.dispatch('updateNotification', {
id: this.notification.id,
updater: notification => {

View file

@ -116,13 +116,12 @@
scope="global"
keypath="notifications.reacted_with"
>
<still-image
<img
v-if="notification.emoji_url !== null"
class="notification-reaction-emoji"
:src="notification.emoji_url"
:title="notification.emoji"
:alt="notification.emoji"
/>
:name="notification.emoji"
>
<span
v-else
class="emoji-reaction-emoji"

View file

@ -9,12 +9,11 @@ import StatusContent from '../status_content/status_content.vue'
import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
import { reject, map, uniqBy, debounce } from 'lodash'
import { usePostLanguageOptions } from 'src/lib/post_language'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue'
import iso6391 from 'iso-639-1'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -63,13 +62,6 @@ const deleteDraft = (draftKey) => {
localStorage.setItem('drafts', JSON.stringify(draftData));
}
const interfaceToISOLanguage = (ilang) => {
const sep = ilang.indexOf("_");
return sep < 0 ?
ilang :
ilang.substr(0, sep);
}
const PostStatusForm = {
props: [
'statusId',
@ -137,13 +129,6 @@ const PostStatusForm = {
this.$refs.textarea.focus()
}
},
setup() {
const {postLanguageOptions} = usePostLanguageOptions()
return {
postLanguageOptions,
}
},
data () {
const preset = this.$route.query.message
let statusText = preset || ''
@ -153,8 +138,7 @@ const PostStatusForm = {
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
}
const { postContentType: contentType, postLanguage: defaultPostLanguage, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage, alwaysShowSubjectInput } = this.$store.getters.mergedConfig
const postLanguage = defaultPostLanguage || interfaceToISOLanguage(interfaceLanguage)
const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage, alwaysShowSubjectInput } = this.$store.getters.mergedConfig
let statusParams = {
spoilerText: this.subject || '',
@ -165,7 +149,7 @@ const PostStatusForm = {
poll: {},
mediaDescriptions: {},
visibility: this.suggestedVisibility(),
language: postLanguage,
language: interfaceLanguage,
contentType
}
@ -180,7 +164,7 @@ const PostStatusForm = {
poll: this.statusPoll || {},
mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || this.suggestedVisibility(),
language: this.statusLanguage || postLanguage,
language: this.statusLanguage || interfaceLanguage,
contentType: statusContentType
}
}
@ -325,6 +309,9 @@ const PostStatusForm = {
...mapState({
mobileLayout: state => state.interface.mobileLayout
}),
isoLanguages () {
return iso6391.getAllCodes();
}
},
watch: {
'newStatus': {

View file

@ -73,50 +73,6 @@
<p>{{ $t('post_status.edit_remote_warning') }}</p>
<p>{{ $t('post_status.edit_unsupported_warning') }}</p>
</div>
<div
v-if="!disablePreview"
class="preview-heading faint"
>
<a
class="preview-toggle faint"
@click.stop.prevent="togglePreview"
>
{{ $t('post_status.preview') }}
<FAIcon :icon="showPreview ? 'chevron-left' : 'chevron-right'" />
</a>
<div
v-show="previewLoading"
class="preview-spinner"
>
<FAIcon
class="fa-old-padding"
spin
icon="circle-notch"
/>
</div>
</div>
<div
v-if="showPreview"
class="preview-container"
>
<div
v-if="!preview"
class="preview-status"
>
{{ $t('general.loading') }}
</div>
<div
v-else-if="preview.error"
class="preview-status preview-error"
>
{{ preview.error }}
</div>
<StatusContent
v-else
:status="preview"
class="preview-status"
/>
</div>
<EmojiInput
ref="subject-emoji-input"
v-if="subjectVisible"
@ -208,11 +164,11 @@
class="form-control"
>
<option
v-for="language in postLanguageOptions"
:key="language.key"
:value="language.value"
v-for="language in isoLanguages"
:key="language"
:value="language"
>
{{ language.label }}
{{ language }}
</option>
</Select>
</div>

View file

@ -4,7 +4,6 @@ import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import { usePostLanguageOptions } from 'src/lib/post_language'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import ServerSideIndicator from '../helpers/server_side_indicator.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
@ -18,11 +17,6 @@ library.add(
)
const GeneralTab = {
setup() {
const {postLanguageOptions} = usePostLanguageOptions()
return {postLanguageOptions}
},
data () {
return {
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
@ -104,7 +98,6 @@ const GeneralTab = {
get: function () { return this.$store.getters.mergedConfig.profile },
set: function (val) {
this.$store.dispatch('setOption', { name: 'profile', value: val })
this.$store.dispatch('getSettingsProfile')
}
},
settingsVersion () {
@ -124,12 +117,6 @@ const GeneralTab = {
this.$store.dispatch('setOption', { name: 'translationLanguage', value: val })
}
},
postLanguage: {
get: function () { return this.$store.getters.mergedConfig.postLanguage },
set: function (val) {
this.$store.dispatch('setOption', { name: 'postLanguage', value: val })
}
},
...SharedComputedObject()
},
methods: {
@ -144,12 +131,10 @@ const GeneralTab = {
},
loadSettingsProfile (name) {
this.$store.commit('setOption', { name: 'profile', value: name })
this.$store.dispatch('getSettingsProfile', true)
},
createSettingsProfile () {
this.$store.dispatch('setOption', { name: 'profile', value: this.newProfileName })
this.$store.dispatch('setOption', { name: 'profileVersion', value: 1 })
this.$store.dispatch('syncSettings')
this.newProfileName = ''
},
forceSync () {

View file

@ -146,11 +146,6 @@
{{ $t('settings.show_wider_shortcuts') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="displayPageBackgrounds">
{{ $t('settings.show_page_backgrounds') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="stopGifs">
{{ $t('settings.stop_gifs') }}
@ -585,15 +580,6 @@
{{ $t('settings.post_status_content_type') }}
</ChoiceSetting>
</li>
<li>
<ChoiceSetting
id="postLanguage"
path="postLanguage"
:options="postLanguageOptions"
>
{{ $t('settings.post_language') }}
</ChoiceSetting>
</li>
<li>
<BooleanSetting
path="alwaysShowNewPostButton"

View file

@ -33,7 +33,6 @@ const ProfileTab = {
newName: this.$store.state.users.currentUser.name_unescaped,
newBio: unescape(this.$store.state.users.currentUser.description),
newLocked: this.$store.state.users.currentUser.locked,
newPermitFollowback: this.$store.state.users.currentUser.permit_followback,
newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })),
showRole: this.$store.state.users.currentUser.show_role,
role: this.$store.state.users.currentUser.role,
@ -136,7 +135,6 @@ const ProfileTab = {
bot: this.bot,
show_role: this.showRole,
status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1,
permit_followback: this.permit_followback,
accepts_direct_messages_from: this.userAcceptsDirectMessagesFrom
/* eslint-enable camelcase */
}

View file

@ -259,19 +259,6 @@
<BooleanSetting path="serverSide_locked">
{{ $t('settings.lock_account_description') }}
</BooleanSetting>
<ul
class="setting-list suboptions"
:class="[{disabled: !serverSide_locked}]"
>
<li>
<BooleanSetting
path="serverSide_permitFollowback"
:disabled="!serverSide_locked"
>
{{ $t('settings.permit_followback_description') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<BooleanSetting path="serverSide_discoverable">

View file

@ -163,7 +163,7 @@
@click="toggleDrawer"
>
<a
href="/pleroma/admin/#/login-pleroma"
:href="'https://' + $store.getters.instanceDomain + '/admin/dashboard'"
target="_blank"
>
<FAIcon

View file

@ -145,12 +145,10 @@ const UserProfile = {
if (user) {
loadById(user.id)
this.note = user.relationship.note
this.$store.dispatch('setDisplayBackground', user.background_image)
} else {
this.$store.dispatch('fetchUser', userNameOrId)
.then(({ id, relationship, background_image }) => {
.then(({ id, relationship }) => {
this.note = relationship.note
this.$store.dispatch('setDisplayBackground', background_image)
return loadById(id)
})
.catch((reason) => {
@ -227,9 +225,6 @@ const UserProfile = {
Conversation,
RichContent,
FollowedTagList
},
beforeRouteLeave(to, from) {
this.$store.dispatch('setDisplayBackground', null)
}
}

View file

@ -16,7 +16,7 @@ const WhoToFollow = {
methods: {
showWhoToFollow (reply) {
reply.forEach((i, index) => {
this.$store.state.api.backendInteractor.fetchUser({ id: i.acct })
this.$store.state.api.backendInteractor.fetchUser({ id: i.id })
.then((externalUser) => {
if (!externalUser.error) {
this.$store.commit('addNewUsers', [externalUser])

View file

@ -9,11 +9,12 @@ function showWhoToFollow (panel, reply) {
let user = shuffled[index]
let img = user.avatar || this.$store.state.instance.defaultAvatar
let name = user.acct
let id = user.id
toFollow.img = img
toFollow.name = name
panel.$store.state.api.backendInteractor.fetchUser({ id: name })
panel.$store.state.api.backendInteractor.fetchUser({ id })
.then((externalUser) => {
if (!externalUser.error) {
panel.$store.commit('addNewUsers', [externalUser])

View file

@ -601,7 +601,6 @@
"list_aliases_error": "Error fetching aliases: {error}",
"list_backups_error": "Error fetching backup list: {error}",
"lock_account_description": "Restrict your account to approved followers only",
"permit_followback_description": "Automatically approve requests from already followed users",
"loop_video": "Loop videos",
"loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")",
"mascot": "Mastodon FE Mascot",
@ -684,7 +683,6 @@
"play_videos_in_modal": "Play videos in a popup frame",
"post_look_feel": "Posts Look & Feel",
"post_status_content_type": "Default post content type",
"post_language": "Default post language",
"posts": "Posts",
"preload_images": "Preload images",
"presets": "Presets",
@ -752,7 +750,6 @@
"show_nav_shortcuts": "Show extra navigation shortcuts in top panel",
"show_panel_nav_shortcuts": "Show timeline navigation shortcuts at the top of the panel",
"show_scrollbars": "Show side column's scrollbars",
"show_page_backgrounds": "Show page-specific backgrounds, e.g. for user profiles",
"show_wider_shortcuts": "Show wider gap between top panel shortcuts",
"show_yous": "Show (You)s",
"stop_gifs": "Pause animated images until you hover on them",

View file

@ -21,7 +21,6 @@ const loaders = {
ga: () => import('./ga.json'),
he: () => import('./he.json'),
hu: () => import('./hu.json'),
id: () => import('./id.json'),
it: () => import('./it.json'),
ja: () => import('./ja_pedantic.json'),
ja_easy: () => import('./ja_easy.json'),

View file

@ -1,16 +0,0 @@
import iso6391 from 'iso-639-1'
import { computed } from 'vue'
export const usePostLanguageOptions = () => {
const postLanguageOptions = computed(() => {
return iso6391.getAllCodes().map(lang => ({
key: lang,
value: lang,
label: lang,
}));
})
return {
postLanguageOptions,
}
}

View file

@ -48,7 +48,6 @@ const announcements = {
return store.rootState.api.backendInteractor.fetchAnnouncements()
}
const all = await store.rootState.api.backendInteractor.adminFetchAnnouncements()
const visible = await store.rootState.api.backendInteractor.fetchAnnouncements()
const visibleObject = visible.reduce((a, c) => {
a[c.id] = c
@ -56,7 +55,7 @@ const announcements = {
}, {})
const getWithinVisible = announcement => visibleObject[announcement.id]
all.forEach(announcement => {
visible.forEach(announcement => {
const visibleAnnouncement = getWithinVisible(announcement)
if (!visibleAnnouncement) {
announcement.inactive = true
@ -65,7 +64,7 @@ const announcements = {
}
})
return all
return visible
}
return getAnnouncements()

View file

@ -55,7 +55,6 @@ export const defaultState = {
alwaysShowNewPostButton: false,
autohideFloatingPostButton: false,
pauseOnUnfocused: true,
displayPageBackgrounds: true,
stopGifs: undefined,
replyVisibility: 'all',
thirdColumnMode: 'notifications',
@ -75,7 +74,7 @@ export const defaultState = {
highlight: {},
interfaceLanguage: browserLocale,
hideScopeNotice: false,
useStreamingApi: false,
useStreamingApi: true,
sidebarRight: undefined, // instance default
subjectLineBehavior: undefined, // instance default
alwaysShowSubjectInput: undefined, // instance default
@ -116,7 +115,6 @@ export const defaultState = {
conversationTreeFadeAncestors: undefined, // instance default
maxDepthInThread: undefined, // instance default
translationLanguage: undefined, // instance default,
postLanguage: undefined, // instance default,
supportedTranslationLanguages: {}, // instance default
userProfileDefaultTab: 'statuses',
useBlurhash: true,
@ -217,9 +215,6 @@ const config = {
},
setOption ({ commit, dispatch }, { name, value, manual }) {
commit('setOption', { name, value })
if (manual === true) {
dispatch('syncSettings')
}
switch (name) {
case 'theme':
setPreset(value)

View file

@ -35,7 +35,7 @@ const defaultState = {
hideWordFilteredPosts: false,
hidePostStats: false,
hideBotIndication: false,
hideSiteFavicon: false,
hideSiteFavicon: true,
hideSiteName: false,
hideUserStats: false,
muteBotStatuses: false,
@ -177,22 +177,19 @@ const instance = {
async getCustomEmoji ({ commit, state }) {
try {
const res = await window.fetch('/api/v1/pleroma/emoji')
const res = await window.fetch('/api/v1/custom_emojis')
if (res.ok) {
const result = await res.json()
const values = Array.isArray(result) ? Object.assign({}, ...result) : result
const emoji = Object.entries(values).map(([key, value]) => {
const imageUrl = value.image_url
return {
displayText: key,
imageUrl: imageUrl ? state.server + imageUrl : value,
tags: imageUrl ? value.tags.sort((a, b) => a > b ? 1 : 0) : ['utf'],
replacement: `:${key}: `
}
// Technically could use tags but those are kinda useless right now,
// should have been "pack" field, that would be more useful
}).sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : -1)
commit('setInstanceOption', { name: 'customEmoji', value: emoji })
const emoji = []
for (const emojiobj of result) {
emoji.push({
displayText: emojiobj.shortcode,
imageUrl: emojiobj.url,
tags: emojiobj.category ? [`pack:${emojiobj.category.toLowerCase()}`] : ['pack:custom'],
replacement: `:${emojiobj.shortcode}: `
})
}
commit('setInstanceOption', { name: 'customEmoji', value: emoji.sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : -1) })
} else {
throw (res)
}

View file

@ -47,10 +47,6 @@ export const settingsMap = {
},
// Privacy
'locked': 'locked',
'permitFollowback': {
get: 'akkoma.permit_followback',
set: 'permit_followback'
},
'allowFollowingMove': {
get: 'pleroma.allow_following_move',
set: 'allow_following_move'

View file

@ -314,7 +314,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
})
// Keep the visible statuses sorted
if (timeline && !(['bookmarks', 'favorites'].includes(timeline))) {
if (timeline && !(timeline === 'bookmarks')) {
sortTimeline(timelineObject)
}
}
@ -349,6 +349,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
// Only add a new notification if we don't have one for the same action
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
updateNotificationsMinMaxId(state, notification)
notification.seen = false
state.notifications.data.push(notification)
state.notifications.idStore[notification.id] = notification
@ -544,7 +545,7 @@ export const mutations = {
count: reaction.count + 1,
me: true,
accounts: [
...reaction.accounts,
//...reaction.accounts,
currentUser
]
}
@ -704,11 +705,11 @@ const statuses = {
},
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
commit('markSingleNotificationAsSeen', { id })
apiService.markNotificationsAsSeen({
/*apiService.markNotificationsAsSeen({
single: true,
id,
credentials: rootState.users.currentUser.credentials
})
})*/
},
dismissNotificationLocal ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
@ -734,7 +735,7 @@ const statuses = {
if (!currentUser) return
commit('addOwnReaction', { id, emoji, currentUser })
rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then(
rootState.api.backendInteractor.reactWithEmoji({ id, emoji: emoji.replaceAll(':', '') }).then(
ok => {
dispatch('fetchEmojiReactionsBy', id)
}
@ -745,16 +746,16 @@ const statuses = {
if (!currentUser) return
commit('removeOwnReaction', { id, emoji, currentUser })
rootState.api.backendInteractor.unreactWithEmoji({ id, emoji }).then(
rootState.api.backendInteractor.unreactWithEmoji({ id, emoji: emoji.replaceAll(':', '') }).then(
ok => {
dispatch('fetchEmojiReactionsBy', id)
}
)
},
fetchEmojiReactionsBy ({ rootState, commit }, id) {
rootState.api.backendInteractor.fetchEmojiReactions({ id }).then(
emojiReactions => {
commit('addEmojiReactionsBy', { id, emojiReactions, currentUser: rootState.users.currentUser })
rootState.api.backendInteractor.fetchStatus({ id }).then(
status => {
commit('addEmojiReactionsBy', { id, emojiReactions: status.emoji_reactions, currentUser: rootState.users.currentUser })
}
)
},

View file

@ -135,10 +135,6 @@ export const mutations = {
const user = state.usersObject[id]
user['deactivated'] = deactivated
},
setDisplayBackground(state, url) {
console.log("Commiting user profile bg mutation")
state.displayBackground = url
},
setCurrentUser (state, user) {
state.lastLoginName = user.screen_name
state.currentUser = mergeWith(state.currentUser || {}, user, mergeArrayLength)
@ -311,7 +307,6 @@ export const defaultState = {
currentUser: false,
users: [],
usersObject: {},
displayBackground: null,
signUpPending: false,
signUpErrors: [],
relationships: {},
@ -324,10 +319,6 @@ const users = {
mutations,
getters,
actions: {
setDisplayBackground (store, url) {
console.log("Performing user profile bg action...")
store.commit('setDisplayBackground', url)
},
fetchUserIfMissing (store, id) {
if (!store.getters.findUser(id)) {
store.dispatch('fetchUser', id)
@ -679,9 +670,6 @@ const users = {
store.dispatch('setLayoutWidth', windowWidth())
store.dispatch('setLayoutHeight', windowHeight())
store.dispatch('getSupportedTranslationlanguages')
store.dispatch('getSettingsProfile')
store.dispatch('listSettingsProfiles')
store.dispatch('startFetchingConfig')
store.dispatch('startFetchingAnnouncements')
if (user.role === 'admin' || user.role === 'moderator') {
store.dispatch('startFetchingReports')

View file

@ -20,7 +20,7 @@ const ADMIN_USERS_URL = '/api/v1/pleroma/admin/users'
const SUGGESTIONS_URL = '/api/v1/suggestions'
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
const ADMIN_REPORTS_URL = '/api/v1/pleroma/admin/reports'
const ADMIN_REPORTS_URL = '/api/v1/admin/reports'
const ADMIN_REPORT_NOTES_URL = id => `/api/v1/pleroma/admin/reports/${id}/notes`
const ADMIN_REPORT_NOTE_URL = (report, note) => `/api/v1/pleroma/admin/reports/${report}/notes/${note}`
@ -99,9 +99,9 @@ const MASTODON_STREAMING = '/api/v1/streaming'
const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers'
const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements'
const MASTODON_ANNOUNCEMENTS_DISMISS_URL = id => `/api/v1/announcements/${id}/dismiss`
const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions`
const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/statuses/${id}/reactions`
const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/statuses/${id}/react/${emoji}`
const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/statuses/${id}/unreact/${emoji}`
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
const PLEROMA_ANNOUNCEMENTS_URL = '/api/v1/pleroma/admin/announcements'
const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements'
@ -113,6 +113,7 @@ 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 MASTODON_FOLLOWED_TAGS_URL = '/api/v1/followed_tags'
const MASTODON_MARKERS_URL = '/api/v1/markers'
const oldfetch = window.fetch
@ -647,7 +648,7 @@ const getReports = ({ state, limit, page, pageSize, credentials }) => {
url = url + (args ? '?' + args : '')
return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json())
.then((data) => data?.reports?.map(parseReport) ?? [])
.then((data) => data?.map(parseReport) ?? [])
}
const updateReportStates = ({ credentials, reports }) => {
@ -715,6 +716,16 @@ const fetchTimeline = ({
const isNotifications = timeline === 'notifications'
const params = []
let markers = {}
let markerPromise = Promise.resolve()
if (isNotifications) {
markerPromise = fetch(`${MASTODON_MARKERS_URL}?timeline=notifications`, { headers: authHeaders(credentials) })
.then((data) => data.json())
.then((data) => {
markers = data.notifications
})
}
let url = timelineUrls[timeline]
if (timeline === 'user' || timeline === 'media' || timeline === 'replies') {
@ -781,6 +792,7 @@ const fetchTimeline = ({
throw new Error(data.error)
}
if (!data.errors) {
if (isNotifications) data.map((val) => val.last_read = markers.last_read_id)
return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination }
} else {
data.status = status
@ -1277,13 +1289,15 @@ const suggestions = ({ credentials }) => {
const markNotificationsAsSeen = ({ id, credentials, single = false }) => {
const body = new FormData()
if (single) {
/*if (single) {
body.append('id', id)
} else {
body.append('max_id', id)
}
}*/
return fetch(NOTIFICATION_READ_URL, {
body.append('notifications[last_read_id]', id)
return fetch(MASTODON_MARKERS_URL, {
body,
headers: authHeaders(credentials),
method: 'POST'
@ -1341,7 +1355,7 @@ const fetchEmojiReactions = ({ id, credentials }) => {
const reactWithEmoji = ({ id, emoji, credentials }) => {
return promisedRequest({
url: PLEROMA_EMOJI_REACT_URL(id, encodeURIComponent(emoji)),
method: 'PUT',
method: 'POST',
credentials
}).then(parseStatus)
}
@ -1349,7 +1363,7 @@ const reactWithEmoji = ({ id, emoji, credentials }) => {
const unreactWithEmoji = ({ id, emoji, credentials }) => {
return promisedRequest({
url: PLEROMA_EMOJI_UNREACT_URL(id, encodeURIComponent(emoji)),
method: 'DELETE',
method: 'POST',
credentials
}).then(parseStatus)
}

View file

@ -95,7 +95,6 @@ export const parseUser = (data) => {
if (data.akkoma) {
output.instance = data.akkoma.instance
output.status_ttl_days = data.akkoma.status_ttl_days
output.permit_followback = data.akkoma.permit_followback
}
if (data.pleroma) {
@ -127,6 +126,34 @@ export const parseUser = (data) => {
} else {
output.role = 'member'
}
} else {
output.relationship = {
muting: [],
blocking: [],
followed_by: [],
following: []
}
output.rights = {
moderator: false,
admin: false
}
// todo: find a better way to map masto roles to akkoma roles
const roles = data.roles
if (roles) {
if (!roles[0]) {
output.role = 'member'
} else if (roles[0].id === "1") {
output.role = 'moderator'
output.rights.moderator = true
} else if (roles[0].id === "2" || roles[0].id === "3") {
output.role = 'admin'
output.rights.admin = true
} else {
output.role = 'member'
}
}
}
if (data.source) {
@ -298,6 +325,10 @@ export const parseStatus = (data) => {
} else {
output.text = data.content
output.summary = data.spoiler_text
output.emoji_reactions = data.reactions
output.statusnet_conversation_id = data.conversation_id
// todo: properly check if post is visible
output.parent_visible = true
}
if (data.akkoma) {
@ -406,14 +437,16 @@ export const parseStatus = (data) => {
export const parseNotification = (data) => {
const mastoDict = {
'favourite': 'like',
'reblog': 'repeat'
'reblog': 'repeat',
'reaction': 'pleroma:emoji_reaction'
}
const masto = !data.hasOwnProperty('ntype')
const output = {}
if (masto) {
output.type = mastoDict[data.type] || data.type
output.seen = data.pleroma.is_seen
// todo: figure out how to tell if a notification has been seen or not
output.seen = data.last_read && data.id <= data.last_read
if (data.status) {
output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null
output.action = output.status // TODO: Refactor, this is unneeded
@ -444,15 +477,12 @@ export const parseNotification = (data) => {
export const parseReport = (data) => {
const report = {}
report.account = parseUser(data.account)
report.actor = parseUser(data.actor)
report.account = parseUser(data.target_account.account)
report.actor = parseUser(data.account.account)
report.statuses = data.statuses.map(parseStatus)
report.notes = data.notes.map(note => {
note.user = parseUser(note.user)
return note
})
report.state = data.state
report.content = data.content
report.notes = []
report.state = data.action_taken ? "closed" : "open"
report.content = data.comment
report.created_at = data.created_at
report.id = data.id

View file

@ -10,9 +10,9 @@ export const getOrCreateApp = ({ clientId, clientSecret, instance, commit }) =>
const url = `${instance}/api/v1/apps`
const form = new window.FormData()
form.append('client_name', `PleromaFE_${window.___pleromafe_commit_hash}_${(new Date()).toISOString()}`)
form.append('client_name', "AkkomaFE")
form.append('redirect_uris', REDIRECT_URI)
form.append('scopes', 'read write follow push admin')
form.append('scopes', 'read write follow push admin:read admin:write')
return window.fetch(url, {
method: 'POST',
@ -28,7 +28,7 @@ const login = ({ instance, clientId }) => {
response_type: 'code',
client_id: clientId,
redirect_uri: REDIRECT_URI,
scope: 'read write follow push admin'
scope: 'read write follow push admin:read admin:write'
}
const dataString = reduce(data, (acc, v, k) => {

View file

@ -1,6 +1,6 @@
{
"alwaysShowSubjectInput": true,
"background": "/static/aurora_borealis.jpg",
"background": "/static/wdwskyboxbanner.png",
"collapseMessageWithSubject": false,
"greentext": false,
"hideFilteredStatuses": false,
@ -8,15 +8,15 @@
"hidePostStats": false,
"hideSitename": false,
"hideUserStats": false,
"loginMethod": "password",
"logo": "/static/logo.svg",
"loginMethod": "token",
"logo": "/static/logo.png",
"logoMargin": ".1em",
"logoMask": true,
"logoMask": false,
"logoLeft": false,
"nsfwCensorImage": "",
"postContentType": "text/plain",
"redirectRootLogin": "/main/friends",
"redirectRootNoLogin": "/main/all",
"redirectRootNoLogin": "/main/public",
"showFeaturesPanel": true,
"showInstanceSpecificPanel": false,
"sidebarRight": false,

File diff suppressed because it is too large Load diff

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,5 +1,3 @@
<h4>Terms of Service</h4>
<p>This is Akkoma-FE modified to work on top of Mastodon/Chuckya. Note that while most of it should work fine, there are still a few differences between Akkoma and Mastodon that may cause things to break.<p>
<p>This is a placeholder, overwrite this by putting a file at <pre>$STATIC_DIR/static/terms-of-service.html</pre><p>
<p>See the <a href="https://docs.akkoma.dev/main/backend/configuration/static_dir/">Static Directory</a> docs for more info.</p>
<p>Source code: <a href="https://akkoma.dev/esm/akkoma-fe">https://akkoma.dev/esm/akkoma-fe</a></p>

BIN
static/wdwskyboxbanner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB