forked from AkkomaGang/akkoma-fe
Compare commits
11 Commits
105ecd3836
...
6c7e691aea
Author | SHA1 | Date |
---|---|---|
Sol Fisher Romanoff | 6c7e691aea | |
floatingghost | 40e86998e6 | |
floatingghost | 9079ac4afa | |
floatingghost | d3280c4ab3 | |
FloatingGhost | abc75c360b | |
FloatingGhost | a8e119b0f1 | |
FloatingGhost | 17e574b173 | |
floatingghost | 71d2e0b0ce | |
FloatingGhost | b68e968bf9 | |
floatingghost | eb49295422 | |
astra akari | 337a30fe01 |
|
@ -0,0 +1,49 @@
|
|||
name: "Bug report"
|
||||
about: "Something isn't working as expected"
|
||||
title: "[bug] "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thanks for taking the time to file this bug report! Please try to be as specific and detailed as you can, so we can track down the issue and fix it as soon as possible."
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: "Version"
|
||||
description: "Which version of pleroma-fe are you running? If running develop, specify the commit hash."
|
||||
placeholder: "e.g. 2022.11, 40e86998e6"
|
||||
- type: textarea
|
||||
id: attempt
|
||||
attributes:
|
||||
label: "What were you trying to do?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expectation
|
||||
attributes:
|
||||
label: "What did you expect to happen?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reality
|
||||
attributes:
|
||||
label: "What actually happened?"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: severity
|
||||
attributes:
|
||||
label: "Severity"
|
||||
description: "Does this issue prevent you from using the software as normal?"
|
||||
options:
|
||||
- "I cannot use the software"
|
||||
- "I cannot use it as easily as I'd like"
|
||||
- "I can manage"
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: searched
|
||||
attributes:
|
||||
label: "Have you searched for this issue?"
|
||||
description: "Please double-check that your issue is not already being tracked on [the forums](https://meta.akkoma.dev) or [the issue tracker](https://akkoma.dev/AkkomaGang/pleroma-fe/issues)."
|
||||
options:
|
||||
- label: "I have double-checked and have not found this issue mentioned anywhere."
|
|
@ -0,0 +1,29 @@
|
|||
name: "Feature request"
|
||||
about: "I'd like something to be added to pleroma-fe"
|
||||
title: "[feat] "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thanks for taking the time to request a new feature! Please be as concise and clear as you can in your proposal, so we could understand what you're going for."
|
||||
- type: textarea
|
||||
id: idea
|
||||
attributes:
|
||||
label: "The idea"
|
||||
description: "What do you think you should be able to do in pleroma-fe?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reason
|
||||
attributes:
|
||||
label: "The reasoning"
|
||||
description: "Why would this be a worthwhile feature? Does it solve any problems? Have people talked about wanting it?"
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: searched
|
||||
attributes:
|
||||
label: "Have you searched for this feature request?"
|
||||
description: "Please double-check that your issue is not already being tracked on [the forums](https://meta.akkoma.dev), [the issue tracker](https://akkoma.dev/AkkomaGang/pleroma-fe/issues), or the one for [the backend](https://akkoma.dev/AkkomaGang/akkoma/issues)."
|
||||
options:
|
||||
- label: "I have double-checked and have not found this feature request mentioned anywhere."
|
||||
- label: "This feature is related to the pleroma-fe Akkoma frontend specifically, and not the backend."
|
|
@ -9,6 +9,7 @@
|
|||
<link rel="stylesheet" href="/static/font/tiresias.css">
|
||||
<link rel="stylesheet" href="/static/font/css/lato.css">
|
||||
<link rel="stylesheet" href="/static/mfm.css">
|
||||
<link rel="stylesheet" href="/static/custom.css">
|
||||
<!--server-generated-meta-->
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
class="fa-scale-110 fa-old-padding"
|
||||
/>
|
||||
</button>
|
||||
{{ ' ' }}
|
||||
<button
|
||||
v-if="showPrivate"
|
||||
class="button-unstyled scope"
|
||||
|
@ -30,7 +29,6 @@
|
|||
class="fa-scale-110 fa-old-padding"
|
||||
/>
|
||||
</button>
|
||||
{{ ' ' }}
|
||||
<button
|
||||
v-if="showUnlisted"
|
||||
class="button-unstyled scope"
|
||||
|
@ -44,7 +42,6 @@
|
|||
class="fa-scale-110 fa-old-padding"
|
||||
/>
|
||||
</button>
|
||||
{{ ' ' }}
|
||||
<button
|
||||
v-if="showPublic"
|
||||
class="button-unstyled scope"
|
||||
|
@ -87,6 +84,7 @@
|
|||
min-width: 1.3em;
|
||||
min-height: 1.3em;
|
||||
text-align: center;
|
||||
margin-right: 0.4em;
|
||||
|
||||
&.selected svg {
|
||||
color: $fallback--lightText;
|
||||
|
|
|
@ -76,6 +76,10 @@
|
|||
position: absolute;
|
||||
right: 20px;
|
||||
padding-right: 10px;
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
<div class="panel-body">
|
||||
<SettingsModalContent v-if="modalOpenedOnce" />
|
||||
</div>
|
||||
<span
|
||||
id="unscrolled-content"
|
||||
class="extra-content"
|
||||
/>
|
||||
<div class="panel-footer settings-footer">
|
||||
<Popover
|
||||
class="export"
|
||||
|
@ -53,7 +57,7 @@
|
|||
:bound-to="{ x: 'container' }"
|
||||
remove-padding
|
||||
>
|
||||
<template v-slot:trigger>
|
||||
<template #trigger>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('general.close')"
|
||||
|
@ -65,7 +69,7 @@
|
|||
/>
|
||||
</button>
|
||||
</template>
|
||||
<template v-slot:content="{close}">
|
||||
<template #content="{close}">
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
|
@ -103,14 +107,11 @@
|
|||
|
||||
<Checkbox
|
||||
:model-value="!!expertLevel"
|
||||
class="expertMode"
|
||||
@update:modelValue="expertLevel = Number($event)"
|
||||
>
|
||||
{{ $t("settings.expert_mode") }}
|
||||
</Checkbox>
|
||||
<span
|
||||
id="unscrolled-content"
|
||||
class="extra-content"
|
||||
/>
|
||||
<button
|
||||
v-if="currentUser"
|
||||
class="button-default logout-button"
|
||||
|
|
|
@ -284,7 +284,6 @@
|
|||
box-shadow: none;
|
||||
background: transparent;
|
||||
color: var(--faint, $fallback--faint);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.theme-color-cl,
|
||||
|
@ -318,11 +317,11 @@
|
|||
|
||||
.extra-content {
|
||||
.apply-container {
|
||||
padding-left: 15vw;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
justify-content: space-evenly;
|
||||
flex-grow: 1;
|
||||
|
||||
.btn {
|
||||
flex-grow: 1;
|
||||
min-height: 2em;
|
||||
|
|
|
@ -958,20 +958,22 @@
|
|||
v-if="isActive"
|
||||
to="#unscrolled-content"
|
||||
>
|
||||
<div class="apply-container">
|
||||
<button
|
||||
class="btn button-default submit"
|
||||
:disabled="!themeValid"
|
||||
@click="setCustomTheme"
|
||||
>
|
||||
{{ $t('general.apply') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="clearAll"
|
||||
>
|
||||
{{ $t('settings.style.switcher.reset') }}
|
||||
</button>
|
||||
<div class="panel-body settings-footer">
|
||||
<div class="apply-container">
|
||||
<button
|
||||
class="btn button-default submit"
|
||||
:disabled="!themeValid"
|
||||
@click="setCustomTheme"
|
||||
>
|
||||
{{ $t('general.apply') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="clearAll"
|
||||
>
|
||||
{{ $t('settings.style.switcher.reset') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
</div>
|
||||
|
|
|
@ -42,6 +42,10 @@
|
|||
display: flex;
|
||||
padding: var(--status-margin, $status-margin);
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
> * {
|
||||
min-width: 0;
|
||||
}
|
||||
|
|
|
@ -352,22 +352,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<StatusContent
|
||||
ref="content"
|
||||
:status="status"
|
||||
:no-heading="noHeading"
|
||||
:highlight="highlight"
|
||||
:focused="isFocused"
|
||||
:controlled-showing-tall="controlledShowingTall"
|
||||
:controlled-expanding-subject="controlledExpandingSubject"
|
||||
:controlled-showing-long-subject="controlledShowingLongSubject"
|
||||
:controlled-toggle-showing-tall="controlledToggleShowingTall"
|
||||
:controlled-toggle-expanding-subject="controlledToggleExpandingSubject"
|
||||
:controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject"
|
||||
@mediaplay="addMediaPlaying($event)"
|
||||
@mediapause="removeMediaPlaying($event)"
|
||||
@parseReady="setHeadTailLinks"
|
||||
/>
|
||||
<div class="content">
|
||||
<StatusContent
|
||||
ref="content"
|
||||
class="status-content"
|
||||
:status="status"
|
||||
:no-heading="noHeading"
|
||||
:highlight="highlight"
|
||||
:focused="isFocused"
|
||||
:controlled-showing-tall="controlledShowingTall"
|
||||
:controlled-expanding-subject="controlledExpandingSubject"
|
||||
:controlled-showing-long-subject="controlledShowingLongSubject"
|
||||
:controlled-toggle-showing-tall="controlledToggleShowingTall"
|
||||
:controlled-toggle-expanding-subject="controlledToggleExpandingSubject"
|
||||
:controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject"
|
||||
@mediaplay="addMediaPlaying($event)"
|
||||
@mediapause="removeMediaPlaying($event)"
|
||||
@parseReady="setHeadTailLinks"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="inConversation && !isPreview && replies && replies.length"
|
||||
|
@ -534,6 +537,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./status.js" ></script>
|
||||
<script src="./status.js"></script>
|
||||
|
||||
<style src="./status.scss" lang="scss"></style>
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
.StatusContent {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
|
||||
img, video {
|
||||
&.emoji {
|
||||
|
|
|
@ -6,11 +6,13 @@ import TimelineMenuTabs from '../timeline_menu_tabs/timeline_menu_tabs.vue'
|
|||
import TimelineQuickSettings from './timeline_quick_settings.vue'
|
||||
import { debounce, throttle, keyBy } from 'lodash'
|
||||
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(
|
||||
faCircleNotch,
|
||||
faCog
|
||||
faCog,
|
||||
faPlus,
|
||||
faMinus
|
||||
)
|
||||
|
||||
const Timeline = {
|
||||
|
@ -90,6 +92,15 @@ const Timeline = {
|
|||
},
|
||||
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 () {
|
||||
|
@ -118,6 +129,10 @@ const Timeline = {
|
|||
}
|
||||
window.addEventListener('keydown', this.handleShortKey)
|
||||
setTimeout(this.determineVisibleStatuses, 250)
|
||||
|
||||
if (this.tag) {
|
||||
this.$store.dispatch('getTag', this.tag)
|
||||
}
|
||||
},
|
||||
unmounted () {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
|
@ -232,6 +247,12 @@ const Timeline = {
|
|||
}, 200),
|
||||
handleVisibilityChange () {
|
||||
this.unfocused = document.hidden
|
||||
},
|
||||
followTag (tag) {
|
||||
return this.$store.dispatch('followTag', tag)
|
||||
},
|
||||
unfollowTag (tag) {
|
||||
return this.$store.dispatch('unfollowTag', tag)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -21,6 +21,36 @@
|
|||
{{ $t('timeline.up_to_date') }}
|
||||
</div>
|
||||
<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 :class="classes.body">
|
||||
<div
|
||||
|
|
|
@ -1055,7 +1055,9 @@
|
|||
"show_new": "Show new",
|
||||
"socket_broke": "Realtime connection lost: CloseEvent code {0}",
|
||||
"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": {
|
||||
"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 editStatusModule from './modules/editStatus.js'
|
||||
import statusHistoryModule from './modules/statusHistory.js'
|
||||
import tagModule from './modules/tags.js'
|
||||
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -96,7 +97,8 @@ const persistedStateOptions = {
|
|||
postStatus: postStatusModule,
|
||||
announcements: announcementsModule,
|
||||
editStatus: editStatusModule,
|
||||
statusHistory: statusHistoryModule
|
||||
statusHistory: statusHistoryModule,
|
||||
tags: tagModule
|
||||
},
|
||||
plugins,
|
||||
strict: false // Socket modifies itself, let's ignore this for now.
|
||||
|
|
|
@ -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 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 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
|
||||
|
||||
|
@ -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 = {} }) => {
|
||||
return Object.entries({
|
||||
...(credentials
|
||||
|
@ -1784,7 +1810,10 @@ const apiService = {
|
|||
getReports,
|
||||
updateReportStates,
|
||||
addNoteToReport,
|
||||
deleteNoteFromReport
|
||||
deleteNoteFromReport,
|
||||
getHashtag,
|
||||
followHashtag,
|
||||
unfollowHashtag
|
||||
}
|
||||
|
||||
export default apiService
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
/* THIS IS A PLACEHOLDER FILE
|
||||
place a css file at $static_dir/static/custom.css
|
||||
to apply custom styles to your frontend
|
||||
*/
|
Loading…
Reference in New Issue