Update stable - 2.5.0 release See merge request pleroma/pleroma-fe!1711cherry-pick-631c2532 2.5.0
commit
3a507ba9b2
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"presets": ["@babel/preset-env", "@vue/babel-preset-jsx"],
|
"presets": ["@babel/preset-env"],
|
||||||
"plugins": ["@babel/plugin-transform-runtime", "lodash"],
|
"plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"],
|
||||||
"comments": false
|
"comments": true
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
# Environment info
|
||||||
|
|
||||||
|
<!-- Everything is optional and where applicable but the more information the better. -->
|
||||||
|
* Browser, version, OS, platform:
|
||||||
|
* Instance URL:
|
||||||
|
* Frontend version (see settings -> about):
|
||||||
|
* Backend version (see settings -> about):
|
||||||
|
* Browser extensions (ublock, rikaichamp etc):
|
||||||
|
* Known instance/user customizations (i.e. pleromafe mods/forks, instance styles etc)
|
||||||
|
|
||||||
|
# Bug description & reproduction steps
|
||||||
|
|
||||||
|
<!-- Type out here how to reproduce the bug, what goes wrong and what should go right -->
|
||||||
|
<!-- Screenshots and videos help a lot ;) any observations might also help -->
|
||||||
|
<!-- Also mention if there any errors in browser's console if relevant -->
|
||||||
|
|
||||||
|
# Bug seriousness
|
||||||
|
|
||||||
|
<!-- Everything is optional and free-form -->
|
||||||
|
* How annoying it is:
|
||||||
|
* How often does it happen:
|
||||||
|
* How many people does it affect:
|
||||||
|
* Is there a workaround for it:
|
||||||
|
|
||||||
|
/label ~Bug
|
@ -0,0 +1,11 @@
|
|||||||
|
# Behavior suggestion/Feature request
|
||||||
|
<!--
|
||||||
|
Type out what you want to see changed or what feature you want to see added to
|
||||||
|
PleormaFE. Please also explain how it would benefit users (or admins/moderators)
|
||||||
|
and what intended usecase is. Any background information (i.e. porting behavior
|
||||||
|
from other frontends/services, specific situations, personal preferences etc.)
|
||||||
|
as well as examples would be greatly appreciated.
|
||||||
|
-->
|
||||||
|
|
||||||
|
/label ~suggestion
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
<!--
|
||||||
|
please use one of the templates if applicable, otherwise - type out here
|
||||||
|
in free-form
|
||||||
|
-->
|
||||||
|
|
||||||
|
/label ~needs-triage
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
Feel free to submit merge requests that are work-in-progress, but mark them as
|
||||||
|
Draft: or WIP:.
|
||||||
|
Merge requests that have Draft or WIP status will not be merged and have less chances
|
||||||
|
of being reviewed, but you can still ask people to take a look if you need advice.
|
||||||
|
-->
|
||||||
|
# Changes
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
|
||||||
|
<!-- List what your merge request changes and how -->
|
||||||
|
<!--
|
||||||
|
Try to not to break existing behavior, if your changes do break existing behavior
|
||||||
|
make it configurable to toggle between old behavior and new. Which one should be
|
||||||
|
default is up to discussion.
|
||||||
|
-->
|
||||||
|
<!-- If your merge request resolves some issue link it like so: "Closes #99999" -->
|
||||||
|
<!--
|
||||||
|
If merge request adds some new feature that depends on backend:
|
||||||
|
|
||||||
|
1. Make sure it gracefully degrades if backend hasn't been updated to support the feature,
|
||||||
|
we try to make PleromaFE compatible with older versions of BE so that people can still
|
||||||
|
update frontend safely without updating backend since it's costly and much riskier.
|
||||||
|
2. Link related BE merge request here
|
||||||
|
-->
|
||||||
|
<!-- Screenshots are welcome -->
|
||||||
|
|
||||||
|
/label ~needs-review
|
@ -1 +1 @@
|
|||||||
7.2.1
|
16.18.1
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
module.exports = {
|
||||||
|
updateEmoji () {
|
||||||
|
const emojis = require('@kazvmoe-infra/unicode-emoji-json/data-by-group')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
Object.keys(emojis)
|
||||||
|
.map(k => {
|
||||||
|
emojis[k].map(e => {
|
||||||
|
delete e.unicode_version
|
||||||
|
delete e.emoji_version
|
||||||
|
delete e.skin_tone_support_unicode_version
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = {}
|
||||||
|
Object.keys(emojis)
|
||||||
|
.map(k => {
|
||||||
|
const groupId = k.replace('&', 'and').replace(/ /g, '-').toLowerCase()
|
||||||
|
res[groupId] = emojis[k]
|
||||||
|
})
|
||||||
|
|
||||||
|
console.info('Updating emojis...')
|
||||||
|
fs.writeFileSync('static/emoji.json', JSON.stringify(res))
|
||||||
|
console.info('Done.')
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 883 KiB |
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:base"
|
||||||
|
]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
|||||||
|
@mixin unfocused-style {
|
||||||
|
@content;
|
||||||
|
|
||||||
|
&:focus:not(:focus-visible):not(:hover) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin focused-style {
|
||||||
|
&:hover, &:focus {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 396 KiB |
After Width: | Height: | Size: 521 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,105 @@
|
|||||||
|
import { mapState } from 'vuex'
|
||||||
|
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
||||||
|
import RichContent from '../rich_content/rich_content.jsx'
|
||||||
|
import localeService from '../../services/locale/locale.service.js'
|
||||||
|
|
||||||
|
const Announcement = {
|
||||||
|
components: {
|
||||||
|
AnnouncementEditor,
|
||||||
|
RichContent
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
editing: false,
|
||||||
|
editedAnnouncement: {
|
||||||
|
content: '',
|
||||||
|
startsAt: undefined,
|
||||||
|
endsAt: undefined,
|
||||||
|
allDay: undefined
|
||||||
|
},
|
||||||
|
editError: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
announcement: Object
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
currentUser: state => state.users.currentUser
|
||||||
|
}),
|
||||||
|
content () {
|
||||||
|
return this.announcement.content
|
||||||
|
},
|
||||||
|
isRead () {
|
||||||
|
return this.announcement.read
|
||||||
|
},
|
||||||
|
publishedAt () {
|
||||||
|
const time = this.announcement.published_at
|
||||||
|
if (!time) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale))
|
||||||
|
},
|
||||||
|
startsAt () {
|
||||||
|
const time = this.announcement.starts_at
|
||||||
|
if (!time) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale))
|
||||||
|
},
|
||||||
|
endsAt () {
|
||||||
|
const time = this.announcement.ends_at
|
||||||
|
if (!time) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale))
|
||||||
|
},
|
||||||
|
inactive () {
|
||||||
|
return this.announcement.inactive
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
markAsRead () {
|
||||||
|
if (!this.isRead) {
|
||||||
|
return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteAnnouncement () {
|
||||||
|
return this.$store.dispatch('deleteAnnouncement', this.announcement.id)
|
||||||
|
},
|
||||||
|
formatTimeOrDate (time, locale) {
|
||||||
|
const d = new Date(time)
|
||||||
|
return this.announcement.all_day ? d.toLocaleDateString(locale) : d.toLocaleString(locale)
|
||||||
|
},
|
||||||
|
enterEditMode () {
|
||||||
|
this.editedAnnouncement.content = this.announcement.pleroma.raw_content
|
||||||
|
this.editedAnnouncement.startsAt = this.announcement.starts_at
|
||||||
|
this.editedAnnouncement.endsAt = this.announcement.ends_at
|
||||||
|
this.editedAnnouncement.allDay = this.announcement.all_day
|
||||||
|
this.editing = true
|
||||||
|
},
|
||||||
|
submitEdit () {
|
||||||
|
this.$store.dispatch('editAnnouncement', {
|
||||||
|
id: this.announcement.id,
|
||||||
|
...this.editedAnnouncement
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.editing = false
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.editError = error.error
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cancelEdit () {
|
||||||
|
this.editing = false
|
||||||
|
},
|
||||||
|
clearError () {
|
||||||
|
this.editError = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Announcement
|
@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<div class="announcement">
|
||||||
|
<div class="heading">
|
||||||
|
<h4>{{ $t('announcements.title') }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<rich-content
|
||||||
|
v-if="!editing"
|
||||||
|
:html="content"
|
||||||
|
:emoji="announcement.emojis"
|
||||||
|
:handle-links="true"
|
||||||
|
/>
|
||||||
|
<announcement-editor
|
||||||
|
v-else
|
||||||
|
:announcement="editedAnnouncement"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div
|
||||||
|
v-if="!editing"
|
||||||
|
class="times"
|
||||||
|
>
|
||||||
|
<span v-if="publishedAt">
|
||||||
|
{{ $t('announcements.published_time_display', { time: publishedAt }) }}
|
||||||
|
</span>
|
||||||
|
<span v-if="startsAt">
|
||||||
|
{{ $t('announcements.start_time_display', { time: startsAt }) }}
|
||||||
|
</span>
|
||||||
|
<span v-if="endsAt">
|
||||||
|
{{ $t('announcements.end_time_display', { time: endsAt }) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!editing"
|
||||||
|
class="actions"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-if="currentUser"
|
||||||
|
class="btn button-default"
|
||||||
|
:class="{ toggled: isRead }"
|
||||||
|
:disabled="inactive"
|
||||||
|
:title="inactive ? $t('announcements.inactive_message') : ''"
|
||||||
|
@click="markAsRead"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.mark_as_read_action') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
|
class="btn button-default"
|
||||||
|
@click="enterEditMode"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.edit_action') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
|
class="btn button-default"
|
||||||
|
@click="deleteAnnouncement"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.delete_action') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="actions"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
@click="submitEdit"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.submit_edit_action') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
@click="cancelEdit"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.cancel_edit_action') }}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-if="editing && editError"
|
||||||
|
class="alert error"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.edit_error', { error }) }}
|
||||||
|
<button
|
||||||
|
class="button-unstyled"
|
||||||
|
@click="clearError"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="fa-scale-110 fa-old-padding"
|
||||||
|
icon="times"
|
||||||
|
:title="$t('announcements.close_error')"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./announcement.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "../../variables";
|
||||||
|
|
||||||
|
.announcement {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
border-bottom-color: var(--border, $fallback--border);
|
||||||
|
border-radius: 0;
|
||||||
|
padding: var(--status-margin, $status-margin);
|
||||||
|
|
||||||
|
.heading, .body {
|
||||||
|
margin-bottom: var(--status-margin, $status-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.times {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
margin: 1em;
|
||||||
|
max-width: 10em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,13 @@
|
|||||||
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
|
||||||
|
const AnnouncementEditor = {
|
||||||
|
components: {
|
||||||
|
Checkbox
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
announcement: Object,
|
||||||
|
disabled: Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnnouncementEditor
|
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<div class="announcement-editor">
|
||||||
|
<textarea
|
||||||
|
ref="textarea"
|
||||||
|
v-model="announcement.content"
|
||||||
|
class="post-textarea"
|
||||||
|
rows="1"
|
||||||
|
cols="1"
|
||||||
|
:placeholder="$t('announcements.post_placeholder')"
|
||||||
|
:disabled="disabled"
|
||||||
|
/>
|
||||||
|
<span class="announcement-metadata">
|
||||||
|
<label for="announcement-start-time">{{ $t('announcements.start_time_prompt') }}</label>
|
||||||
|
<input
|
||||||
|
id="announcement-start-time"
|
||||||
|
v-model="announcement.startsAt"
|
||||||
|
:type="announcement.allDay ? 'date' : 'datetime-local'"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="announcement-metadata">
|
||||||
|
<label for="announcement-end-time">{{ $t('announcements.end_time_prompt') }}</label>
|
||||||
|
<input
|
||||||
|
id="announcement-end-time"
|
||||||
|
v-model="announcement.endsAt"
|
||||||
|
:type="announcement.allDay ? 'date' : 'datetime-local'"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="announcement-metadata">
|
||||||
|
<Checkbox
|
||||||
|
id="announcement-all-day"
|
||||||
|
v-model="announcement.allDay"
|
||||||
|
:disabled="disabled"
|
||||||
|
/>
|
||||||
|
<label for="announcement-all-day">{{ $t('announcements.all_day_prompt') }}</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./announcement_editor.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.announcement-editor {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.announcement-metadata {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-textarea {
|
||||||
|
resize: vertical;
|
||||||
|
height: 10em;
|
||||||
|
overflow: none;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,55 @@
|
|||||||
|
import { mapState } from 'vuex'
|
||||||
|
import Announcement from '../announcement/announcement.vue'
|
||||||
|
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
||||||
|
|
||||||
|
const AnnouncementsPage = {
|
||||||
|
components: {
|
||||||
|
Announcement,
|
||||||
|
AnnouncementEditor
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
newAnnouncement: {
|
||||||
|
content: '',
|
||||||
|
startsAt: undefined,
|
||||||
|
endsAt: undefined,
|
||||||
|
allDay: false
|
||||||
|
},
|
||||||
|
posting: false,
|
||||||
|
error: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$store.dispatch('fetchAnnouncements')
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
currentUser: state => state.users.currentUser
|
||||||
|
}),
|
||||||
|
announcements () {
|
||||||
|
return this.$store.state.announcements.announcements
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
postAnnouncement () {
|
||||||
|
this.posting = true
|
||||||
|
this.$store.dispatch('postAnnouncement', this.newAnnouncement)
|
||||||
|
.then(() => {
|
||||||
|
this.newAnnouncement.content = ''
|
||||||
|
this.startsAt = undefined
|
||||||
|
this.endsAt = undefined
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.error = error.error
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.posting = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clearError () {
|
||||||
|
this.error = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnnouncementsPage
|
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel panel-default announcements-page">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<span>
|
||||||
|
{{ $t('announcements.page_header') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<section
|
||||||
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
|
>
|
||||||
|
<div class="post-form">
|
||||||
|
<div class="heading">
|
||||||
|
<h4>{{ $t('announcements.post_form_header') }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<announcement-editor
|
||||||
|
:announcement="newAnnouncement"
|
||||||
|
:disabled="posting"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<button
|
||||||
|
class="btn button-default post-button"
|
||||||
|
:disabled="posting"
|
||||||
|
@click.prevent="postAnnouncement"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.post_action') }}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-if="error"
|
||||||
|
class="alert error"
|
||||||
|
>
|
||||||
|
{{ $t('announcements.post_error', { error }) }}
|
||||||
|
<button
|
||||||
|
class="button-unstyled"
|
||||||
|
@click="clearError"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="fa-scale-110 fa-old-padding"
|
||||||
|
icon="times"
|
||||||
|
:title="$t('announcements.close_error')"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section
|
||||||
|
v-for="announcement in announcements"
|
||||||
|
:key="announcement.id"
|
||||||
|
>
|
||||||
|
<announcement
|
||||||
|
:announcement="announcement"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./announcements_page.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "../../variables";
|
||||||
|
|
||||||
|
.announcements-page {
|
||||||
|
.post-form {
|
||||||
|
padding: var(--status-margin, $status-margin);
|
||||||
|
|
||||||
|
.heading, .body {
|
||||||
|
margin-bottom: var(--status-margin, $status-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-button {
|
||||||
|
min-width: 10em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,268 @@
|
|||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.Attachment {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
align-self: flex-start;
|
||||||
|
line-height: 0;
|
||||||
|
height: 100%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: $fallback--attachmentRadius;
|
||||||
|
border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
|
||||||
|
border-color: $fallback--border;
|
||||||
|
border-color: var(--border, $fallback--border);
|
||||||
|
|
||||||
|
.attachment-wrapper {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-container {
|
||||||
|
flex: 0 1 0;
|
||||||
|
display: flex;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
p {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-static {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
background: var(--popover);
|
||||||
|
box-shadow: var(--popupShadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-field {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .placeholder-container,
|
||||||
|
& .image-container,
|
||||||
|
& .audio-container,
|
||||||
|
& .video-container,
|
||||||
|
& .flash-container,
|
||||||
|
& .oembed-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .flash-container,
|
||||||
|
& .video-container {
|
||||||
|
& .flash,
|
||||||
|
& video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
audio {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 64px;
|
||||||
|
top: calc(50% - 32px);
|
||||||
|
left: calc(50% - 32px);
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
text-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-buttons {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.attachment-button {
|
||||||
|
padding: 0;
|
||||||
|
border-radius: $fallback--tooltipRadius;
|
||||||
|
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
||||||
|
text-align: center;
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
font-size: 1.25em;
|
||||||
|
// TODO: theming? hard to theme with unknown background image color
|
||||||
|
background: rgba(230, 230, 230, 0.7);
|
||||||
|
|
||||||
|
.svg-inline--fa {
|
||||||
|
color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .svg-inline--fa {
|
||||||
|
color: rgba(0, 0, 0, 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.oembed-container {
|
||||||
|
line-height: 1.2em;
|
||||||
|
flex: 1 0 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 15px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
flex: 1;
|
||||||
|
img {
|
||||||
|
border: 0px;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
flex: 2;
|
||||||
|
margin: 8px;
|
||||||
|
word-break: break-all;
|
||||||
|
h1 {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-size-small {
|
||||||
|
.play-icon {
|
||||||
|
zoom: 0.5;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-buttons {
|
||||||
|
zoom: 0.7;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-editable {
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
& .description-container,
|
||||||
|
& .attachment-buttons {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-placeholder {
|
||||||
|
display: inline-block;
|
||||||
|
color: $fallback--link;
|
||||||
|
color: var(--postLink, $fallback--link);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
height: auto;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
&:not(.-editable) {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-editable {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
& .description-container,
|
||||||
|
& .attachment-buttons {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-container {
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-buttons {
|
||||||
|
order: 99;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-loading {
|
||||||
|
cursor: progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-contain-fit {
|
||||||
|
img,
|
||||||
|
canvas {
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-cover-fit {
|
||||||
|
img,
|
||||||
|
canvas {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
|
import statusPosterService from '../../services/status_poster/status_poster.service.js'
|
||||||
|
import get from 'lodash/get'
|
||||||
|
|
||||||
|
const EditStatusModal = {
|
||||||
|
components: {
|
||||||
|
PostStatusForm,
|
||||||
|
Modal
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
resettingForm: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoggedIn () {
|
||||||
|
return !!this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
modalActivated () {
|
||||||
|
return this.$store.state.editStatus.modalActivated
|
||||||
|
},
|
||||||
|
isFormVisible () {
|
||||||
|
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
||||||
|
},
|
||||||
|
params () {
|
||||||
|
return this.$store.state.editStatus.params || {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
params (newVal, oldVal) {
|
||||||
|
if (get(newVal, 'statusId') !== get(oldVal, 'statusId')) {
|
||||||
|
this.resettingForm = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.resettingForm = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isFormVisible (val) {
|
||||||
|
if (val) {
|
||||||
|
this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
|
||||||
|
const params = {
|
||||||
|
store: this.$store,
|
||||||
|
statusId: this.$store.state.editStatus.params.statusId,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
media,
|
||||||
|
contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusPosterService.editStatus(params)
|
||||||
|
.then((data) => {
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Error editing status', err)
|
||||||
|
return {
|
||||||
|
error: err.message
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$store.dispatch('closeEditStatusModal')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditStatusModal
|
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
v-if="isFormVisible"
|
||||||
|
class="edit-form-modal-view"
|
||||||
|
@backdropClicked="closeModal"
|
||||||
|
>
|
||||||
|
<div class="edit-form-modal-panel panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
{{ $t('post_status.edit_status') }}
|
||||||
|
</div>
|
||||||
|
<PostStatusForm
|
||||||
|
class="panel-body"
|
||||||
|
v-bind="params"
|
||||||
|
:post-handler="doEditStatus"
|
||||||
|
:disable-polls="true"
|
||||||
|
:disable-visibility-selector="true"
|
||||||
|
@posted="closeModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./edit_status_modal.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal-view.edit-form-modal-view {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.edit-form-modal-panel {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 25%;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 700px;
|
||||||
|
|
||||||
|
@media (orientation: landscape) {
|
||||||
|
margin-top: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-bottom-left {
|
||||||
|
max-width: 6.5em;
|
||||||
|
|
||||||
|
.emoji-icon {
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue