Merge pull request 'small-fixes-and-improvements' (#3492) from small-fixes-and-improvements into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma-fe/pulls/3492
This commit is contained in:
commit
2b62f96889
129 changed files with 1448 additions and 814 deletions
|
|
@ -36,7 +36,6 @@ export default function () {
|
||||||
const warning = warnings[i]
|
const warning = warnings[i]
|
||||||
console.warn(' ' + warning)
|
console.warn(' ' + warning)
|
||||||
}
|
}
|
||||||
console.warn()
|
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
changelog.d/minor.add
Normal file
6
changelog.d/minor.add
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
button to remove all drafts
|
||||||
|
option to remove forced aspect ratio for user profiles (requested)
|
||||||
|
showing user tags (mrf policies for user + custom if present)
|
||||||
|
version information now is also in about page
|
||||||
|
mention autosuggest now sorts by recent activity
|
||||||
|
non-square emoji support (toggleable by user)
|
||||||
7
changelog.d/minor.change
Normal file
7
changelog.d/minor.change
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
overall improved spacings in status action buttons and post form
|
||||||
|
logout confirm button is now dangerous
|
||||||
|
reply/quote now is a radio group and wraps, fixes overflow on languages where labels are too wide
|
||||||
|
personal note input is now bigger
|
||||||
|
moved "edit pinned" to the bottom for status action buttons.
|
||||||
|
dots status action button drops down instead of up to avoid hiding the action buttons
|
||||||
|
improved attachment description (alt text) input and display
|
||||||
12
changelog.d/minor.fix
Normal file
12
changelog.d/minor.fix
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
navbar wide logo cropping search input
|
||||||
|
danger buttons being too bright
|
||||||
|
user background upload failure no longer breaks new uploads + displays an error
|
||||||
|
importing theme from old theme editor
|
||||||
|
removed duplicate federationpolicy entry in admin tab
|
||||||
|
repeater name overflowing content
|
||||||
|
reply popover is now shown if replied-to status is muted
|
||||||
|
second language input not having header
|
||||||
|
post form's bottom left buttons not showing their toggled state
|
||||||
|
some font overrides not working
|
||||||
|
popovers opening outside of window's boundaries
|
||||||
|
occasional blank page when showing new posts
|
||||||
2
changelog.d/sync-config.add
Normal file
2
changelog.d/sync-config.add
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
settings synchronization
|
||||||
|
user highlight synchronization
|
||||||
|
|
@ -67,6 +67,11 @@ export default {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
mobileActivePanel: 'timeline',
|
mobileActivePanel: 'timeline',
|
||||||
}),
|
}),
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
allowNonSquareEmoji: useMergedConfigStore().mergedConfig.nonSquareEmoji,
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
themeApplied() {
|
themeApplied() {
|
||||||
this.removeSplash()
|
this.removeSplash()
|
||||||
|
|
|
||||||
19
src/App.scss
19
src/App.scss
|
|
@ -50,7 +50,7 @@ body {
|
||||||
// have a cursor/pointer to operate them
|
// have a cursor/pointer to operate them
|
||||||
@media (any-pointer: fine) {
|
@media (any-pointer: fine) {
|
||||||
* {
|
* {
|
||||||
scrollbar-color: var(--fg) transparent;
|
scrollbar-color: var(--icon) transparent;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -130,7 +130,7 @@ body {
|
||||||
}
|
}
|
||||||
// Body should have background to scrollbar otherwise it will use white (body color?)
|
// Body should have background to scrollbar otherwise it will use white (body color?)
|
||||||
html {
|
html {
|
||||||
scrollbar-color: var(--fg) var(--wallpaper);
|
scrollbar-color: var(--icon) var(--wallpaper);
|
||||||
background: var(--wallpaper);
|
background: var(--wallpaper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -787,6 +787,19 @@ option {
|
||||||
padding: 0 0.25em;
|
padding: 0 0.25em;
|
||||||
border-radius: var(--roundness);
|
border-radius: var(--roundness);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
|
|
||||||
|
&.-dismissible {
|
||||||
|
display: flex;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
align-items: baseline;
|
||||||
|
line-height: 2;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.faint {
|
.faint {
|
||||||
|
|
@ -802,9 +815,11 @@ option {
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
|
p,
|
||||||
span {
|
span {
|
||||||
display: block;
|
display: block;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dismiss {
|
.dismiss {
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@
|
||||||
>
|
>
|
||||||
<user-panel />
|
<user-panel />
|
||||||
<template v-if="layoutType !== 'mobile'">
|
<template v-if="layoutType !== 'mobile'">
|
||||||
<nav-panel />
|
<NavPanel />
|
||||||
<instance-specific-panel v-if="showInstanceSpecificPanel" />
|
<InstanceSpecificPanel v-if="showInstanceSpecificPanel" />
|
||||||
<features-panel v-if="!currentUser && showFeaturesPanel" />
|
<FeaturesPanel v-if="!currentUser && showFeaturesPanel" />
|
||||||
<who-to-follow-panel v-if="currentUser && suggestionsEnabled" />
|
<WhoToFollowPanel v-if="currentUser && suggestionsEnabled" />
|
||||||
<div id="notifs-sidebar" />
|
<div id="notifs-sidebar" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -152,8 +152,10 @@ const getStaticConfig = async () => {
|
||||||
throw res
|
throw res
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to load static/config.json, continuing without it.')
|
console.warn(
|
||||||
console.warn(error)
|
'Failed to load static/config.json, continuing without it.',
|
||||||
|
error,
|
||||||
|
)
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,14 +177,16 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
||||||
if (source === 'name') return
|
if (source === 'name') return
|
||||||
if (INSTANCE_IDENTIY_EXTERNAL.has(source)) return
|
if (INSTANCE_IDENTIY_EXTERNAL.has(source)) return
|
||||||
useInstanceStore().set({
|
useInstanceStore().set({
|
||||||
value: config[source],
|
value:
|
||||||
|
config[source] ?? INSTANCE_IDENTITY_DEFAULT_DEFINITIONS[source].default,
|
||||||
path: `instanceIdentity.${source}`,
|
path: `instanceIdentity.${source}`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) =>
|
Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) =>
|
||||||
useInstanceStore().set({
|
useInstanceStore().set({
|
||||||
value: config[source],
|
value:
|
||||||
|
config[source] ?? INSTANCE_DEFAULT_CONFIG_DEFINITIONS[source].default,
|
||||||
path: `prefsStorage.${source}`,
|
path: `prefsStorage.${source}`,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -440,8 +444,7 @@ const getNodeInfo = async ({ store }) => {
|
||||||
throw res
|
throw res
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Could not load nodeinfo')
|
console.warn('Could not load nodeinfo', e)
|
||||||
console.warn(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import FeaturesPanel from '../features_panel/features_panel.vue'
|
import FeaturesPanel from '../features_panel/features_panel.vue'
|
||||||
import InstanceSpecificPanel from '../instance_specific_panel/instance_specific_panel.vue'
|
import InstanceSpecificPanel from '../instance_specific_panel/instance_specific_panel.vue'
|
||||||
import MRFTransparencyPanel from '../mrf_transparency_panel/mrf_transparency_panel.vue'
|
import MRFTransparencyPanel from '../mrf_transparency_panel/mrf_transparency_panel.vue'
|
||||||
|
|
@ -7,6 +9,9 @@ import TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_pane
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
|
const pleromaFeCommitUrl =
|
||||||
|
'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
||||||
|
|
||||||
const About = {
|
const About = {
|
||||||
components: {
|
components: {
|
||||||
InstanceSpecificPanel,
|
InstanceSpecificPanel,
|
||||||
|
|
@ -19,6 +24,14 @@ const About = {
|
||||||
showFeaturesPanel() {
|
showFeaturesPanel() {
|
||||||
return useInstanceStore().instanceIdentity.showFeaturesPanel
|
return useInstanceStore().instanceIdentity.showFeaturesPanel
|
||||||
},
|
},
|
||||||
|
frontendVersionLink() {
|
||||||
|
return pleromaFeCommitUrl + this.frontendVersion
|
||||||
|
},
|
||||||
|
...mapState(useInstanceStore, [
|
||||||
|
'backendVersion',
|
||||||
|
'backendRepository',
|
||||||
|
'frontendVersion',
|
||||||
|
]),
|
||||||
showInstanceSpecificPanel() {
|
showInstanceSpecificPanel() {
|
||||||
return (
|
return (
|
||||||
useInstanceStore().instanceIdentity.showInstanceSpecificPanel &&
|
useInstanceStore().instanceIdentity.showInstanceSpecificPanel &&
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,47 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column-inner">
|
<div class="About column-inner">
|
||||||
<instance-specific-panel v-if="showInstanceSpecificPanel" />
|
<instance-specific-panel v-if="showInstanceSpecificPanel" />
|
||||||
<staff-panel />
|
<staff-panel />
|
||||||
<terms-of-service-panel />
|
<terms-of-service-panel />
|
||||||
<MRFTransparencyPanel />
|
<MRFTransparencyPanel />
|
||||||
<features-panel v-if="showFeaturesPanel" />
|
<features-panel v-if="showFeaturesPanel" />
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="title">
|
||||||
|
{{ $t('settings.version.title') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<dl>
|
||||||
|
<dt>{{ $t('settings.version.backend_version') }}</dt>
|
||||||
|
<dd>
|
||||||
|
<a
|
||||||
|
:href="backendRepository"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ backendVersion }}
|
||||||
|
</a>
|
||||||
|
</dd>
|
||||||
|
<dt>{{ $t('settings.version.frontend_version') }}</dt>
|
||||||
|
<dd>
|
||||||
|
<a
|
||||||
|
:href="frontendVersionLink"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ frontendVersion }}
|
||||||
|
</a>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./about.js"></script>
|
<script src="./about.js"></script>
|
||||||
|
<style>
|
||||||
|
.About {
|
||||||
|
dl {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { mapState } from 'pinia'
|
||||||
|
|
||||||
import nsfwImage from '../../assets/nsfw.png'
|
import nsfwImage from '../../assets/nsfw.png'
|
||||||
import Flash from '../flash/flash.vue'
|
import Flash from '../flash/flash.vue'
|
||||||
|
import Popover from '../popover/popover.vue'
|
||||||
import StillImage from '../still-image/still-image.vue'
|
import StillImage from '../still-image/still-image.vue'
|
||||||
import VideoAttachment from '../video_attachment/video_attachment.vue'
|
import VideoAttachment from '../video_attachment/video_attachment.vue'
|
||||||
|
|
||||||
|
|
@ -65,13 +66,13 @@ const Attachment = {
|
||||||
modalOpen: false,
|
modalOpen: false,
|
||||||
showHidden: false,
|
showHidden: false,
|
||||||
flashLoaded: false,
|
flashLoaded: false,
|
||||||
showDescription: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Flash,
|
Flash,
|
||||||
StillImage,
|
StillImage,
|
||||||
VideoAttachment,
|
VideoAttachment,
|
||||||
|
Popover,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
classNames() {
|
classNames() {
|
||||||
|
|
@ -180,9 +181,6 @@ const Attachment = {
|
||||||
setFlashLoaded(event) {
|
setFlashLoaded(event) {
|
||||||
this.flashLoaded = event
|
this.flashLoaded = event
|
||||||
},
|
},
|
||||||
toggleDescription() {
|
|
||||||
this.showDescription = !this.showDescription
|
|
||||||
},
|
|
||||||
toggleHidden(event) {
|
toggleHidden(event) {
|
||||||
if (
|
if (
|
||||||
this.mergedConfig.useOneClickNsfw &&
|
this.mergedConfig.useOneClickNsfw &&
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
font-size: 1.25em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,3 +265,27 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description-popover {
|
||||||
|
padding: 1em;
|
||||||
|
width: 50ch;
|
||||||
|
max-width: 90vw;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 10.5em;
|
||||||
|
text-wrap: pretty;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,21 +30,16 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="size !== 'hide' && !hideDescription && (edit || localDescription || showDescription)"
|
v-if="size !== 'hide' && !hideDescription && edit"
|
||||||
class="description-container"
|
class="description-container"
|
||||||
:class="{ '-static': !edit }"
|
:class="{ '-static': !edit }"
|
||||||
>
|
>
|
||||||
<input
|
<textarea
|
||||||
v-if="edit"
|
|
||||||
v-model="localDescription"
|
v-model="localDescription"
|
||||||
type="text"
|
type="text"
|
||||||
class="input description-field"
|
class="input description-field"
|
||||||
:placeholder="$t('post_status.media_description')"
|
:placeholder="$t('post_status.media_description')"
|
||||||
@keydown.enter.prevent=""
|
/>
|
||||||
>
|
|
||||||
<p v-else>
|
|
||||||
{{ localDescription }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
|
|
@ -87,14 +82,22 @@
|
||||||
>
|
>
|
||||||
<FAIcon icon="stop" />
|
<FAIcon icon="stop" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<Popover
|
||||||
v-if="attachment.description && size !== 'small' && !edit && attachment.type !== 'unknown'"
|
v-if="attachment.description && size !== 'small' && !edit && attachment.type !== 'unknown'"
|
||||||
class="button-default attachment-button -transparent"
|
trigger="click"
|
||||||
:title="$t('status.show_attachment_description')"
|
popover-class="popover popover-default description-popover"
|
||||||
@click.prevent="toggleDescription"
|
:trigger-attrs="{ 'class': 'button-default attachment-button -transparent', 'title': $t('status.attachment_description') }"
|
||||||
>
|
>
|
||||||
<FAIcon icon="align-right" />
|
<template #trigger>
|
||||||
</button>
|
<FAIcon icon="align-right" />
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<details open>
|
||||||
|
<summary>{{ $t('status.attachment_description') }}</summary>
|
||||||
|
<span>{{ localDescription }}</span>
|
||||||
|
</details>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
<button
|
<button
|
||||||
v-if="!useModal && attachment.type !== 'unknown'"
|
v-if="!useModal && attachment.type !== 'unknown'"
|
||||||
class="button-default attachment-button -transparent"
|
class="button-default attachment-button -transparent"
|
||||||
|
|
@ -244,21 +247,16 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="size !== 'hide' && !hideDescription && (edit || (localDescription && showDescription))"
|
v-if="size !== 'hide' && !hideDescription && edit"
|
||||||
class="description-container"
|
class="description-container"
|
||||||
:class="{ '-static': !edit }"
|
:class="{ '-static': !edit }"
|
||||||
>
|
>
|
||||||
<input
|
<textarea
|
||||||
v-if="edit"
|
|
||||||
v-model="localDescription"
|
v-model="localDescription"
|
||||||
type="text"
|
type="text"
|
||||||
class="input description-field"
|
class="input description-field"
|
||||||
:placeholder="$t('post_status.media_description')"
|
:placeholder="$t('post_status.media_description')"
|
||||||
@keydown.enter.prevent=""
|
/>
|
||||||
>
|
|
||||||
<p v-else>
|
|
||||||
{{ localDescription }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import UserLink from '../user_link/user_link.vue'
|
||||||
import UserPopover from '../user_popover/user_popover.vue'
|
import UserPopover from '../user_popover/user_popover.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
|
|
@ -24,6 +25,11 @@ const BasicUserCard = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return useMergedConfigStore().mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BasicUserCard
|
export default BasicUserCard
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
class="basic-user-card-user-name-value"
|
class="basic-user-card-user-name-value"
|
||||||
:html="user.name"
|
:html="user.name"
|
||||||
:emoji="user.emoji"
|
:emoji="user.emoji"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ export default {
|
||||||
{
|
{
|
||||||
variant: 'danger',
|
variant: 'danger',
|
||||||
directives: {
|
directives: {
|
||||||
background: '--cRed',
|
background: '$blend(--cRed 0.25 --inheritedBackground)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { defineAsyncComponent } from 'vue'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
|
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChatTitle',
|
name: 'ChatTitle',
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -20,5 +22,8 @@ export default {
|
||||||
htmlTitle() {
|
htmlTitle() {
|
||||||
return this.user ? this.user.name_html : ''
|
return this.user ? this.user.name_html : ''
|
||||||
},
|
},
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return useMergedConfigStore().mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
:title="'@'+(user && user.screen_name_ui)"
|
:title="'@'+(user && user.screen_name_ui)"
|
||||||
:html="htmlTitle"
|
:html="htmlTitle"
|
||||||
:emoji="user.emoji || []"
|
:emoji="user.emoji || []"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
:is-local="user.is_local"
|
:is-local="user.is_local"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,14 @@ export default {
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.checkbox {
|
.checkbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
min-height: 1.2em;
|
min-height: 1.2em;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0 0.5em;
|
||||||
|
|
||||||
&-indicator,
|
&-indicator,
|
||||||
& .label {
|
& .label {
|
||||||
vertical-align: middle;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > &-indicator {
|
& > &-indicator {
|
||||||
|
|
@ -138,15 +140,5 @@ export default {
|
||||||
content: "–";
|
content: "–";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .label {
|
|
||||||
&.-after {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.-before {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ export default {
|
||||||
label: {
|
label: {
|
||||||
required: false,
|
required: false,
|
||||||
type: String,
|
type: String,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
// use unstyled, uh, style
|
// use unstyled, uh, style
|
||||||
unstyled: {
|
unstyled: {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ const ConfirmModal = {
|
||||||
confirmText: {
|
confirmText: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
confirmDanger: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['cancelled', 'accepted'],
|
emits: ['cancelled', 'accepted'],
|
||||||
computed: {},
|
computed: {},
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
<slot name="footerLeft" />
|
<slot name="footerLeft" />
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
|
:class="{ '-danger': confirmDanger }"
|
||||||
@click.prevent="onAccept"
|
@click.prevent="onAccept"
|
||||||
v-text="confirmText"
|
v-text="confirmText"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
.inner-nav {
|
.inner-nav {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: var(--navbar-height);
|
grid-template-rows: var(--navbar-height);
|
||||||
grid-template-columns: 2fr auto 2fr;
|
grid-template-columns: minmax(5em, 1fr) auto minmax(5em, 1fr);
|
||||||
grid-template-areas: "sitename logo actions";
|
grid-template-areas: "sitename logo actions";
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0 1.2em;
|
padding: 0 1.2em;
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-logoLeft .inner-nav {
|
&.-logoLeft .inner-nav {
|
||||||
grid-template-columns: auto 2fr 2fr;
|
grid-template-columns: auto minmax(5em, 1fr) minmax(5em, 1fr);
|
||||||
grid-template-areas: "logo sitename actions";
|
grid-template-areas: "logo sitename actions";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,23 +92,18 @@
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
grid-area: actions;
|
grid-area: actions;
|
||||||
|
justify-content: flex-end;
|
||||||
|
text-align: right;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
flex: 1;
|
|
||||||
line-height: var(--navbar-height);
|
line-height: var(--navbar-height);
|
||||||
height: var(--navbar-height);
|
height: var(--navbar-height);
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
&.right {
|
|
||||||
justify-content: flex-end;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
width: 1em;
|
min-width: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,54 +32,57 @@
|
||||||
>
|
>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="item right actions">
|
<div class="item right actions">
|
||||||
<search-bar
|
<SearchBar
|
||||||
v-if="currentUser || !privateMode"
|
v-if="currentUser || !privateMode"
|
||||||
@toggled="onSearchBarToggled"
|
@toggled="onSearchBarToggled"
|
||||||
@click.stop
|
@click.stop
|
||||||
/>
|
/>
|
||||||
<button
|
<template v-if="searchBarHidden">
|
||||||
class="button-unstyled nav-icon"
|
<button
|
||||||
:title="$t('nav.preferences')"
|
class="button-unstyled nav-icon"
|
||||||
@click.stop="openSettingsModal('user')"
|
:title="$t('nav.preferences')"
|
||||||
>
|
@click.stop="openSettingsModal('user')"
|
||||||
<FAIcon
|
>
|
||||||
fixed-width
|
<FAIcon
|
||||||
class="fa-scale-110 fa-old-padding"
|
fixed-width
|
||||||
icon="cog"
|
class="fa-scale-110 fa-old-padding"
|
||||||
/>
|
icon="cog"
|
||||||
</button>
|
/>
|
||||||
<button
|
</button>
|
||||||
v-if="currentUser && currentUser.role === 'admin'"
|
<button
|
||||||
class="button-unstyled nav-icon"
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
target="_blank"
|
class="button-unstyled nav-icon"
|
||||||
:title="$t('nav.administration')"
|
target="_blank"
|
||||||
@click.stop="openSettingsModal('admin')"
|
:title="$t('nav.administration')"
|
||||||
>
|
@click.stop="openSettingsModal('admin')"
|
||||||
<FAIcon
|
>
|
||||||
fixed-width
|
<FAIcon
|
||||||
class="fa-scale-110 fa-old-padding"
|
fixed-width
|
||||||
icon="tachometer-alt"
|
class="fa-scale-110 fa-old-padding"
|
||||||
/>
|
icon="tachometer-alt"
|
||||||
</button>
|
/>
|
||||||
<span class="spacer" />
|
</button>
|
||||||
<button
|
<span class="spacer" />
|
||||||
v-if="currentUser"
|
<button
|
||||||
class="button-unstyled nav-icon"
|
v-if="currentUser"
|
||||||
:title="$t('login.logout')"
|
class="button-unstyled nav-icon"
|
||||||
@click.stop.prevent="logout"
|
:title="$t('login.logout')"
|
||||||
>
|
@click.stop.prevent="logout"
|
||||||
<FAIcon
|
>
|
||||||
fixed-width
|
<FAIcon
|
||||||
class="fa-scale-110 fa-old-padding"
|
fixed-width
|
||||||
icon="sign-out-alt"
|
class="fa-scale-110 fa-old-padding"
|
||||||
/>
|
icon="sign-out-alt"
|
||||||
</button>
|
/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<teleport to="#modal">
|
<teleport to="#modal">
|
||||||
<confirm-modal
|
<confirm-modal
|
||||||
v-if="showingConfirmLogout"
|
v-if="showingConfirmLogout"
|
||||||
:title="$t('login.logout_confirm_title')"
|
:title="$t('login.logout_confirm_title')"
|
||||||
|
:confirm-danger="true"
|
||||||
:confirm-text="$t('login.logout_confirm_accept_button')"
|
:confirm-text="$t('login.logout_confirm_accept_button')"
|
||||||
:cancel-text="$t('login.logout_confirm_cancel_button')"
|
:cancel-text="$t('login.logout_confirm_cancel_button')"
|
||||||
@accepted="doLogout"
|
@accepted="doLogout"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||||
import Draft from 'src/components/draft/draft.vue'
|
import Draft from 'src/components/draft/draft.vue'
|
||||||
import List from 'src/components/list/list.vue'
|
import List from 'src/components/list/list.vue'
|
||||||
|
|
||||||
|
|
@ -5,12 +6,31 @@ const Drafts = {
|
||||||
components: {
|
components: {
|
||||||
Draft,
|
Draft,
|
||||||
List,
|
List,
|
||||||
|
ConfirmModal,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showingConfirmDialog: false,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
drafts() {
|
drafts() {
|
||||||
return this.$store.getters.draftsArray
|
return this.$store.getters.draftsArray
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
abandonAll() {
|
||||||
|
this.showingConfirmDialog = true
|
||||||
|
},
|
||||||
|
doAbandonAll() {
|
||||||
|
this.$store
|
||||||
|
.dispatch('abandonAllDrafts')
|
||||||
|
.then(() => this.hideConfirmDialog())
|
||||||
|
},
|
||||||
|
hideConfirmDialog() {
|
||||||
|
this.showingConfirmDialog = false
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Drafts
|
export default Drafts
|
||||||
|
|
|
||||||
|
|
@ -13,36 +13,66 @@
|
||||||
>
|
>
|
||||||
{{ $t('drafts.no_drafts') }}
|
{{ $t('drafts.no_drafts') }}
|
||||||
</div>
|
</div>
|
||||||
<List
|
<template v-else>
|
||||||
v-else
|
<List
|
||||||
:items="drafts"
|
:items="drafts"
|
||||||
:non-interactive="true"
|
:non-interactive="true"
|
||||||
>
|
>
|
||||||
<template #item="{ item: draft }">
|
<template #item="{ item: draft }">
|
||||||
<Draft
|
<Draft
|
||||||
class="draft"
|
class="draft"
|
||||||
:draft="draft"
|
:draft="draft"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</List>
|
</List>
|
||||||
|
<div class="remove-all">
|
||||||
|
<button
|
||||||
|
class="btn -danger button-default"
|
||||||
|
@click="abandonAll"
|
||||||
|
>
|
||||||
|
{{ $t('drafts.clean_drafts') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<teleport to="#modal">
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="showingConfirmDialog"
|
||||||
|
:confirm-danger="true"
|
||||||
|
:title="$t('drafts.abandon_confirm_title')"
|
||||||
|
:confirm-text="$t('drafts.abandon_confirm_accept_button')"
|
||||||
|
:cancel-text="$t('drafts.abandon_confirm_cancel_button')"
|
||||||
|
@accepted="doAbandonAll"
|
||||||
|
@cancelled="hideConfirmDialog"
|
||||||
|
>
|
||||||
|
{{ $t('drafts.abandon_all_confirm') }}
|
||||||
|
</ConfirmModal>
|
||||||
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./drafts.js"></script>
|
<script src="./drafts.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.draft {
|
.Drafts {
|
||||||
margin: 1em 0;
|
.draft {
|
||||||
}
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
.empty-drafs-list-alert {
|
.remove-all {
|
||||||
padding: 3em;
|
margin: 1em;
|
||||||
font-size: 1.2em;
|
display: flex;
|
||||||
display: flex;
|
justify-content: center;
|
||||||
justify-content: center;
|
}
|
||||||
color: var(--textFaint);
|
|
||||||
|
.empty-drafs-list-alert {
|
||||||
|
padding: 3em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--textFaint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,6 @@ const EmojiInput = {
|
||||||
if (!this.pickerShown) {
|
if (!this.pickerShown) {
|
||||||
this.scrollIntoView()
|
this.scrollIntoView()
|
||||||
this.$refs.picker.showPicker()
|
this.$refs.picker.showPicker()
|
||||||
this.$refs.picker.startEmojiLoad()
|
|
||||||
} else {
|
} else {
|
||||||
this.$refs.picker.hidePicker()
|
this.$refs.picker.hidePicker()
|
||||||
}
|
}
|
||||||
|
|
@ -590,7 +589,7 @@ const EmojiInput = {
|
||||||
setCaret({ target: { selectionStart } }) {
|
setCaret({ target: { selectionStart } }) {
|
||||||
this.caret = selectionStart
|
this.caret = selectionStart
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.suggestorPopover.updateStyles()
|
this.$refs.suggestorPopover?.updateStyles()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
autoCompleteItemLabel(suggestion) {
|
autoCompleteItemLabel(suggestion) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div
|
<div
|
||||||
ref="root"
|
ref="root"
|
||||||
class="input emoji-input"
|
class="input emoji-input"
|
||||||
:class="{ 'with-picker': !hideEmojiButton }"
|
:class="{ '-with-picker': !hideEmojiButton, '-textarea': input?.tagName === 'TEXTAREA' }"
|
||||||
>
|
>
|
||||||
<slot
|
<slot
|
||||||
:id="'textbox-' + randomSeed"
|
:id="'textbox-' + randomSeed"
|
||||||
|
|
@ -118,9 +118,10 @@
|
||||||
|
|
||||||
.emoji-picker-icon {
|
.emoji-picker-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
margin: 0.2em 0.25em;
|
height: 100%;
|
||||||
|
padding: 0 0.2em;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
|
|
@ -130,6 +131,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.-textarea {
|
||||||
|
.emoji-picker-icon {
|
||||||
|
height: auto;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.emoji-picker-panel {
|
.emoji-picker-panel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
|
@ -151,8 +159,11 @@
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.with-picker input {
|
&.-with-picker {
|
||||||
padding-right: 2em;
|
textarea,
|
||||||
|
input {
|
||||||
|
padding-right: 2.4em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden-overlay {
|
.hidden-overlay {
|
||||||
|
|
|
||||||
|
|
@ -131,10 +131,11 @@ export const suggestUsers = ({ dispatch, state }) => {
|
||||||
const diff = (bScore - aScore) * 10
|
const diff = (bScore - aScore) * 10
|
||||||
|
|
||||||
// Then sort alphabetically
|
// Then sort alphabetically
|
||||||
|
const activity = a.last_status_at < b.last_status_at ? 100 : -100
|
||||||
const nameAlphabetically = a.name > b.name ? 1 : -1
|
const nameAlphabetically = a.name > b.name ? 1 : -1
|
||||||
const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1
|
const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1
|
||||||
|
|
||||||
return diff + nameAlphabetically + screenNameAlphabetically
|
return diff + nameAlphabetically + screenNameAlphabetically + activity
|
||||||
})
|
})
|
||||||
.map((user) => ({
|
.map((user) => ({
|
||||||
user,
|
user,
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ const EmojiPicker = {
|
||||||
hideCustomEmojiInPicker: false,
|
hideCustomEmojiInPicker: false,
|
||||||
// Lazy-load only after the first time `showing` becomes true.
|
// Lazy-load only after the first time `showing` becomes true.
|
||||||
contentLoaded: false,
|
contentLoaded: false,
|
||||||
|
popoverShown: false,
|
||||||
groupRefs: {},
|
groupRefs: {},
|
||||||
emojiRefs: {},
|
emojiRefs: {},
|
||||||
filteredEmojiGroups: [],
|
filteredEmojiGroups: [],
|
||||||
|
|
@ -176,6 +177,13 @@ const EmojiPicker = {
|
||||||
const fullEmojiSize = emojiSizeReal + 2 * 0.2 * fontSizeMultiplier * 14
|
const fullEmojiSize = emojiSizeReal + 2 * 0.2 * fontSizeMultiplier * 14
|
||||||
this.emojiSize = fullEmojiSize
|
this.emojiSize = fullEmojiSize
|
||||||
},
|
},
|
||||||
|
togglePicker() {
|
||||||
|
if (this.popoverShown) {
|
||||||
|
this.hidePicker()
|
||||||
|
} else {
|
||||||
|
this.showPicker()
|
||||||
|
}
|
||||||
|
},
|
||||||
showPicker() {
|
showPicker() {
|
||||||
this.$refs.popover.showPopover()
|
this.$refs.popover.showPopover()
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|
@ -194,10 +202,10 @@ const EmojiPicker = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPopoverShown() {
|
onPopoverShown() {
|
||||||
this.$emit('show')
|
this.popoverShown = true
|
||||||
},
|
},
|
||||||
onPopoverClosed() {
|
onPopoverClosed() {
|
||||||
this.$emit('close')
|
this.popoverShown = false
|
||||||
},
|
},
|
||||||
onStickerUploaded(e) {
|
onStickerUploaded(e) {
|
||||||
this.$emit('sticker-uploaded', e)
|
this.$emit('sticker-uploaded', e)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
popover-class="emoji-picker popover-default"
|
popover-class="emoji-picker popover-default"
|
||||||
:hide-trigger="true"
|
:hide-trigger="true"
|
||||||
|
placement="bottom"
|
||||||
@show="onPopoverShown"
|
@show="onPopoverShown"
|
||||||
@close="onPopoverClosed"
|
@close="onPopoverClosed"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import UserListPopover from '../user_list_popover/user_list_popover.vue'
|
import UserListPopover from '../user_list_popover/user_list_popover.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
|
import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -48,6 +49,9 @@ const EmojiReactions = {
|
||||||
statusId: this.status.id,
|
statusId: this.status.id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return useMergedConfigStore().mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleShowAll() {
|
toggleShowAll() {
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,12 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
&.-wide {
|
||||||
|
width: auto;
|
||||||
|
min-width: var(--emoji-size);
|
||||||
|
max-width: calc(var(--emoji-size) * 3);
|
||||||
|
}
|
||||||
|
|
||||||
--_still_image-label-scale: 0.3;
|
--_still_image-label-scale: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,6 +68,12 @@
|
||||||
font-size: calc(var(--emoji-size) * 0.8);
|
font-size: calc(var(--emoji-size) * 0.8);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
&.-wide {
|
||||||
|
width: auto;
|
||||||
|
min-width: var(--emoji-size);
|
||||||
|
max-width: calc(var(--emoji-size) * 3);
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,13 @@
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="reaction-emoji"
|
class="reaction-emoji"
|
||||||
|
:class="{ ['-wide']: allowNonSquareEmoji }"
|
||||||
>
|
>
|
||||||
<StillImage
|
<StillImage
|
||||||
v-if="reaction.url"
|
v-if="reaction.url"
|
||||||
:src="reaction.url"
|
:src="reaction.url"
|
||||||
class="reaction-emoji-content"
|
class="reaction-emoji-content"
|
||||||
|
:class="{ ['-wide']: allowNonSquareEmoji }"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
<div
|
<div
|
||||||
v-if="modelValue?.family"
|
v-if="modelValue"
|
||||||
class="font-input setting-item"
|
class="font-input setting-item"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
|
|
@ -67,10 +67,10 @@
|
||||||
</button>
|
</button>
|
||||||
<input
|
<input
|
||||||
:id="name"
|
:id="name"
|
||||||
:model-value="modelValue.family"
|
:model-value="modelValue"
|
||||||
class="input custom-font"
|
class="input custom-font"
|
||||||
type="text"
|
type="text"
|
||||||
@update:modelValue="$emit('update:modelValue', { ...(modelValue || {}), family: $event.target.value })"
|
@update:modelValue="$emit('update:modelValue', $event.target.value)"
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
|
|
@ -89,9 +89,9 @@
|
||||||
</button>
|
</button>
|
||||||
<Select
|
<Select
|
||||||
:id="name + '-local-font-switcher'"
|
:id="name + '-local-font-switcher'"
|
||||||
:model-value="modelValue?.family"
|
:model-value="modelValue"
|
||||||
class="custom-font"
|
class="custom-font"
|
||||||
@update:model-value="v => $emit('update:modelValue', { ...(modelValue || {}), family: v })"
|
@update:model-value="v => $emit('update:modelValue', v)"
|
||||||
>
|
>
|
||||||
<optgroup
|
<optgroup
|
||||||
:label="$t('settings.style.themes3.font.group-builtin')"
|
:label="$t('settings.style.themes3.font.group-builtin')"
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,11 @@ const Gallery = {
|
||||||
: attachments
|
: attachments
|
||||||
.reduce(
|
.reduce(
|
||||||
(acc, attachment, i) => {
|
(acc, attachment, i) => {
|
||||||
|
const peek = attachments[i + 1]
|
||||||
|
const nextEnd = peek == null
|
||||||
|
const nextWide = !nextEnd && !displayTypes.has(peek?.type)
|
||||||
|
|
||||||
|
// Inserting new row
|
||||||
if (attachment.type === 'audio') {
|
if (attachment.type === 'audio') {
|
||||||
return [
|
return [
|
||||||
...acc,
|
...acc,
|
||||||
|
|
@ -61,18 +66,27 @@ const Gallery = {
|
||||||
{ items: [] },
|
{ items: [] },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxPerRow = 3
|
const maxPerRow = 3
|
||||||
const attachmentsRemaining = this.attachments.length - i + 1
|
const currentRow = acc[acc.length - 1]
|
||||||
const currentRow = acc[acc.length - 1].items
|
const previousRow = acc[acc.length - 2]
|
||||||
currentRow.push(attachment)
|
|
||||||
if (
|
if (currentRow.items.length >= maxPerRow) {
|
||||||
currentRow.length >= maxPerRow &&
|
if (nextWide || nextEnd) {
|
||||||
attachmentsRemaining > maxPerRow
|
if (previousRow?.items.length > 1) {
|
||||||
) {
|
currentRow.items.push(attachment)
|
||||||
return [...acc, { items: [] }]
|
return [...acc, { items: [] }]
|
||||||
|
} else {
|
||||||
|
const last = currentRow.items.splice(-1)[0]
|
||||||
|
return [...acc, { items: [last, attachment] }]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [...acc, { items: [attachment] }]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return acc
|
currentRow.items.push(attachment)
|
||||||
}
|
}
|
||||||
|
return acc
|
||||||
},
|
},
|
||||||
[{ items: [] }],
|
[{ items: [] }],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@
|
||||||
|
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 15em;
|
height: 20em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./instance_specific_panel.js"></script>
|
<script src="./instance_specific_panel.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.instance-specific-panel .panel-body {
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@
|
||||||
</tab-switcher>
|
</tab-switcher>
|
||||||
<Notifications
|
<Notifications
|
||||||
ref="notifications"
|
ref="notifications"
|
||||||
:no-heading="true"
|
|
||||||
:no-extra="true"
|
:no-extra="true"
|
||||||
:minimal-mode="true"
|
:minimal-mode="true"
|
||||||
:filter-mode="filterMode"
|
:filter-mode="filterMode"
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
max-height: 10em;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: var(--roundness);
|
border-radius: var(--roundness);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,16 @@
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span
|
<details
|
||||||
v-if="description"
|
v-if="description"
|
||||||
|
open
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
{{ description }}
|
<summary>{{ $t('status.attachment_description') }}</summary>
|
||||||
</span>
|
<span>{{ description }}</span>
|
||||||
|
</details>
|
||||||
<span
|
<span
|
||||||
|
v-if="media.length > 1"
|
||||||
class="counter"
|
class="counter"
|
||||||
>
|
>
|
||||||
{{ $t('media_modal.counter', { current: currentIndex + 1, total: media.length }, currentIndex + 1) }}
|
{{ $t('media_modal.counter', { current: currentIndex + 1, total: media.length }, currentIndex + 1) }}
|
||||||
|
|
@ -159,19 +162,43 @@ $modal-view-button-icon-margin: 0.5em;
|
||||||
.counter {
|
.counter {
|
||||||
/* Hardcoded since background is also hardcoded */
|
/* Hardcoded since background is also hardcoded */
|
||||||
color: white;
|
color: white;
|
||||||
margin-top: 1em;
|
text-shadow: 0 0 1em black, 0 0 1em black, 0 0 1em black;
|
||||||
text-shadow: 0 0 10px black, 0 0 10px black;
|
margin: 1em 2em;
|
||||||
padding: 0.2em 2em;
|
overflow: hidden;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.description + .counter {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
overflow-y: auto;
|
max-width: 80ch;
|
||||||
min-height: 1em;
|
|
||||||
max-width: 35.8em;
|
|
||||||
max-height: 9.5em;
|
max-height: 9.5em;
|
||||||
overflow-wrap: break-word;
|
|
||||||
text-wrap: pretty;
|
summary {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 1em;
|
||||||
|
text-wrap: pretty;
|
||||||
|
max-height: 10.5em;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
line-height: 1.5;
|
||||||
|
scrollbar-color: white transparent;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-button,
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-image {
|
.modal-image {
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@
|
||||||
<confirm-modal
|
<confirm-modal
|
||||||
v-if="showingConfirmLogout"
|
v-if="showingConfirmLogout"
|
||||||
:title="$t('login.logout_confirm_title')"
|
:title="$t('login.logout_confirm_title')"
|
||||||
|
:confirm-danger="true"
|
||||||
:confirm-text="$t('login.logout_confirm_accept_button')"
|
:confirm-text="$t('login.logout_confirm_accept_button')"
|
||||||
:cancel-text="$t('login.logout_confirm_cancel_button')"
|
:cancel-text="$t('login.logout_confirm_cancel_button')"
|
||||||
@accepted="doLogout"
|
@accepted="doLogout"
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,6 @@ const Notification = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doApprove() {
|
doApprove() {
|
||||||
this.$emit('interacted')
|
|
||||||
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
|
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', {
|
this.$store.dispatch('markSingleNotificationAsSeen', {
|
||||||
|
|
@ -166,7 +165,6 @@ const Notification = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doDeny() {
|
doDeny() {
|
||||||
this.$emit('interacted')
|
|
||||||
this.$store.state.api.backendInteractor
|
this.$store.state.api.backendInteractor
|
||||||
.denyUser({ id: this.user.id })
|
.denyUser({ id: this.user.id })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -212,6 +210,9 @@ const Notification = {
|
||||||
mergedConfig() {
|
mergedConfig() {
|
||||||
return useMergedConfigStore().mergedConfig
|
return useMergedConfigStore().mergedConfig
|
||||||
},
|
},
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return this.mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
shouldConfirmApprove() {
|
shouldConfirmApprove() {
|
||||||
return this.mergedConfig.modalOnApproveFollow
|
return this.mergedConfig.modalOnApproveFollow
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
class="Notification"
|
class="Notification"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
:statusoid="notification.status"
|
:statusoid="notification.status"
|
||||||
@interacted="interacted"
|
@click="interacted"
|
||||||
/>
|
/>
|
||||||
</article>
|
</article>
|
||||||
<article
|
<article
|
||||||
|
|
@ -71,6 +71,7 @@
|
||||||
:title="'@'+notification.from_profile.screen_name_ui"
|
:title="'@'+notification.from_profile.screen_name_ui"
|
||||||
:html="notification.from_profile.name_html"
|
:html="notification.from_profile.name_html"
|
||||||
:emoji="notification.from_profile.emoji"
|
:emoji="notification.from_profile.emoji"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
:is-local="notification.from_profile.is_local"
|
:is-local="notification.from_profile.is_local"
|
||||||
/>
|
/>
|
||||||
</bdi>
|
</bdi>
|
||||||
|
|
@ -136,6 +137,7 @@
|
||||||
:src="notification.emoji_url"
|
:src="notification.emoji_url"
|
||||||
:alt="notification.emoji"
|
:alt="notification.emoji"
|
||||||
:title="notification.emoji"
|
:title="notification.emoji"
|
||||||
|
:class="{ ['-wide']: allowNonSquareEmoji }"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ const Notifications = {
|
||||||
return unseenNotificationsFromStore(
|
return unseenNotificationsFromStore(
|
||||||
this.$store,
|
this.$store,
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
|
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
filteredNotifications() {
|
filteredNotifications() {
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,10 @@
|
||||||
max-width: 1.25em;
|
max-width: 1.25em;
|
||||||
height: 1.25em;
|
height: 1.25em;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
|
||||||
|
&.-wide {
|
||||||
|
max-width: 3.75em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-reaction-emoji-image {
|
.emoji-reaction-emoji-image {
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@ export default {
|
||||||
return this.expired ? 'polls.expired' : 'polls.expires_in'
|
return this.expired ? 'polls.expired' : 'polls.expires_in'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return useMergedConfigStore().mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
:html="option.title_html"
|
:html="option.title_html"
|
||||||
:handle-links="false"
|
:handle-links="false"
|
||||||
:emoji="emoji"
|
:emoji="emoji"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('polls.option')"
|
:placeholder="$t('polls.option')"
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
@change="updatePollToParent"
|
|
||||||
@keydown.enter.stop.prevent="nextOption(index)"
|
@keydown.enter.stop.prevent="nextOption(index)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -50,7 +49,6 @@
|
||||||
v-model="pollType"
|
v-model="pollType"
|
||||||
class="poll-type-select"
|
class="poll-type-select"
|
||||||
unstyled="true"
|
unstyled="true"
|
||||||
@change="updatePollToParent"
|
|
||||||
>
|
>
|
||||||
<option value="single">
|
<option value="single">
|
||||||
{{ $t('polls.single_choice') }}
|
{{ $t('polls.single_choice') }}
|
||||||
|
|
|
||||||
|
|
@ -304,10 +304,13 @@ const Popover = {
|
||||||
}
|
}
|
||||||
this.scrollable.addEventListener('scroll', this.onScroll)
|
this.scrollable.addEventListener('scroll', this.onScroll)
|
||||||
this.scrollable.addEventListener('resize', this.onResize)
|
this.scrollable.addEventListener('resize', this.onResize)
|
||||||
this.$nextTick(() => {
|
// My assumption is that upon showing popover initially has different size
|
||||||
|
// as its contents are getting populating, so logic uses those incorrect
|
||||||
|
// sizes as basis
|
||||||
|
setTimeout(() => {
|
||||||
if (wasHidden) this.$emit('show')
|
if (wasHidden) this.$emit('show')
|
||||||
this.updateStyles()
|
this.updateStyles()
|
||||||
})
|
}, 1)
|
||||||
},
|
},
|
||||||
hidePopover() {
|
hidePopover() {
|
||||||
if (this.disabled) return
|
if (this.disabled) return
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,6 @@ const PostStatusForm = {
|
||||||
DraftCloser,
|
DraftCloser,
|
||||||
Popover,
|
Popover,
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.initQuote()
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.updateIdempotencyKey()
|
this.updateIdempotencyKey()
|
||||||
this.resize(this.$refs.textarea)
|
this.resize(this.$refs.textarea)
|
||||||
|
|
@ -214,7 +211,11 @@ const PostStatusForm = {
|
||||||
poll: {},
|
poll: {},
|
||||||
hasPoll: false,
|
hasPoll: false,
|
||||||
hasQuote: false,
|
hasQuote: false,
|
||||||
quote: {},
|
quote: {
|
||||||
|
id: '',
|
||||||
|
url: '',
|
||||||
|
thread: false,
|
||||||
|
},
|
||||||
mediaDescriptions: {},
|
mediaDescriptions: {},
|
||||||
visibility: scope,
|
visibility: scope,
|
||||||
contentType,
|
contentType,
|
||||||
|
|
@ -233,7 +234,11 @@ const PostStatusForm = {
|
||||||
poll: this.statusPoll || {},
|
poll: this.statusPoll || {},
|
||||||
hasPoll: false,
|
hasPoll: false,
|
||||||
hasQuote: false,
|
hasQuote: false,
|
||||||
quote: {},
|
quote: {
|
||||||
|
id: '',
|
||||||
|
url: '',
|
||||||
|
thread: false,
|
||||||
|
},
|
||||||
mediaDescriptions: this.statusMediaDescriptions || {},
|
mediaDescriptions: this.statusMediaDescriptions || {},
|
||||||
visibility: this.statusScope || scope,
|
visibility: this.statusScope || scope,
|
||||||
contentType: statusContentType,
|
contentType: statusContentType,
|
||||||
|
|
@ -373,8 +378,15 @@ const PostStatusForm = {
|
||||||
quotable() {
|
quotable() {
|
||||||
return this.quotingAvailable && this.replyTo
|
return this.quotingAvailable && this.replyTo
|
||||||
},
|
},
|
||||||
quoteThreadToggled() {
|
quoteThreadToggled: {
|
||||||
return this.newStatus.hasQuote && this.newStatus.quote.thread
|
get() {
|
||||||
|
return this.newStatus.hasQuote && this.newStatus.quote.thread
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.newStatus.hasQuote = value
|
||||||
|
this.newStatus.quote.thread = value
|
||||||
|
this.newStatus.quote.id = value ? this.replyTo : ''
|
||||||
|
},
|
||||||
},
|
},
|
||||||
defaultQuotable() {
|
defaultQuotable() {
|
||||||
if (
|
if (
|
||||||
|
|
@ -855,24 +867,6 @@ const PostStatusForm = {
|
||||||
this.$refs.pollForm.clear()
|
this.$refs.pollForm.clear()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initQuote() {
|
|
||||||
const quote = this.newStatus.quote
|
|
||||||
|
|
||||||
if (Object.keys(quote).length > 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const quotable = this.defaultQuotable
|
|
||||||
|
|
||||||
quote.id = quotable ? this.replyTo : ''
|
|
||||||
quote.url = ''
|
|
||||||
quote.thread = quotable
|
|
||||||
},
|
|
||||||
setQuoteThread(v) {
|
|
||||||
this.newStatus.hasQuote = v
|
|
||||||
this.newStatus.quote.thread = v
|
|
||||||
this.newStatus.quote.id = v ? this.replyTo : ''
|
|
||||||
},
|
|
||||||
clearQuoteForm() {
|
clearQuoteForm() {
|
||||||
if (this.$refs.quoteForm) {
|
if (this.$refs.quoteForm) {
|
||||||
this.$refs.quoteForm.clear()
|
this.$refs.quoteForm.clear()
|
||||||
|
|
@ -880,6 +874,11 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
toggleQuoteForm() {
|
toggleQuoteForm() {
|
||||||
this.newStatus.hasQuote = !this.newStatus.hasQuote
|
this.newStatus.hasQuote = !this.newStatus.hasQuote
|
||||||
|
|
||||||
|
this.newStatus.quote = {}
|
||||||
|
this.newStatus.quote.thread = false
|
||||||
|
this.newStatus.quote.id = null
|
||||||
|
this.newStatus.quote.url = ''
|
||||||
},
|
},
|
||||||
dismissScopeNotice() {
|
dismissScopeNotice() {
|
||||||
useSyncConfigStore().setSimplePrefAndSave({
|
useSyncConfigStore().setSimplePrefAndSave({
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
.form-bottom-left {
|
.form-bottom-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1.5em;
|
gap: 1.5em;
|
||||||
|
margin-right: 1em;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|
@ -52,13 +53,13 @@
|
||||||
.preview-heading {
|
.preview-heading {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
margin: 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-toggle {
|
.preview-toggle {
|
||||||
flex: 10 0 auto;
|
flex: 10 0 auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
padding-left: 0.5em;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|
@ -89,9 +90,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-or-quote-selector {
|
.reply-or-quote-selector {
|
||||||
flex: 1 0 auto;
|
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
display: grid;
|
gap: 0 1em;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap-reverse;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +107,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
margin-left: -0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.visibility-notice {
|
.visibility-notice {
|
||||||
|
|
@ -144,10 +145,7 @@
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-upload-icon,
|
.bottom-left-button {
|
||||||
.poll-icon,
|
|
||||||
.quote-icon,
|
|
||||||
.emoji-icon {
|
|
||||||
font-size: 1.85em;
|
font-size: 1.85em;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
@ -210,14 +208,14 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input.form-post-body {
|
textarea.input.form-post-body {
|
||||||
// TODO: make a resizable textarea component?
|
// TODO: make a resizable textarea component?
|
||||||
box-sizing: content-box; // needed for easier computation of dynamic size
|
box-sizing: content-box; // needed for easier computation of dynamic size
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: min-height 200ms 100ms;
|
transition: min-height 200ms 100ms;
|
||||||
// stock padding + 1 line of text (for counter)
|
// stock padding + 1 line of text (for counter)
|
||||||
padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em);
|
padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em);
|
||||||
padding-right: 1.5em;
|
padding-right: 0.5em;
|
||||||
// two lines of text
|
// two lines of text
|
||||||
height: calc(var(--post-line-height) * 1em);
|
height: calc(var(--post-line-height) * 1em);
|
||||||
min-height: calc(var(--post-line-height) * 1em);
|
min-height: calc(var(--post-line-height) * 1em);
|
||||||
|
|
@ -241,9 +239,11 @@
|
||||||
.character-counter {
|
.character-counter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 2.2em;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0 0.5em;
|
margin: 0;
|
||||||
|
line-height: 2.2em;
|
||||||
|
height: 2.2em;
|
||||||
|
|
||||||
&.error {
|
&.error {
|
||||||
color: var(--cRed);
|
color: var(--cRed);
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!disablePreview"
|
v-if="!disablePreview"
|
||||||
class="preview-heading faint"
|
class="preview-heading"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="preview-toggle faint"
|
class="preview-toggle faint"
|
||||||
|
|
@ -110,34 +110,24 @@
|
||||||
<div
|
<div
|
||||||
v-if="quotable"
|
v-if="quotable"
|
||||||
role="radiogroup"
|
role="radiogroup"
|
||||||
class="btn-group reply-or-quote-selector"
|
class="reply-or-quote-selector"
|
||||||
>
|
>
|
||||||
<button
|
<Checkbox
|
||||||
:id="`reply-or-quote-option-${randomSeed}-reply`"
|
v-model="quoteThreadToggled"
|
||||||
class="btn button-default reply-or-quote-option"
|
:radio="true"
|
||||||
:class="{ toggled: !quoteThreadToggled }"
|
|
||||||
tabindex="0"
|
|
||||||
role="radio"
|
|
||||||
:disabled="quoteFormVisible"
|
:disabled="quoteFormVisible"
|
||||||
:aria-labelledby="`reply-or-quote-option-${randomSeed}-reply`"
|
|
||||||
:aria-checked="!newStatus.quote.thread"
|
|
||||||
@click="setQuoteThread(false)"
|
|
||||||
>
|
|
||||||
{{ $t('post_status.reply_option') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
:id="`reply-or-quote-option-${randomSeed}-quote`"
|
|
||||||
class="btn button-default reply-or-quote-option"
|
|
||||||
:class="{ toggled: quoteThreadToggled }"
|
|
||||||
tabindex="0"
|
|
||||||
role="radio"
|
|
||||||
:disabled="quoteFormVisible"
|
|
||||||
:aria-labelledby="`reply-or-quote-option-${randomSeed}-quote`"
|
|
||||||
:aria-checked="newStatus.quote.thread"
|
|
||||||
@click="setQuoteThread(true)"
|
|
||||||
>
|
>
|
||||||
{{ $t('post_status.quote_option') }}
|
{{ $t('post_status.quote_option') }}
|
||||||
</button>
|
</Checkbox>
|
||||||
|
<Checkbox
|
||||||
|
role="radio"
|
||||||
|
:radio="true"
|
||||||
|
:model-value="!quoteThreadToggled"
|
||||||
|
:disabled="quoteFormVisible"
|
||||||
|
@update:model-value="e => quoteThreadToggled = !e"
|
||||||
|
>
|
||||||
|
{{ $t('post_status.reply_option') }}
|
||||||
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -266,18 +256,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<poll-form
|
<PollForm
|
||||||
v-if="pollsAvailable"
|
v-if="pollsAvailable"
|
||||||
ref="pollForm"
|
ref="pollForm"
|
||||||
:visible="pollFormVisible"
|
:visible="pollFormVisible"
|
||||||
:params="newStatus.poll"
|
:params="newStatus.poll"
|
||||||
/>
|
/>
|
||||||
<quote-form
|
<QuoteForm
|
||||||
v-if="quotingAvailable"
|
v-if="quotingAvailable"
|
||||||
|
:id="newStatus.quote.id"
|
||||||
ref="quoteForm"
|
ref="quoteForm"
|
||||||
:visible="quoteFormVisible"
|
:visible="quoteFormVisible"
|
||||||
:reply="isReply"
|
:url="newStatus.quote.url"
|
||||||
:params="newStatus.quote"
|
@update:url="url => newStatus.quote.url = url"
|
||||||
|
@update:id="id => newStatus.quote.id = id"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-if="!disableDraft && shouldAutoSaveDraft"
|
v-if="!disableDraft && shouldAutoSaveDraft"
|
||||||
|
|
@ -292,7 +284,7 @@
|
||||||
<div class="form-bottom-left">
|
<div class="form-bottom-left">
|
||||||
<media-upload
|
<media-upload
|
||||||
ref="mediaUpload"
|
ref="mediaUpload"
|
||||||
class="media-upload-icon"
|
class="bottom-left-button media-upload-icon"
|
||||||
:drop-files="dropFiles"
|
:drop-files="dropFiles"
|
||||||
:disabled="uploadFileLimitReached"
|
:disabled="uploadFileLimitReached"
|
||||||
@uploading="startedUploadingFiles"
|
@uploading="startedUploadingFiles"
|
||||||
|
|
@ -302,8 +294,8 @@
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="pollsAvailable"
|
v-if="pollsAvailable"
|
||||||
class="poll-icon button-unstyled"
|
class="bottom-left-button poll-icon button-unstyled"
|
||||||
:class="{ selected: pollFormVisible }"
|
:class="{ toggled: pollFormVisible }"
|
||||||
:title="$t('polls.add_poll')"
|
:title="$t('polls.add_poll')"
|
||||||
@click="togglePollForm"
|
@click="togglePollForm"
|
||||||
>
|
>
|
||||||
|
|
@ -311,9 +303,9 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="quotingAvailable"
|
v-if="quotingAvailable"
|
||||||
class="quote-icon button-unstyled"
|
class="bottom-left-button quote-icon button-unstyled"
|
||||||
:disabled="newStatus.quote.thread"
|
:disabled="quoteThreadToggled"
|
||||||
:class="{ selected: quoteFormVisible }"
|
:class="{ toggled: quoteFormVisible }"
|
||||||
:title="$t('tool_tip.add_quote')"
|
:title="$t('tool_tip.add_quote')"
|
||||||
@click="toggleQuoteForm"
|
@click="toggleQuoteForm"
|
||||||
>
|
>
|
||||||
|
|
@ -389,9 +381,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="error"
|
v-if="error"
|
||||||
class="alert error"
|
class="alert error -dismissible"
|
||||||
>
|
>
|
||||||
Error: {{ error }}
|
<span>
|
||||||
|
{{ error }}
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
@click="clearError"
|
@click="clearError"
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,20 @@ export default {
|
||||||
visible: {
|
visible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
reply: {
|
url: {
|
||||||
type: Boolean,
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
params: {
|
id: {
|
||||||
type: Object,
|
type: String,
|
||||||
required: true,
|
required: false,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
text: this.params.url,
|
text: this.url,
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
debounceSetQuote: debounce((value) => {
|
debounceSetQuote: debounce((value) => {
|
||||||
|
|
@ -34,16 +37,15 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.params.url && !this.params.id) {
|
if (this.url && !this.id) {
|
||||||
this.fetchStatus(this.params.url)
|
this.fetchStatus(this.url)
|
||||||
} else if (this.params.id) {
|
} else if (this.id) {
|
||||||
this.text =
|
this.text =
|
||||||
window.location.protocol +
|
window.location.protocol +
|
||||||
'//' +
|
'//' +
|
||||||
this.instanceHost +
|
this.instanceHost +
|
||||||
'/notice/' +
|
'/notice/' +
|
||||||
this.params.id
|
this.id
|
||||||
this.params.url = this.text
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -56,22 +58,23 @@ export default {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
quoteVisible() {
|
quoteVisible() {
|
||||||
return (!!this.params.id || this.loading) && !this.error
|
return (!!this.id || this.loading) && !this.error
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
text(value) {
|
text(value) {
|
||||||
this.debounceSetQuote(value)
|
this.debounceSetQuote(value)
|
||||||
|
this.$emit('update:url', value)
|
||||||
},
|
},
|
||||||
visible(value) {
|
visible(value) {
|
||||||
if (value && this.params.url) {
|
if (value && this.url) {
|
||||||
this.fetchStatus(this.params.url)
|
this.fetchStatus(this.url)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clear() {
|
clear() {
|
||||||
this.text = this.params.url
|
this.text = this.url
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.error = false
|
this.error = false
|
||||||
},
|
},
|
||||||
|
|
@ -79,16 +82,15 @@ export default {
|
||||||
this.loading = value
|
this.loading = value
|
||||||
},
|
},
|
||||||
handleError(error) {
|
handleError(error) {
|
||||||
this.params.id = null
|
this.id = null
|
||||||
this.error = !!error
|
this.error = !!error
|
||||||
},
|
},
|
||||||
fetchStatus(value) {
|
fetchStatus(value) {
|
||||||
this.params.url = value
|
|
||||||
this.error = false
|
this.error = false
|
||||||
|
|
||||||
const notice = this.noticeRegex.exec(value)
|
const notice = this.noticeRegex.exec(value)
|
||||||
if (notice && notice.length === 4) {
|
if (notice && notice.length === 4) {
|
||||||
this.params.id = notice[3]
|
this.$emit('update:id', notice[3])
|
||||||
} else if (value) {
|
} else if (value) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$store
|
this.$store
|
||||||
|
|
@ -101,7 +103,7 @@ export default {
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data && data.statuses && data.statuses.length === 1) {
|
if (data && data.statuses && data.statuses.length === 1) {
|
||||||
this.params.id = data.statuses[0].id
|
this.$emit('update:id', data.statuses[0].id)
|
||||||
} else {
|
} else {
|
||||||
this.handleError(true)
|
this.handleError(true)
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +113,7 @@ export default {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.params.id = null
|
this.$emit('update:id', null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<Quote
|
<Quote
|
||||||
:status-id="params.id"
|
:status-id="id"
|
||||||
:status-url="params.url"
|
:status-url="url"
|
||||||
:status-visible="quoteVisible"
|
:status-visible="quoteVisible"
|
||||||
:initially-expanded="true"
|
:initially-expanded="true"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,12 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
// Allow wide emoji (max 3:1 ratio)
|
||||||
|
allowNonSquareEmoji: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// NEVER EVER TOUCH DATA INSIDE RENDER
|
// NEVER EVER TOUCH DATA INSIDE RENDER
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -322,7 +328,13 @@ export default {
|
||||||
// slots updated -> rerender -> emit -> update up the tree -> rerender -> ...
|
// slots updated -> rerender -> emit -> update up the tree -> rerender -> ...
|
||||||
// at least until vue3?
|
// at least until vue3?
|
||||||
const result = (
|
const result = (
|
||||||
<span class={['RichContent', this.faint ? '-faint' : '']}>
|
<span
|
||||||
|
class={[
|
||||||
|
'RichContent',
|
||||||
|
this.faint ? '-faint' : '',
|
||||||
|
this.allowNonSquareEmoji ? '-allow-non-square-emoji' : '',
|
||||||
|
]}
|
||||||
|
>
|
||||||
{this.collapse
|
{this.collapse
|
||||||
? pass2.map((x) => {
|
? pass2.map((x) => {
|
||||||
if (!Array.isArray(x)) return x.replace(/\n/g, ' ')
|
if (!Array.isArray(x)) return x.replace(/\n/g, ' ')
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,14 @@
|
||||||
height: var(--emoji-size, 32px);
|
height: var(--emoji-size, 32px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.-allow-non-square-emoji {
|
||||||
|
.emoji {
|
||||||
|
width: auto;
|
||||||
|
max-width: calc(var(--emoji-size, 32px) * 3);
|
||||||
|
min-width: var(--emoji-size, 32px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.img,
|
.img,
|
||||||
video {
|
video {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,6 @@
|
||||||
class="cancel-icon fa-scale-110 fa-old-padding"
|
class="cancel-icon fa-scale-110 fa-old-padding"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<span class="spacer" />
|
|
||||||
<span class="spacer" />
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -61,18 +59,14 @@
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.SearchBar {
|
.SearchBar {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
&.-expanded {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-bar-input,
|
.search-bar-input,
|
||||||
.search-button {
|
.search-button {
|
||||||
height: 29px;
|
height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar-input {
|
.search-bar-input {
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,16 @@ const present = computed(() => props.modelValue[props.selectedId] != null)
|
||||||
|
|
||||||
const moveUp = async () => {
|
const moveUp = async () => {
|
||||||
const newModel = [...props.modelValue]
|
const newModel = [...props.modelValue]
|
||||||
const movable = newModel.splice(props.selectedId, 1)[0]
|
const movableId = Number(props.selectedId)
|
||||||
newModel.splice(props.selectedId - 1, 0, movable)
|
|
||||||
|
|
||||||
emit('update:modelValue', newModel)
|
const movable = newModel.slice(movableId, movableId + 1)[0]
|
||||||
|
const before = newModel.slice(0, movableId)
|
||||||
|
const after = newModel.slice(movableId + 1)
|
||||||
|
|
||||||
|
const newBefore = before.slice(0, -1)
|
||||||
|
const newAfter = [before.slice(-1)[0], ...after]
|
||||||
|
|
||||||
|
emit('update:modelValue', [...newBefore, movable, ...newAfter])
|
||||||
await nextTick()
|
await nextTick()
|
||||||
emit('update:selectedId', props.selectedId - 1)
|
emit('update:selectedId', props.selectedId - 1)
|
||||||
}
|
}
|
||||||
|
|
@ -94,12 +100,18 @@ const moveDnValid = computed(() => {
|
||||||
|
|
||||||
const moveDn = async () => {
|
const moveDn = async () => {
|
||||||
const newModel = [...props.modelValue]
|
const newModel = [...props.modelValue]
|
||||||
const movable = newModel.splice(props.selectedId.value, 1)[0]
|
const movableId = Number(props.selectedId)
|
||||||
newModel.splice(props.selectedId + 1, 0, movable)
|
|
||||||
|
|
||||||
emit('update:modelValue', newModel)
|
const movable = newModel.slice(movableId, movableId + 1)[0]
|
||||||
|
const before = newModel.slice(0, movableId)
|
||||||
|
const after = newModel.slice(movableId + 1)
|
||||||
|
|
||||||
|
const newBefore = [...before, after.slice(0, 1)[0]]
|
||||||
|
const newAfter = after.slice(1)
|
||||||
|
|
||||||
|
emit('update:modelValue', [...newBefore, movable, ...newAfter])
|
||||||
await nextTick()
|
await nextTick()
|
||||||
emit('update:selectedId', props.selectedId + 1)
|
emit('update:selectedId', movableId + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const add = async () => {
|
const add = async () => {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import ModifiedIndicator from '../helpers/modified_indicator.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import StringSetting from '../helpers/string_setting.vue'
|
import StringSetting from '../helpers/string_setting.vue'
|
||||||
|
|
||||||
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
|
||||||
|
|
@ -174,63 +175,25 @@ const EmojiTab = {
|
||||||
this.sortPackFiles(packName)
|
this.sortPackFiles(packName)
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPacksPaginated(listFunction) {
|
|
||||||
const pageSize = 25
|
|
||||||
const allPacks = {}
|
|
||||||
|
|
||||||
return listFunction({
|
|
||||||
instance: this.remotePackInstance,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 0,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
.then((data) => {
|
|
||||||
if (data.error !== undefined) {
|
|
||||||
return Promise.reject(data.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
let resultingPromise = Promise.resolve({})
|
|
||||||
for (let i = 0; i < Math.ceil(data.count / pageSize); i++) {
|
|
||||||
resultingPromise = resultingPromise
|
|
||||||
.then(() =>
|
|
||||||
listFunction({
|
|
||||||
instance: this.remotePackInstance,
|
|
||||||
page: i,
|
|
||||||
pageSize,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.then((data) => data.json())
|
|
||||||
.then((pageData) => {
|
|
||||||
if (pageData.error !== undefined) {
|
|
||||||
return Promise.reject(pageData.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(allPacks, pageData.packs)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultingPromise
|
|
||||||
})
|
|
||||||
.then(() => allPacks)
|
|
||||||
.catch((data) => {
|
|
||||||
this.displayError(data)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshPackList() {
|
refreshPackList() {
|
||||||
this.loadPacksPaginated(
|
useEmojiStore()
|
||||||
this.$store.state.api.backendInteractor.listEmojiPacks,
|
.getAdminPacks(
|
||||||
).then((allPacks) => {
|
this.remotePackInstance,
|
||||||
this.knownLocalPacks = allPacks
|
this.$store.state.api.backendInteractor.listEmojiPacks,
|
||||||
for (const name of Object.keys(this.knownLocalPacks)) {
|
)
|
||||||
this.sortPackFiles(name)
|
.then((allPacks) => {
|
||||||
}
|
this.knownLocalPacks = allPacks
|
||||||
})
|
for (const name of Object.keys(this.knownLocalPacks)) {
|
||||||
|
this.sortPackFiles(name)
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
listRemotePacks() {
|
listRemotePacks() {
|
||||||
this.loadPacksPaginated(
|
useEmojiStore()
|
||||||
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
|
.getAdminPacks(
|
||||||
)
|
this.remotePackInstance,
|
||||||
|
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
|
||||||
|
)
|
||||||
.then((allPacks) => {
|
.then((allPacks) => {
|
||||||
let inst = this.remotePackInstance
|
let inst = this.remotePackInstance
|
||||||
if (!inst.startsWith('http')) {
|
if (!inst.startsWith('http')) {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
<h3>{{ $t('admin_dash.federation.activitypub') }}</h3>
|
<h3>{{ $t('admin_dash.federation.activitypub') }}</h3>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
|
||||||
<BooleanSetting path=":pleroma.:instance.:allow_relay" />
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path=":pleroma.:activitypub.:unfollow_blocked" />
|
<BooleanSetting path=":pleroma.:activitypub.:unfollow_blocked" />
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
:trigger-attrs="{ 'aria-label': $t('settings.setting_changed') }"
|
:trigger-attrs="{ 'aria-label': $t('settings.setting_changed') }"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
|
|
||||||
<FAIcon icon="circle-question" />
|
<FAIcon icon="circle-question" />
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
|
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="desktop"
|
icon="desktop"
|
||||||
:aria-label="$t('settings.setting_local_side')"
|
:aria-label="$t('settings.setting_local_side')"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
:trigger-attrs="{ 'aria-label': $t('settings.setting_changed') }"
|
:trigger-attrs="{ 'aria-label': $t('settings.setting_changed') }"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
|
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="wrench"
|
icon="wrench"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
class="setting-label"
|
class="setting-label"
|
||||||
:class="{ 'faint': shouldBeDisabled }"
|
:class="{ 'faint': shouldBeDisabled }"
|
||||||
>
|
>
|
||||||
<ModifiedIndicator
|
<ModifiedIndicator
|
||||||
:changed="isChanged"
|
:changed="isChanged"
|
||||||
:onclick="reset"
|
:onclick="reset"
|
||||||
/>
|
/>
|
||||||
<LocalSettingIndicator :is-local="isLocalSetting" />
|
<LocalSettingIndicator :is-local="isLocalSetting" />
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
<DraftButtons v-if="!hideDraftButtons" />
|
<DraftButtons v-if="!hideDraftButtons" />
|
||||||
<template v-if="backendDescriptionLabel">
|
<template v-if="backendDescriptionLabel">
|
||||||
{{ backendDescriptionLabel + ' ' }}
|
{{ backendDescriptionLabel + ' ' }}
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
.tab-slot-wrapper {
|
.tab-slot-wrapper {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 0 1em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(1em, 1fr) minmax(min-content, 45em) minmax(1em, 1fr);
|
grid-template-columns: minmax(1em, 1fr) minmax(min-content, 45em) minmax(1em, 1fr);
|
||||||
|
|
@ -65,6 +66,8 @@
|
||||||
.tab-content-wrapper {
|
.tab-content-wrapper {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
&.-hidden {
|
&.-hidden {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,7 @@
|
||||||
|
|
||||||
p {
|
p {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
margin-left: 2em;
|
||||||
|
|
||||||
.suboptions {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidenote {
|
.sidenote {
|
||||||
|
|
@ -53,6 +50,7 @@
|
||||||
.setting-description {
|
.setting-description {
|
||||||
margin-top: 0.2em;
|
margin-top: 0.2em;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
margin-left: 0;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +64,7 @@
|
||||||
column-gap: 0.5em;
|
column-gap: 0.5em;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
padding: 0.5em 0;
|
padding: 0.5em 0;
|
||||||
|
line-height: 1.5em;
|
||||||
|
|
||||||
.setting-label {
|
.setting-label {
|
||||||
grid-area: label;
|
grid-area: label;
|
||||||
|
|
@ -96,6 +95,9 @@
|
||||||
|
|
||||||
.checkbox-indicator {
|
.checkbox-indicator {
|
||||||
grid-area: control;
|
grid-area: control;
|
||||||
|
height: 1.5em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
align-self: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.-mobile & {
|
.-mobile & {
|
||||||
|
|
@ -130,6 +132,13 @@
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
&.suboptions {
|
||||||
|
margin-left: 2em;
|
||||||
|
border-top: 1px dotted var(--border);
|
||||||
|
border-bottom: 1px dotted var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.btn:not(.dropdown-button) {
|
.btn:not(.dropdown-button) {
|
||||||
padding: 0 2em;
|
padding: 0 2em;
|
||||||
}
|
}
|
||||||
|
|
@ -207,6 +216,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
.sidenote {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* stylelint-disable no-descending-specificity */
|
/* stylelint-disable no-descending-specificity */
|
||||||
.setting-item {
|
.setting-item {
|
||||||
grid-template-columns: 1fr min-content;
|
grid-template-columns: 1fr min-content;
|
||||||
|
|
@ -221,9 +236,14 @@
|
||||||
.checkbox {
|
.checkbox {
|
||||||
.label {
|
.label {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-left: 0;
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-indicator {
|
||||||
|
order: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
|
@ -236,14 +256,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.setting-list:not(.suboptions),
|
.setting-list:not(.suboptions),
|
||||||
.option-list {
|
.option-list {
|
||||||
&.two-column {
|
&.two-column {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.UnitSetting {
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.peek {
|
&.peek {
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ const AppearanceTab = {
|
||||||
})),
|
})),
|
||||||
backgroundUploading: false,
|
backgroundUploading: false,
|
||||||
background: null,
|
background: null,
|
||||||
|
backgroundError: null,
|
||||||
backgroundPreview: null,
|
backgroundPreview: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -474,6 +475,9 @@ const AppearanceTab = {
|
||||||
resetUploadedBackground() {
|
resetUploadedBackground() {
|
||||||
this.backgroundPreview = null
|
this.backgroundPreview = null
|
||||||
},
|
},
|
||||||
|
clearBackgroundError() {
|
||||||
|
this.backgroundError = null
|
||||||
|
},
|
||||||
submitBackground(background) {
|
submitBackground(background) {
|
||||||
if (!this.backgroundPreview && background !== '') {
|
if (!this.backgroundPreview && background !== '') {
|
||||||
return
|
return
|
||||||
|
|
@ -486,8 +490,11 @@ const AppearanceTab = {
|
||||||
this.$store.commit('addNewUsers', [data])
|
this.$store.commit('addNewUsers', [data])
|
||||||
this.$store.commit('setCurrentUser', data)
|
this.$store.commit('setCurrentUser', data)
|
||||||
this.backgroundPreview = null
|
this.backgroundPreview = null
|
||||||
|
this.backgroundError = null
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.backgroundError = e
|
||||||
})
|
})
|
||||||
.catch(this.displayUploadError)
|
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.backgroundUploading = false
|
this.backgroundUploading = false
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,23 @@
|
||||||
{{ $t('settings.reset') }}
|
{{ $t('settings.reset') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="backgroundError"
|
||||||
|
class="alert error -dismissible"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ backgroundError }}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="button-unstyled"
|
||||||
|
@click="clearBackgroundError"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="fa-scale-110 fa-old-padding"
|
||||||
|
icon="times"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="!isDefaultBackground"
|
v-if="!isDefaultBackground"
|
||||||
class="btn button-default reset-button"
|
class="btn button-default reset-button"
|
||||||
|
|
@ -246,6 +263,11 @@
|
||||||
{{ $t('settings.hide_wallpaper') }}
|
{{ $t('settings.hide_wallpaper') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting path="compactProfiles">
|
||||||
|
{{ $t('settings.compact_profiles') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,16 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting path="userCardHidePersonalMarks">
|
||||||
|
{{ $t('settings.user_card_hide_personal_marks') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
<li v-if="shoutAvailable">
|
||||||
|
<BooleanSetting path="hideShoutbox">
|
||||||
|
{{ $t('settings.hide_shoutbox') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>{{ $t('settings.attachments') }}</h3>
|
<h3>{{ $t('settings.attachments') }}</h3>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
|
|
@ -77,16 +87,6 @@
|
||||||
{{ $t('settings.hide_attachments_in_convo') }}
|
{{ $t('settings.hide_attachments_in_convo') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<BooleanSetting path="userCardHidePersonalMarks">
|
|
||||||
{{ $t('settings.user_card_hide_personal_marks') }}
|
|
||||||
</BooleanSetting>
|
|
||||||
</li>
|
|
||||||
<li v-if="shoutAvailable">
|
|
||||||
<BooleanSetting path="hideShoutbox">
|
|
||||||
{{ $t('settings.hide_shoutbox') }}
|
|
||||||
</BooleanSetting>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
|
||||||
|
|
@ -9,14 +11,6 @@ const pleromaFeCommitUrl =
|
||||||
'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
||||||
|
|
||||||
const VersionTab = {
|
const VersionTab = {
|
||||||
data() {
|
|
||||||
const instance = useInstanceStore()
|
|
||||||
return {
|
|
||||||
backendVersion: instance.backendVersion,
|
|
||||||
backendRepository: instance.backendRepository,
|
|
||||||
frontendVersion: instance.frontendVersion,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
BooleanSetting,
|
BooleanSetting,
|
||||||
},
|
},
|
||||||
|
|
@ -24,6 +18,11 @@ const VersionTab = {
|
||||||
frontendVersionLink() {
|
frontendVersionLink() {
|
||||||
return pleromaFeCommitUrl + this.frontendVersion
|
return pleromaFeCommitUrl + this.frontendVersion
|
||||||
},
|
},
|
||||||
|
...mapState(useInstanceStore, [
|
||||||
|
'backendVersion',
|
||||||
|
'backendRepository',
|
||||||
|
'frontendVersion',
|
||||||
|
]),
|
||||||
...SharedComputedObject(),
|
...SharedComputedObject(),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ const FilteringTab = {
|
||||||
hide = false,
|
hide = false,
|
||||||
name = '',
|
name = '',
|
||||||
value = '',
|
value = '',
|
||||||
|
caseSensitive = false,
|
||||||
} = data
|
} = data
|
||||||
|
|
||||||
this.createFilter({
|
this.createFilter({
|
||||||
|
|
@ -66,6 +67,7 @@ const FilteringTab = {
|
||||||
hide,
|
hide,
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
|
caseSensitive,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onImportFailure(result) {
|
onImportFailure(result) {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,16 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> label.checkbox {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: subgrid;
|
||||||
|
grid-template-rows: subgrid;
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-field-value {
|
.filter-field-value {
|
||||||
|
display: flex;
|
||||||
grid-column: 2 / span 2;
|
grid-column: 2 / span 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,20 @@
|
||||||
:value="filter[1].value"
|
:value="filter[1].value"
|
||||||
@input="updateFilter(filter[0], 'value', $event.target.value)"
|
@input="updateFilter(filter[0], 'value', $event.target.value)"
|
||||||
>
|
>
|
||||||
|
{{ ' ' }}
|
||||||
|
</div>
|
||||||
|
<div class="filter-value filter-field">
|
||||||
|
<Checkbox
|
||||||
|
:id="'filterCaseSensitive' + filter[0]"
|
||||||
|
:model-value="filter[1].caseSensitive"
|
||||||
|
:name="'filterCaseSensitive' + filter[0]"
|
||||||
|
class="input-inset input-boolean case-sensitive"
|
||||||
|
@update:model-value="updateFilter(filter[0], 'caseSensitive', $event)"
|
||||||
|
>
|
||||||
|
<template #before>
|
||||||
|
{{ $t('settings.filter.case_sensitive') }}
|
||||||
|
</template>
|
||||||
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-expires filter-field">
|
<div class="filter-expires filter-field">
|
||||||
<label
|
<label
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
class="lang-selector"
|
class="lang-selector"
|
||||||
@update="val => language = val"
|
@update="val => language = val"
|
||||||
/>
|
/>
|
||||||
|
<h5>{{ $t('settings.email_language') }}</h5>
|
||||||
<interface-language-switcher
|
<interface-language-switcher
|
||||||
v-model="emailLanguage"
|
v-model="emailLanguage"
|
||||||
class="lang-selector"
|
class="lang-selector"
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,6 @@
|
||||||
:key="column"
|
:key="column"
|
||||||
:local="true"
|
:local="true"
|
||||||
:path="column + 'ColumnWidth'"
|
:path="column + 'ColumnWidth'"
|
||||||
:units="horizontalUnits"
|
|
||||||
expert="1"
|
expert="1"
|
||||||
>
|
>
|
||||||
{{ $t('settings.column_sizes_' + column) }}
|
{{ $t('settings.column_sizes_' + column) }}
|
||||||
|
|
|
||||||
|
|
@ -509,22 +509,14 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setCustomTheme() {
|
setCustomTheme() {
|
||||||
useInterfaceStore().setThemeV2({
|
useInterfaceStore().setTheme({
|
||||||
customTheme: {
|
themeFileVersion: this.selectedVersion,
|
||||||
ignore: true,
|
themeEngineVersion: CURRENT_VERSION,
|
||||||
themeFileVersion: this.selectedVersion,
|
shadows: this.shadowsLocal,
|
||||||
themeEngineVersion: CURRENT_VERSION,
|
fonts: this.fontsLocal,
|
||||||
...this.previewTheme,
|
opacity: this.currentOpacity,
|
||||||
},
|
colors: this.currentColors,
|
||||||
customThemeSource: {
|
radii: this.currentRadii,
|
||||||
themeFileVersion: this.selectedVersion,
|
|
||||||
themeEngineVersion: CURRENT_VERSION,
|
|
||||||
shadows: this.shadowsLocal,
|
|
||||||
fonts: this.fontsLocal,
|
|
||||||
opacity: this.currentOpacity,
|
|
||||||
colors: this.currentColors,
|
|
||||||
radii: this.currentRadii,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updatePreviewColors() {
|
updatePreviewColors() {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
import UnitSetting from '../helpers/unit_setting.vue'
|
||||||
|
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
|
|
@ -62,6 +63,7 @@ const PostsTab = {
|
||||||
ChoiceSetting,
|
ChoiceSetting,
|
||||||
IntegerSetting,
|
IntegerSetting,
|
||||||
FontControl,
|
FontControl,
|
||||||
|
UnitSetting,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...SharedComputedObject(),
|
...SharedComputedObject(),
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,11 @@
|
||||||
{{ $t('settings.stop_gifs') }}
|
{{ $t('settings.stop_gifs') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting path="nonSquareEmoji">
|
||||||
|
{{ $t('settings.non_square_emoji') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
:local="true"
|
:local="true"
|
||||||
|
|
|
||||||
|
|
@ -492,7 +492,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
return deserializeShadow(shadow)
|
return deserializeShadow(shadow)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e)
|
console.warn('Failed to deserialize shadow', e)
|
||||||
return shadow
|
return shadow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -652,7 +652,7 @@ export default {
|
||||||
return rgb2hex(computedColor)
|
return rgb2hex(computedColor)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e)
|
console.warn('failed to get computed color', e)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ export default {
|
||||||
if (computedColor) return rgb2hex(computedColor)
|
if (computedColor) return rgb2hex(computedColor)
|
||||||
return null
|
return null
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e)
|
console.warn('Failed to get fallback color', e)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ const SideDrawer = {
|
||||||
return unseenNotificationsFromStore(
|
return unseenNotificationsFromStore(
|
||||||
this.$store,
|
this.$store,
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
|
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
unseenNotificationsCount() {
|
unseenNotificationsCount() {
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ const Status = {
|
||||||
'simpleTree',
|
'simpleTree',
|
||||||
'showOtherRepliesAsButton',
|
'showOtherRepliesAsButton',
|
||||||
'dive',
|
'dive',
|
||||||
|
'ignoreMute',
|
||||||
|
|
||||||
'controlledThreadDisplayStatus',
|
'controlledThreadDisplayStatus',
|
||||||
'controlledToggleThreadDisplay',
|
'controlledToggleThreadDisplay',
|
||||||
|
|
@ -166,7 +167,7 @@ const Status = {
|
||||||
'controlledMediaPlaying',
|
'controlledMediaPlaying',
|
||||||
'controlledSetMediaPlaying',
|
'controlledSetMediaPlaying',
|
||||||
],
|
],
|
||||||
emits: ['interacted', 'goto', 'toggleExpanded'],
|
emits: ['goto', 'toggleExpanded'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
uncontrolledReplying: false,
|
uncontrolledReplying: false,
|
||||||
|
|
@ -187,6 +188,9 @@ const Status = {
|
||||||
!this.inConversation
|
!this.inConversation
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return this.mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
repeaterClass() {
|
repeaterClass() {
|
||||||
const user = this.statusoid.user
|
const user = this.statusoid.user
|
||||||
return highlightClass(user)
|
return highlightClass(user)
|
||||||
|
|
@ -345,6 +349,7 @@ const Status = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
muted() {
|
muted() {
|
||||||
|
if (this.ignoreMute) return false
|
||||||
if (this.statusoid.user.id === this.currentUser.id) return false
|
if (this.statusoid.user.id === this.currentUser.id) return false
|
||||||
return !this.unmuted && !this.shouldNotMute && this.muteReasons.length > 0
|
return !this.unmuted && !this.shouldNotMute && this.muteReasons.length > 0
|
||||||
},
|
},
|
||||||
|
|
@ -366,6 +371,7 @@ const Status = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
shouldNotMute() {
|
shouldNotMute() {
|
||||||
|
if (this.ignoreMute) return true
|
||||||
if (this.isFocused) return true
|
if (this.isFocused) return true
|
||||||
const { status } = this
|
const { status } = this
|
||||||
const { reblog } = status
|
const { reblog } = status
|
||||||
|
|
@ -557,11 +563,9 @@ const Status = {
|
||||||
this.error = error
|
this.error = error
|
||||||
},
|
},
|
||||||
clearError() {
|
clearError() {
|
||||||
this.$emit('interacted')
|
|
||||||
this.error = undefined
|
this.error = undefined
|
||||||
},
|
},
|
||||||
toggleReplying() {
|
toggleReplying() {
|
||||||
this.$emit('interacted')
|
|
||||||
if (this.replying) {
|
if (this.replying) {
|
||||||
this.$refs.postStatusForm.requestClose()
|
this.$refs.postStatusForm.requestClose()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -23,21 +23,18 @@
|
||||||
.status-container {
|
.status-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--status-margin);
|
padding: var(--status-margin);
|
||||||
|
gap: var(--status-margin);
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-repeat {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin {
|
.pin {
|
||||||
padding: var(--status-margin) var(--status-margin) 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
._misclick-prevention & {
|
._misclick-prevention & {
|
||||||
|
|
@ -50,12 +47,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-side {
|
.left-side {
|
||||||
margin-right: var(--status-margin);
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-side {
|
.right-side {
|
||||||
flex: 1;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.usercard {
|
.usercard {
|
||||||
|
|
@ -230,29 +226,47 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.repeat-info {
|
.repeat-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
padding: 0.4em var(--status-margin);
|
padding: 0.4em var(--status-margin);
|
||||||
|
|
||||||
.repeat-icon {
|
.repeater-avatar {
|
||||||
color: var(--cGreen);
|
flex: 0 0 1.5em;
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
margin-left: 2em; // 3.5 (poster avatar size) - 1.5 (repeater avatar size)
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.repeater-avatar {
|
.right-side {
|
||||||
border-radius: var(--roundness);
|
display: flex;
|
||||||
margin-left: 2em; // 3.5 (poster avatar size) - 1.5 (repeater avatar size)
|
flex: 1 1 auto;
|
||||||
width: 1.5em;
|
overflow-x: hidden;
|
||||||
height: 1.5em;
|
text-overflow: ellipsis;
|
||||||
}
|
margin-right: 0;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
.repeater-name {
|
.repeater-name {
|
||||||
text-overflow: ellipsis;
|
flex: 0 1 auto;
|
||||||
margin-right: 0;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.emoji {
|
.repeat-label {
|
||||||
width: 1em;
|
white-space: nowrap;
|
||||||
height: 1em;
|
flex: 0 0 auto;
|
||||||
vertical-align: middle;
|
|
||||||
object-fit: contain;
|
.repeat-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
color: var(--cGreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
vertical-align: middle;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,21 +385,4 @@
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (width <= 800px) {
|
|
||||||
.repeater-avatar {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-avatar {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
|
|
||||||
// TODO define those other way somehow?
|
|
||||||
&.-compact {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,16 +46,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div
|
|
||||||
v-if="showPinned"
|
|
||||||
class="pin"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
icon="thumbtack"
|
|
||||||
class="faint"
|
|
||||||
/>
|
|
||||||
<span class="faint">{{ $t('status.pinned') }}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="retweet && !noHeading && !inConversation"
|
v-if="retweet && !noHeading && !inConversation"
|
||||||
:class="[repeaterClass, { highlighted: repeaterStyle }]"
|
:class="[repeaterClass, { highlighted: repeaterStyle }]"
|
||||||
|
|
@ -80,6 +70,7 @@
|
||||||
<RichContent
|
<RichContent
|
||||||
:html="retweeterHtml"
|
:html="retweeterHtml"
|
||||||
:emoji="retweeterUser.emoji"
|
:emoji="retweeterUser.emoji"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
:is-local="retweeterUser.is_local"
|
:is-local="retweeterUser.is_local"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -88,13 +79,14 @@
|
||||||
:to="retweeterProfileLink"
|
:to="retweeterProfileLink"
|
||||||
>{{ retweeter }}</router-link>
|
>{{ retweeter }}</router-link>
|
||||||
</bdi>
|
</bdi>
|
||||||
{{ ' ' }}
|
<div class="repeat-label">
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="retweet"
|
icon="retweet"
|
||||||
class="repeat-icon"
|
class="repeat-icon"
|
||||||
:title="$t('tool_tip.repeat')"
|
:title="$t('tool_tip.repeat')"
|
||||||
/>
|
/>
|
||||||
{{ $t('timeline.repeated') }}
|
{{ $t('timeline.repeated') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -152,6 +144,7 @@
|
||||||
<RichContent
|
<RichContent
|
||||||
:html="status.user.name"
|
:html="status.user.name"
|
||||||
:emoji="status.user.emoji"
|
:emoji="status.user.emoji"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
:is-local="status.user.is_local"
|
:is-local="status.user.is_local"
|
||||||
/>
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
@ -176,6 +169,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="heading-right">
|
<span class="heading-right">
|
||||||
|
<span
|
||||||
|
v-if="showPinned"
|
||||||
|
class="pin"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
icon="thumbtack"
|
||||||
|
class="faint"
|
||||||
|
/>
|
||||||
|
<span class="faint">{{ $t('status.pinned') }}</span>
|
||||||
|
</span>
|
||||||
<router-link
|
<router-link
|
||||||
class="timeago faint"
|
class="timeago faint"
|
||||||
:to="{ name: 'conversation', params: { id: status.id } }"
|
:to="{ name: 'conversation', params: { id: status.id } }"
|
||||||
|
|
@ -519,7 +522,6 @@
|
||||||
:status="status"
|
:status="status"
|
||||||
:replying="replying"
|
:replying="replying"
|
||||||
@toggle-replying="toggleReplying"
|
@toggle-replying="toggleReplying"
|
||||||
@interacted="e => $emit('interacted')"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import StatusBookmarkFolderMenu from 'src/components/status_bookmark_folder_menu
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,8 +14,8 @@ import {
|
||||||
import {
|
import {
|
||||||
faBookmark,
|
faBookmark,
|
||||||
faCheck,
|
faCheck,
|
||||||
|
faChevronDown,
|
||||||
faChevronRight,
|
faChevronRight,
|
||||||
faChevronUp,
|
|
||||||
faExternalLinkAlt,
|
faExternalLinkAlt,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
faHistory,
|
faHistory,
|
||||||
|
|
@ -38,7 +39,7 @@ library.add(
|
||||||
faWrench,
|
faWrench,
|
||||||
|
|
||||||
faChevronRight,
|
faChevronRight,
|
||||||
faChevronUp,
|
faChevronDown,
|
||||||
|
|
||||||
faReply,
|
faReply,
|
||||||
faRetweet,
|
faRetweet,
|
||||||
|
|
@ -67,7 +68,6 @@ export default {
|
||||||
'doAction',
|
'doAction',
|
||||||
'outerClose',
|
'outerClose',
|
||||||
],
|
],
|
||||||
emits: ['interacted'],
|
|
||||||
components: {
|
components: {
|
||||||
StatusBookmarkFolderMenu,
|
StatusBookmarkFolderMenu,
|
||||||
EmojiPicker,
|
EmojiPicker,
|
||||||
|
|
@ -97,6 +97,9 @@ export default {
|
||||||
return !useInstanceCapabilitiesStore()
|
return !useInstanceCapabilitiesStore()
|
||||||
.pleromaCustomEmojiReactionsAvailable
|
.pleromaCustomEmojiReactionsAvailable
|
||||||
},
|
},
|
||||||
|
hidePostStats() {
|
||||||
|
return useMergedConfigStore().mergedConfig.hidePostStats
|
||||||
|
},
|
||||||
buttonInnerClass() {
|
buttonInnerClass() {
|
||||||
return [
|
return [
|
||||||
this.button.name + '-button',
|
this.button.name + '-button',
|
||||||
|
|
@ -128,6 +131,12 @@ export default {
|
||||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onShowEmojiPicker() {
|
||||||
|
this.$emit('emojiPickerShown', true)
|
||||||
|
},
|
||||||
|
onHideEmojiPicker() {
|
||||||
|
this.$emit('emojiPickerShown', false)
|
||||||
|
},
|
||||||
doActionWrap(
|
doActionWrap(
|
||||||
button,
|
button,
|
||||||
close = () => {
|
close = () => {
|
||||||
|
|
@ -138,9 +147,8 @@ export default {
|
||||||
this.button.interactive ? !this.button.interactive(this.funcArg) : false
|
this.button.interactive ? !this.button.interactive(this.funcArg) : false
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
this.$emit('interacted')
|
|
||||||
if (button.name === 'emoji') {
|
if (button.name === 'emoji') {
|
||||||
this.$refs.picker.showPicker()
|
this.$refs.picker.togglePicker()
|
||||||
} else {
|
} else {
|
||||||
this.animationState = true
|
this.animationState = true
|
||||||
this.getComponent(button) === 'button' && this.doAction(button)
|
this.getComponent(button) === 'button' && this.doAction(button)
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,24 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 1.5em;
|
border: 2px solid transparent;
|
||||||
|
|
||||||
|
.chevron-popover {
|
||||||
|
.popover-trigger-button {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.action-counter {
|
.action-counter {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-left: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button-inner,
|
.action-button-inner,
|
||||||
.extra-button {
|
.extra-button {
|
||||||
margin: -0.5em;
|
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
|
|
@ -26,27 +31,13 @@
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background-color: var(--icon);
|
background-color: var(--icon);
|
||||||
margin-left: 0.75em;
|
opacity: 0.75;
|
||||||
margin-right: 0.125em;
|
margin: 0.5em 0;
|
||||||
}
|
|
||||||
|
|
||||||
&.-pin {
|
|
||||||
margin: calc(-2px - 0.25em);
|
|
||||||
padding: 0.25em;
|
|
||||||
border: 2px dashed var(--icon);
|
|
||||||
border-radius: var(--roundness);
|
|
||||||
grid-template-columns: minmax(max-content, 1fr) auto;
|
|
||||||
|
|
||||||
.chevron-icon,
|
|
||||||
.extra-button,
|
|
||||||
.separator {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button-inner {
|
.action-button-inner {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 1em;
|
grid-gap: 0.125em;
|
||||||
grid-template-columns: max-content;
|
grid-template-columns: max-content;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
grid-auto-columns: max-content;
|
grid-auto-columns: max-content;
|
||||||
|
|
@ -72,6 +63,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.-pin {
|
||||||
|
border: 2px dashed var(--icon);
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
grid-template-columns: minmax(max-content, 1fr) auto;
|
||||||
|
|
||||||
|
.action-button-inner {
|
||||||
|
opacity: 0.8;
|
||||||
|
padding-right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chevron-icon,
|
||||||
|
.extra-button,
|
||||||
|
.separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-with-extra {
|
||||||
|
.action-button-inner,
|
||||||
|
.extra-button {
|
||||||
|
padding-left: 0.25em;
|
||||||
|
padding-right: 0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
|
|
@ -106,7 +123,7 @@
|
||||||
&.-extra {
|
&.-extra {
|
||||||
.action-counter {
|
.action-counter {
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
margin-right: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chevron-icon {
|
.chevron-icon {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
:class="buttonInnerClass"
|
:class="buttonInnerClass"
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
type="button"
|
type="button"
|
||||||
|
placement="bottom"
|
||||||
:title="$t(button.label(funcArg))"
|
:title="$t(button.label(funcArg))"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:tabindex="0"
|
:tabindex="0"
|
||||||
|
|
@ -26,21 +27,21 @@
|
||||||
/>
|
/>
|
||||||
<template v-if="!buttonClass.disabled && (!button.interactive || button?.interactive(funcArg)) && button.toggleable?.(funcArg) && button.active">
|
<template v-if="!buttonClass.disabled && (!button.interactive || button?.interactive(funcArg)) && button.toggleable?.(funcArg) && button.active">
|
||||||
<FAIcon
|
<FAIcon
|
||||||
v-if="button.active(funcArg)"
|
v-if="button.active(funcArg) && button.activeIndicator?.() !== null"
|
||||||
class="active-marker"
|
class="active-marker"
|
||||||
transform="shrink-6 up-9 right-15"
|
transform="shrink-6 up-9 left-12"
|
||||||
:icon="button.activeIndicator?.(funcArg) || 'check'"
|
:icon="button.activeIndicator?.(funcArg) || 'check'"
|
||||||
/>
|
/>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
v-if="!button.active(funcArg)"
|
v-if="!button.active(funcArg)"
|
||||||
class="focus-marker"
|
class="focus-marker"
|
||||||
transform="shrink-6 up-9 right-15"
|
transform="shrink-6 up-9 left-12"
|
||||||
:icon="button.openIndicator?.(funcArg) || 'plus'"
|
:icon="button.openIndicator?.(funcArg) || 'plus'"
|
||||||
/>
|
/>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
v-else
|
v-else
|
||||||
class="focus-marker"
|
class="focus-marker"
|
||||||
transform="shrink-6 up-9 right-15"
|
transform="shrink-6 up-9 left-12"
|
||||||
:icon="button.closeIndicator?.(funcArg) || 'minus'"
|
:icon="button.closeIndicator?.(funcArg) || 'minus'"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -54,13 +55,12 @@
|
||||||
<FAIcon
|
<FAIcon
|
||||||
v-if="button.dropdown?.()"
|
v-if="button.dropdown?.()"
|
||||||
class="chevron-icon"
|
class="chevron-icon"
|
||||||
size="lg"
|
:icon="extra ? 'chevron-right' : 'chevron-down'"
|
||||||
:icon="extra ? 'chevron-right' : 'chevron-up'"
|
|
||||||
fixed-width
|
fixed-width
|
||||||
/>
|
/>
|
||||||
</component>
|
</component>
|
||||||
<span
|
<span
|
||||||
v-if="button.counter?.(funcArg) > 0"
|
v-if="!hidePostStats && button.counter?.(funcArg) > 0"
|
||||||
class="action-counter"
|
class="action-counter"
|
||||||
>
|
>
|
||||||
{{ button.counter?.(funcArg) }}
|
{{ button.counter?.(funcArg) }}
|
||||||
|
|
@ -71,16 +71,16 @@
|
||||||
/>
|
/>
|
||||||
<Popover
|
<Popover
|
||||||
v-if="button.name === 'bookmark'"
|
v-if="button.name === 'bookmark'"
|
||||||
|
class="chevron-popover"
|
||||||
:trigger="extra ? 'hover' : 'click'"
|
:trigger="extra ? 'hover' : 'click'"
|
||||||
:placement="extra ? 'right' : 'top'"
|
:placement="extra ? 'right' : 'bottom'"
|
||||||
:offset="extra ? { x: 10 } : { y: 10 }"
|
:offset="extra ? { x: 10 } : { y: 10 }"
|
||||||
:trigger-attrs="{ class: 'extra-button' }"
|
:trigger-attrs="{ class: 'extra-button' }"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="chevron-icon"
|
class="chevron-icon"
|
||||||
size="lg"
|
:icon="extra ? 'chevron-right' : 'chevron-down'"
|
||||||
:icon="extra ? 'chevron-right' : 'chevron-up'"
|
|
||||||
fixed-width
|
fixed-width
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -100,6 +100,8 @@
|
||||||
:hide-custom-emoji="hideCustomEmoji"
|
:hide-custom-emoji="hideCustomEmoji"
|
||||||
class="emoji-picker-panel"
|
class="emoji-picker-panel"
|
||||||
@emoji="addReaction"
|
@emoji="addReaction"
|
||||||
|
@show="onShowEmojiPicker"
|
||||||
|
@close="onHideEmojiPicker"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
UserTimedFilterModal,
|
UserTimedFilterModal,
|
||||||
},
|
},
|
||||||
props: ['button', 'status'],
|
props: ['button', 'status'],
|
||||||
emits: ['interacted'],
|
emits: ['emojiPickerShown'],
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.button.name === 'mute') {
|
if (this.button.name === 'mute') {
|
||||||
this.$store.dispatch('fetchDomainMutes')
|
this.$store.dispatch('fetchDomainMutes')
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
v-if="button.dropdown?.()"
|
v-if="button.dropdown?.()"
|
||||||
:trigger="$attrs.extra ? 'hover' : 'click'"
|
:trigger="$attrs.extra ? 'hover' : 'click'"
|
||||||
:offset="{ y: 5 }"
|
:offset="{ y: 5 }"
|
||||||
:placement="$attrs.extra ? 'right' : 'top'"
|
:placement="$attrs.extra ? 'right' : 'bottom'"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
:button="button"
|
:button="button"
|
||||||
:status="status"
|
:status="status"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@interacted="e => $emit('interacted')"
|
@emojiPickerShown="e => $emit('emojiPickerShown', e)"
|
||||||
/>
|
/>
|
||||||
<teleport to="#modal">
|
<teleport to="#modal">
|
||||||
<MuteConfirm
|
<MuteConfirm
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export const BUTTONS = [
|
||||||
anonLink: true,
|
anonLink: true,
|
||||||
toggleable: true,
|
toggleable: true,
|
||||||
closeIndicator: 'times',
|
closeIndicator: 'times',
|
||||||
activeIndicator: 'none',
|
activeIndicator: null,
|
||||||
action({ emit }) {
|
action({ emit }) {
|
||||||
emit('toggleReplying')
|
emit('toggleReplying')
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
|
@ -97,6 +97,9 @@ export const BUTTONS = [
|
||||||
name: 'emoji',
|
name: 'emoji',
|
||||||
label: 'tool_tip.add_reaction',
|
label: 'tool_tip.add_reaction',
|
||||||
icon: ['far', 'smile-beam'],
|
icon: ['far', 'smile-beam'],
|
||||||
|
interactive: () => true,
|
||||||
|
active: ({ emojiPickerShown }) => emojiPickerShown,
|
||||||
|
toggleable: true,
|
||||||
anonLink: true,
|
anonLink: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ library.add(faEllipsisH)
|
||||||
|
|
||||||
const StatusActionButtons = {
|
const StatusActionButtons = {
|
||||||
props: ['status', 'replying'],
|
props: ['status', 'replying'],
|
||||||
emits: ['toggleReplying', 'interacted', 'onSuccess', 'onError'],
|
emits: ['toggleReplying', 'onSuccess', 'onError'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showPin: false,
|
showPin: false,
|
||||||
|
|
@ -28,6 +28,7 @@ const StatusActionButtons = {
|
||||||
/* no-op */
|
/* no-op */
|
||||||
},
|
},
|
||||||
randomSeed: genRandomSeed(),
|
randomSeed: genRandomSeed(),
|
||||||
|
emojiPickerShown: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -56,6 +57,7 @@ const StatusActionButtons = {
|
||||||
return {
|
return {
|
||||||
status: this.status,
|
status: this.status,
|
||||||
replying: this.replying,
|
replying: this.replying,
|
||||||
|
emojiPickerShown: this.emojiPickerShown,
|
||||||
emit: this.$emit,
|
emit: this.$emit,
|
||||||
dispatch: this.$store.dispatch,
|
dispatch: this.$store.dispatch,
|
||||||
state: this.$store.state,
|
state: this.$store.state,
|
||||||
|
|
@ -107,6 +109,9 @@ const StatusActionButtons = {
|
||||||
onExtraClose() {
|
onExtraClose() {
|
||||||
this.showPin = false
|
this.showPin = false
|
||||||
},
|
},
|
||||||
|
onEmojiPickerShown(state) {
|
||||||
|
this.emojiPickerShown = state
|
||||||
|
},
|
||||||
isPinned(button) {
|
isPinned(button) {
|
||||||
return this.pinnedItems.has(button.name)
|
return this.pinnedItems.has(button.name)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@
|
||||||
.StatusActionButtons {
|
.StatusActionButtons {
|
||||||
.quick-action-buttons {
|
.quick-action-buttons {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(10%, 3em));
|
margin-left: -0.5em;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(3.75em, 10%));
|
||||||
grid-auto-flow: row dense;
|
grid-auto-flow: row dense;
|
||||||
grid-auto-rows: 1fr;
|
grid-auto-rows: 1fr;
|
||||||
grid-gap: 1.25em 0;
|
grid-gap: 0.5em 0.1em;
|
||||||
margin-top: var(--status-margin);
|
margin-top: var(--status-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin-action-button {
|
.pin-action-button {
|
||||||
margin: -0.5em;
|
display: flex;
|
||||||
|
z-index: 1;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// popover
|
// popover
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="StatusActionButtons">
|
<div class="StatusActionButtons">
|
||||||
<span class="quick-action-buttons">
|
<span
|
||||||
|
class="quick-action-buttons"
|
||||||
|
:class="{ '-pin': showPin }"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-for="button in quickButtons"
|
v-for="button in quickButtons"
|
||||||
:key="button.name"
|
:key="button.name"
|
||||||
class="quick-action"
|
class="quick-action"
|
||||||
:class="{ '-pin': showPin, '-toggle': button.dropdown?.() }"
|
:class="{ '-pin': showPin, '-toggle': button.dropdown?.(), '-with-extra': button.name === 'bookmark' }"
|
||||||
>
|
>
|
||||||
<ActionButtonContainer
|
<ActionButtonContainer
|
||||||
:class="{ '-pin': showPin }"
|
:class="{ '-pin': showPin }"
|
||||||
|
|
@ -17,7 +20,7 @@
|
||||||
:get-component="getComponent"
|
:get-component="getComponent"
|
||||||
:close="() => { /* no-op */ }"
|
:close="() => { /* no-op */ }"
|
||||||
:do-action="doAction"
|
:do-action="doAction"
|
||||||
@interacted="e => $emit('interacted')"
|
@emojiPickerShown="onEmojiPickerShown"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
|
|
@ -30,7 +33,6 @@
|
||||||
<FAIcon
|
<FAIcon
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
fixed-width
|
fixed-width
|
||||||
class="fa-scale-110"
|
|
||||||
icon="thumbtack"
|
icon="thumbtack"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -38,15 +40,16 @@
|
||||||
<Popover
|
<Popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
:trigger-attrs="triggerAttrs"
|
:trigger-attrs="triggerAttrs"
|
||||||
|
class="quick-action"
|
||||||
:tabindex="0"
|
:tabindex="0"
|
||||||
placement="top"
|
placement="bottom"
|
||||||
:offset="{ y: 5 }"
|
:offset="{ y: 5 }"
|
||||||
remove-padding
|
remove-padding
|
||||||
@close="onExtraClose"
|
@close="onExtraClose"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="fa-scale-110 "
|
class="action-button-inner"
|
||||||
icon="ellipsis-h"
|
icon="ellipsis-h"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -56,23 +59,6 @@
|
||||||
class="dropdown-menu extra-action-buttons"
|
class="dropdown-menu extra-action-buttons"
|
||||||
role="menu"
|
role="menu"
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
v-if="currentUser"
|
|
||||||
class="menu-item dropdown-item extra-action -icon"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="main-button"
|
|
||||||
role="menuitem"
|
|
||||||
:tabindex="0"
|
|
||||||
@click.stop="() => { resize(); showPin = !showPin }"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
class="fa-scale-110"
|
|
||||||
fixed-width
|
|
||||||
icon="wrench"
|
|
||||||
/><span>{{ $t('nav.edit_pinned') }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-for="button in extraButtons"
|
v-for="button in extraButtons"
|
||||||
:key="button.name"
|
:key="button.name"
|
||||||
|
|
@ -89,7 +75,6 @@
|
||||||
:get-component="getComponent"
|
:get-component="getComponent"
|
||||||
:outer-close="close"
|
:outer-close="close"
|
||||||
:do-action="doAction"
|
:do-action="doAction"
|
||||||
@interacted="e => $emit('interacted')"
|
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
|
|
@ -108,6 +93,23 @@
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="currentUser"
|
||||||
|
class="menu-item dropdown-item extra-action -icon"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="main-button"
|
||||||
|
role="menuitem"
|
||||||
|
:tabindex="0"
|
||||||
|
@click.stop="() => { resize(); showPin = !showPin }"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="fa-scale-110"
|
||||||
|
fixed-width
|
||||||
|
icon="wrench"
|
||||||
|
/><span>{{ $t('nav.edit_pinned') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@ const StatusBody = {
|
||||||
localCollapseSubjectDefault() {
|
localCollapseSubjectDefault() {
|
||||||
return this.mergedConfig.collapseMessageWithSubject
|
return this.mergedConfig.collapseMessageWithSubject
|
||||||
},
|
},
|
||||||
|
allowNonSquareEmoji() {
|
||||||
|
return this.mergedConfig.nonSquareEmoji
|
||||||
|
},
|
||||||
// This is a bit hacky, but we want to approximate post height before rendering
|
// This is a bit hacky, but we want to approximate post height before rendering
|
||||||
// so we count newlines (masto uses <p> for paragraphs, GS uses <br> between them)
|
// so we count newlines (masto uses <p> for paragraphs, GS uses <br> between them)
|
||||||
// as well as approximate line count by counting characters and approximating ~80
|
// as well as approximate line count by counting characters and approximating ~80
|
||||||
|
|
|
||||||
|
|
@ -138,8 +138,6 @@
|
||||||
align-items: start;
|
align-items: start;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
--emoji-size: calc(var(--emojiSize, 32px) / 2);
|
|
||||||
|
|
||||||
& .body,
|
& .body,
|
||||||
& .attachments {
|
& .attachments {
|
||||||
max-height: 3.25em;
|
max-height: 3.25em;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
:html="status.summary_raw_html"
|
:html="status.summary_raw_html"
|
||||||
:emoji="status.emojis"
|
:emoji="status.emojis"
|
||||||
:is-local="status.isLocal"
|
:is-local="status.isLocal"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-show="longSubject && showingLongSubject"
|
v-show="longSubject && showingLongSubject"
|
||||||
|
|
@ -47,6 +48,7 @@
|
||||||
:greentext="mergedConfig.greentext"
|
:greentext="mergedConfig.greentext"
|
||||||
:attentions="status.attentions"
|
:attentions="status.attentions"
|
||||||
:is-local="status.is_local"
|
:is-local="status.is_local"
|
||||||
|
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||||
@parse-ready="onParseReady"
|
@parse-ready="onParseReady"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
:is-preview="true"
|
:is-preview="true"
|
||||||
:statusoid="status"
|
:statusoid="status"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
|
:ignore-mute="true"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else-if="error"
|
v-else-if="error"
|
||||||
|
|
|
||||||
74
src/components/still-image/still-image-emoji-popover.js
Normal file
74
src/components/still-image/still-image-emoji-popover.js
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import Popover from 'components/popover/popover.vue'
|
||||||
|
import SelectComponent from 'components/select/select.vue'
|
||||||
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
|
import StillImage from './still-image.vue'
|
||||||
|
|
||||||
|
import { useEmojiStore } from 'src/stores/emoji'
|
||||||
|
import { useInterfaceStore } from 'src/stores/interface'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { StillImage, Popover, SelectComponent },
|
||||||
|
props: {
|
||||||
|
shortcode: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isLocal: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
packName: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isUserAdmin() {
|
||||||
|
return this.$store.state.users.currentUser?.rights.admin
|
||||||
|
},
|
||||||
|
...mapState(useEmojiStore, ['adminPacksLocal', 'adminPacksLocalLoading']),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
displayError(msg) {
|
||||||
|
useInterfaceStore().pushGlobalNotice({
|
||||||
|
messageKey: 'admin_dash.emoji.error',
|
||||||
|
messageArgs: [msg],
|
||||||
|
level: 'error',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
copyToLocalPack() {
|
||||||
|
this.$store.state.api.backendInteractor
|
||||||
|
.addNewEmojiFile({
|
||||||
|
packName: this.packName,
|
||||||
|
file: this.$attrs.src,
|
||||||
|
shortcode: this.shortcode,
|
||||||
|
filename: '',
|
||||||
|
})
|
||||||
|
.then((resp) => resp.json())
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.error !== undefined) {
|
||||||
|
this.displayError(resp.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
useInterfaceStore().pushGlobalNotice({
|
||||||
|
messageKey: 'admin_dash.emoji.copied_successfully',
|
||||||
|
messageArgs: [this.shortcode, this.packName],
|
||||||
|
level: 'success',
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$refs.emojiPopover.hidePopover()
|
||||||
|
this.packName = ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchEmojiPacksIfAdmin() {
|
||||||
|
useEmojiStore()
|
||||||
|
.getAdminPacksLocal()
|
||||||
|
.then(() => {
|
||||||
|
this.$refs.emojiPopover.updateStyles()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue