Merge branch 'fixes-roundup5' into 'develop'
Fixes roundup5 See merge request pleroma/pleroma-fe!2081
This commit is contained in:
commit
598c569c93
25 changed files with 280 additions and 225 deletions
0
changelog.d/roundup5.skip
Normal file
0
changelog.d/roundup5.skip
Normal file
|
|
@ -129,6 +129,21 @@ PanelHeader {
|
||||||
background: --fg
|
background: --fg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PanelHeader ButtonUnstyled Icon {
|
||||||
|
textColor: --text;
|
||||||
|
textAuto: 'no-preserve'
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelHeader Button Icon {
|
||||||
|
textColor: --text;
|
||||||
|
textAuto: 'no-preserve'
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelHeader Button Text {
|
||||||
|
textColor: --text;
|
||||||
|
textAuto: 'no-preserve'
|
||||||
|
}
|
||||||
|
|
||||||
Tab:hover {
|
Tab:hover {
|
||||||
background: --bg;
|
background: --bg;
|
||||||
shadow: --buttonDefaultBevel
|
shadow: --buttonDefaultBevel
|
||||||
|
|
@ -172,6 +187,14 @@ MenuItem:hover {
|
||||||
background: --fg
|
background: --fg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuItem:active {
|
||||||
|
background: --fg
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem:active:hover {
|
||||||
|
background: --fg
|
||||||
|
}
|
||||||
|
|
||||||
Popover {
|
Popover {
|
||||||
shadow: --buttonDefaultBevel, 5 5 0 0 #000000 / 0.2;
|
shadow: --buttonDefaultBevel, 5 5 0 0 #000000 / 0.2;
|
||||||
roundness: 0
|
roundness: 0
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Button',
|
component: 'Button',
|
||||||
parent: { component: 'Attachment' },
|
parent: {
|
||||||
|
component: 'Attachment'
|
||||||
|
},
|
||||||
directives: {
|
directives: {
|
||||||
background: '#FFFFFF',
|
background: '#FFFFFF',
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ export default {
|
||||||
{
|
{
|
||||||
state: ['disabled'],
|
state: ['disabled'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '$blend(--accent 0.25 --parent)',
|
background: '$blend(--inheritedBackground 0.25 --parent)',
|
||||||
shadow: ['--buttonDefaultBevel']
|
shadow: ['--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
class="checkbox"
|
class="checkbox"
|
||||||
:class="{ disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }"
|
:class="[{ disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }, radio ? '-radio' : '-checkbox']"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="!!$slots.before"
|
v-if="!!$slots.before"
|
||||||
|
|
@ -19,9 +19,9 @@
|
||||||
@change="$emit('update:modelValue', $event.target.checked)"
|
@change="$emit('update:modelValue', $event.target.checked)"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="input -checkbox checkbox-indicator"
|
class="input checkbox-indicator"
|
||||||
:aria-hidden="true"
|
:aria-hidden="true"
|
||||||
:class="{ disabled }"
|
:class="[{ disabled }, radio ? '-radio' : '-checkbox']"
|
||||||
@transitionend.capture="onTransitionEnd"
|
@transitionend.capture="onTransitionEnd"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: [
|
props: [
|
||||||
|
'radio',
|
||||||
'modelValue',
|
'modelValue',
|
||||||
'indeterminate',
|
'indeterminate',
|
||||||
'disabled'
|
'disabled'
|
||||||
|
|
@ -107,6 +108,19 @@ export default {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.-radio {
|
||||||
|
.checkbox-indicator {
|
||||||
|
&,
|
||||||
|
&::before {
|
||||||
|
border-radius: 9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "•";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
.checkbox-indicator::before {
|
.checkbox-indicator::before {
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
|
|
|
||||||
44
src/components/login_form/login_form.scss
Normal file
44
src/components/login_form/login_form.scss
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
.login-panel {
|
||||||
|
.login-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
min-height: 2em;
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-bottom {
|
||||||
|
margin-top: 1em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.3em 0.5em 0.6em;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-error {
|
||||||
|
display: flex;
|
||||||
|
line-height: 2;
|
||||||
|
margin: 0.5em;
|
||||||
|
animation-name: shakeError;
|
||||||
|
animation-duration: 0.4s;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="login panel panel-default">
|
<div class="login-panel panel panel-default">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
|
|
@ -70,14 +70,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
<div
|
||||||
|
v-if="error"
|
||||||
<div
|
class="login-error alert error"
|
||||||
v-if="error"
|
>
|
||||||
class="form-group"
|
<span class="error-message">
|
||||||
>
|
{{ error }}
|
||||||
<div class="alert error">
|
</span>
|
||||||
{{ error }}
|
|
||||||
<button
|
<button
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
@click="clearError"
|
@click="clearError"
|
||||||
|
|
@ -94,57 +93,4 @@
|
||||||
|
|
||||||
<script src="./login_form.js"></script>
|
<script src="./login_form.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style src="./login_form.scss"/>
|
||||||
.login-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0.6em;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
min-height: 2em;
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.register {
|
|
||||||
flex: 1 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-bottom {
|
|
||||||
margin-top: 1em;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0.3em 0.5em 0.6em;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-bottom {
|
|
||||||
display: flex;
|
|
||||||
padding: 0.5em;
|
|
||||||
height: 32px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.35em;
|
|
||||||
padding: 0.35em;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
text-align: center;
|
|
||||||
animation-name: shakeError;
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
animation-timing-function: ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -238,4 +238,4 @@
|
||||||
|
|
||||||
<script src="./mrf_transparency_panel.js"></script>
|
<script src="./mrf_transparency_panel.js"></script>
|
||||||
|
|
||||||
<style src="./mrf_transparency_panel.scss" lang="scss"/>
|
<style src="./mrf_transparency_panel.scss" lang="scss" />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import Timeago from 'components/timeago/timeago.vue'
|
import Timeago from 'components/timeago/timeago.vue'
|
||||||
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
|
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
|
||||||
import RichContent from 'components/rich_content/rich_content.jsx'
|
import RichContent from 'components/rich_content/rich_content.jsx'
|
||||||
import { forEach, map } from 'lodash'
|
import Checkbox from 'components/checkbox/checkbox.vue'
|
||||||
import { usePollsStore } from 'src/stores/polls'
|
import { usePollsStore } from 'src/stores/polls'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -9,7 +9,8 @@ export default {
|
||||||
props: ['basePoll', 'emoji'],
|
props: ['basePoll', 'emoji'],
|
||||||
components: {
|
components: {
|
||||||
Timeago,
|
Timeago,
|
||||||
RichContent
|
RichContent,
|
||||||
|
Checkbox
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
@ -44,6 +45,13 @@ export default {
|
||||||
expired () {
|
expired () {
|
||||||
return (this.poll && this.poll.expired) || false
|
return (this.poll && this.poll.expired) || false
|
||||||
},
|
},
|
||||||
|
expirationLabel () {
|
||||||
|
if (this.$store.getters.mergedConfig.useAbsoluteTimeFormat) {
|
||||||
|
return this.expired ? 'polls.expired_at' : 'polls.expires_at'
|
||||||
|
} else {
|
||||||
|
return this.expired ? 'polls.expired' : 'polls.expires_in'
|
||||||
|
}
|
||||||
|
},
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
},
|
},
|
||||||
|
|
@ -78,26 +86,15 @@ export default {
|
||||||
resultTitle (option) {
|
resultTitle (option) {
|
||||||
return `${option.votes_count}/${this.totalVotesCount} ${this.$t('polls.votes')}`
|
return `${option.votes_count}/${this.totalVotesCount} ${this.$t('polls.votes')}`
|
||||||
},
|
},
|
||||||
activateOption (index) {
|
activateOption (index, value) {
|
||||||
// forgive me father: doing checking the radio/checkboxes
|
let result
|
||||||
// in code because of customized input elements need either
|
|
||||||
// a) an extra element for the actual graphic, or b) use a
|
|
||||||
// pseudo element for the label. We use b) which mandates
|
|
||||||
// using "for" and "id" matching which isn't nice when the
|
|
||||||
// same poll appears multiple times on the site (notifs and
|
|
||||||
// timeline for example). With code we can make sure it just
|
|
||||||
// works without altering the pseudo element implementation.
|
|
||||||
const allElements = this.$el.querySelectorAll('input')
|
|
||||||
const clickedElement = this.$el.querySelector(`input[value="${index}"]`)
|
|
||||||
if (this.poll.multiple) {
|
if (this.poll.multiple) {
|
||||||
// Checkboxes, toggle only the clicked one
|
result = this.choices || this.options.map(() => false)
|
||||||
clickedElement.checked = !clickedElement.checked
|
|
||||||
} else {
|
} else {
|
||||||
// Radio button, uncheck everything and check the clicked one
|
result = this.options.map(() => false)
|
||||||
forEach(allElements, element => { element.checked = false })
|
|
||||||
clickedElement.checked = true
|
|
||||||
}
|
}
|
||||||
this.choices = map(allElements, e => e.checked)
|
result[index] = value
|
||||||
|
this.choices = result
|
||||||
},
|
},
|
||||||
optionId (index) {
|
optionId (index) {
|
||||||
return `poll${this.poll.id}-${index}`
|
return `poll${this.poll.id}-${index}`
|
||||||
|
|
|
||||||
62
src/components/poll/poll.scss
Normal file
62
src/components/poll/poll.scss
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
.poll {
|
||||||
|
.votes {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-option {
|
||||||
|
margin: 0.75em 0.5em;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-result {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
color: var(--textLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-result-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.1em 0.25em;
|
||||||
|
z-index: 1;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-percentage {
|
||||||
|
width: 3.5em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-fill {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transition: width 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 3.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading * {
|
||||||
|
cursor: progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-vote-button {
|
||||||
|
padding: 0 1em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-checkbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,36 +37,56 @@
|
||||||
:role="poll.multiple ? 'checkbox' : 'radio'"
|
:role="poll.multiple ? 'checkbox' : 'radio'"
|
||||||
:aria-labelledby="`option-vote-${randomSeed}-${index}`"
|
:aria-labelledby="`option-vote-${randomSeed}-${index}`"
|
||||||
:aria-checked="choices[index]"
|
:aria-checked="choices[index]"
|
||||||
class="input unstyled"
|
|
||||||
@click="activateOption(index)"
|
|
||||||
>
|
>
|
||||||
<!-- TODO: USE CHECKBOX -->
|
<Checkbox
|
||||||
<input
|
:radio="!poll.multiple"
|
||||||
v-if="poll.multiple"
|
|
||||||
type="checkbox"
|
|
||||||
class="input -checkbox poll-checkbox"
|
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:value="index"
|
:model-value="choices[index]"
|
||||||
|
@update:model-value="value => activateOption(index, value)"
|
||||||
>
|
>
|
||||||
<input
|
|
||||||
v-else
|
|
||||||
type="radio"
|
|
||||||
:disabled="loading"
|
|
||||||
:value="index"
|
|
||||||
class="input -radio"
|
|
||||||
>
|
|
||||||
<label class="option-vote">
|
|
||||||
<RichContent
|
<RichContent
|
||||||
:id="`option-vote-${randomSeed}-${index}`"
|
:id="`option-vote-${randomSeed}-${index}`"
|
||||||
:html="option.title_html"
|
:html="option.title_html"
|
||||||
:handle-links="false"
|
:handle-links="false"
|
||||||
:emoji="emoji"
|
:emoji="emoji"
|
||||||
/>
|
/>
|
||||||
</label>
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer faint">
|
<div class="footer faint">
|
||||||
|
<p>
|
||||||
|
<span
|
||||||
|
v-if="poll.pleroma?.non_anonymous"
|
||||||
|
:title="$t('polls.non_anonymous_title')"
|
||||||
|
>
|
||||||
|
{{ $t('polls.non_anonymous') }}
|
||||||
|
·
|
||||||
|
</span>
|
||||||
|
<span class="total">
|
||||||
|
<template v-if="typeof poll.voters_count === 'number'">
|
||||||
|
{{ $t("polls.people_voted_count", { count: poll.voters_count }, poll.voters_count) }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ $t("polls.votes_count", { count: poll.votes_count }, poll.votes_count) }}
|
||||||
|
</template>
|
||||||
|
<span v-if="expiresAt !== null">
|
||||||
|
·
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="expiresAt !== null">
|
||||||
|
<i18n-t
|
||||||
|
scope="global"
|
||||||
|
:keypath="expirationLabel"
|
||||||
|
>
|
||||||
|
<Timeago
|
||||||
|
:time="expiresAt"
|
||||||
|
:auto-update="60"
|
||||||
|
:now-threshold="0"
|
||||||
|
/>
|
||||||
|
</i18n-t>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
v-if="!showResults"
|
v-if="!showResults"
|
||||||
class="btn button-default poll-vote-button"
|
class="btn button-default poll-vote-button"
|
||||||
|
|
@ -76,113 +96,10 @@
|
||||||
>
|
>
|
||||||
{{ $t('polls.vote') }}
|
{{ $t('polls.vote') }}
|
||||||
</button>
|
</button>
|
||||||
<span
|
|
||||||
v-if="poll.pleroma?.non_anonymous"
|
|
||||||
:title="$t('polls.non_anonymous_title')"
|
|
||||||
>
|
|
||||||
{{ $t('polls.non_anonymous') }}
|
|
||||||
·
|
|
||||||
</span>
|
|
||||||
<div class="total">
|
|
||||||
<template v-if="typeof poll.voters_count === 'number'">
|
|
||||||
{{ $t("polls.people_voted_count", { count: poll.voters_count }, poll.voters_count) }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ $t("polls.votes_count", { count: poll.votes_count }, poll.votes_count) }}
|
|
||||||
</template>
|
|
||||||
<span v-if="expiresAt !== null">
|
|
||||||
·
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span v-if="expiresAt !== null">
|
|
||||||
<i18n-t
|
|
||||||
scope="global"
|
|
||||||
:keypath="expired ? 'polls.expired' : 'polls.expires_in'"
|
|
||||||
>
|
|
||||||
<Timeago
|
|
||||||
:time="expiresAt"
|
|
||||||
:auto-update="60"
|
|
||||||
:now-threshold="0"
|
|
||||||
/>
|
|
||||||
</i18n-t>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./poll.js"></script>
|
<script src="./poll.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style src="./poll.scss" lang="scss" />
|
||||||
.poll {
|
|
||||||
.votes {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0 0 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-option {
|
|
||||||
margin: 0.75em 0.5em;
|
|
||||||
|
|
||||||
.input {
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.option-result {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
position: relative;
|
|
||||||
color: var(--textLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
.option-result-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.1em 0.25em;
|
|
||||||
z-index: 1;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-percentage {
|
|
||||||
width: 3.5em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-fill {
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
border-radius: var(--roundness);
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
transition: width 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option-vote {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 3.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.loading * {
|
|
||||||
cursor: progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-vote-button {
|
|
||||||
padding: 0 0.5em;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-checkbox {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -119,10 +119,13 @@
|
||||||
:key="hashtag.url"
|
:key="hashtag.url"
|
||||||
class="status trend search-result"
|
class="status trend search-result"
|
||||||
>
|
>
|
||||||
<div class="hashtag">
|
<router-link
|
||||||
<router-link :to="{ name: 'tag-timeline', params: { tag: hashtag.name } }">
|
class="list-item hashtag"
|
||||||
|
:to="{ name: 'tag-timeline', params: { tag: hashtag.name } }"
|
||||||
|
>
|
||||||
|
<span class="name">
|
||||||
#{{ hashtag.name }}
|
#{{ hashtag.name }}
|
||||||
</router-link>
|
</span>
|
||||||
<div v-if="lastHistoryRecord(hashtag)">
|
<div v-if="lastHistoryRecord(hashtag)">
|
||||||
<span v-if="lastHistoryRecord(hashtag).accounts == 1">
|
<span v-if="lastHistoryRecord(hashtag).accounts == 1">
|
||||||
{{ $t('search.person_talking', { count: lastHistoryRecord(hashtag).accounts }) }}
|
{{ $t('search.person_talking', { count: lastHistoryRecord(hashtag).accounts }) }}
|
||||||
|
|
@ -131,7 +134,7 @@
|
||||||
{{ $t('search.people_talking', { count: lastHistoryRecord(hashtag).accounts }) }}
|
{{ $t('search.people_talking', { count: lastHistoryRecord(hashtag).accounts }) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-if="lastHistoryRecord(hashtag)"
|
v-if="lastHistoryRecord(hashtag)"
|
||||||
class="count"
|
class="count"
|
||||||
|
|
@ -199,10 +202,13 @@
|
||||||
|
|
||||||
.hashtag {
|
.hashtag {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
color: var(--text);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
|
|
|
||||||
|
|
@ -540,6 +540,7 @@
|
||||||
:status="status"
|
:status="status"
|
||||||
:replying="replying"
|
:replying="replying"
|
||||||
@toggle-replying="toggleReplying"
|
@toggle-replying="toggleReplying"
|
||||||
|
@interacted="e => $emit('interacted')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ export default {
|
||||||
'doAction',
|
'doAction',
|
||||||
'outerClose'
|
'outerClose'
|
||||||
],
|
],
|
||||||
|
emits: [
|
||||||
|
'interacted'
|
||||||
|
],
|
||||||
components: {
|
components: {
|
||||||
StatusBookmarkFolderMenu,
|
StatusBookmarkFolderMenu,
|
||||||
EmojiPicker,
|
EmojiPicker,
|
||||||
|
|
@ -120,7 +123,8 @@ export default {
|
||||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doActionWrap (button, close) {
|
doActionWrap (button, close = () => {}) {
|
||||||
|
this.$emit('interacted')
|
||||||
if (button.name === 'emoji') {
|
if (button.name === 'emoji') {
|
||||||
this.$refs.picker.showPicker()
|
this.$refs.picker.showPicker()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export default {
|
||||||
MuteConfirm
|
MuteConfirm
|
||||||
},
|
},
|
||||||
props: ['button', 'status'],
|
props: ['button', 'status'],
|
||||||
|
emits: ['interacted'],
|
||||||
mounted () {
|
mounted () {
|
||||||
if (this.button.name === 'mute') {
|
if (this.button.name === 'mute') {
|
||||||
this.$store.dispatch('fetchDomainMutes')
|
this.$store.dispatch('fetchDomainMutes')
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@
|
||||||
:button="button"
|
:button="button"
|
||||||
:status="status"
|
:status="status"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
|
@interacted="e => $emit('interacted')"
|
||||||
/>
|
/>
|
||||||
<teleport to="#modal">
|
<teleport to="#modal">
|
||||||
<MuteConfirm
|
<MuteConfirm
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import { useEditStatusStore } from 'src/stores/editStatus.js'
|
import { useEditStatusStore } from 'src/stores/editStatus.js'
|
||||||
|
import { useReportsStore } from 'src/stores/reports.js'
|
||||||
|
import { useStatusHistoryStore } from 'src/stores/statusHistory.js'
|
||||||
|
|
||||||
const PRIVATE_SCOPES = new Set(['private', 'direct'])
|
const PRIVATE_SCOPES = new Set(['private', 'direct'])
|
||||||
const PUBLIC_SCOPES = new Set(['public', 'unlisted'])
|
const PUBLIC_SCOPES = new Set(['public', 'unlisted'])
|
||||||
export const BUTTONS = [{
|
export const BUTTONS = [{
|
||||||
|
|
@ -138,6 +141,34 @@ export const BUTTONS = [{
|
||||||
return dispatch('bookmark', { id: status.id })
|
return dispatch('bookmark', { id: status.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
// =========
|
||||||
|
// EDIT HISTORY
|
||||||
|
// =========
|
||||||
|
name: 'editHistory',
|
||||||
|
icon: 'history',
|
||||||
|
label: 'status.status_history',
|
||||||
|
if ({ status, state }) {
|
||||||
|
return state.instance.editingAvailable &&
|
||||||
|
status.edited_at !== null
|
||||||
|
},
|
||||||
|
action ({ status }) {
|
||||||
|
const originalStatus = { ...status }
|
||||||
|
const stripFieldsList = [
|
||||||
|
'attachments',
|
||||||
|
'created_at',
|
||||||
|
'emojis',
|
||||||
|
'text',
|
||||||
|
'raw_html',
|
||||||
|
'nsfw',
|
||||||
|
'poll',
|
||||||
|
'summary',
|
||||||
|
'summary_raw_html'
|
||||||
|
]
|
||||||
|
stripFieldsList.forEach(p => delete originalStatus[p])
|
||||||
|
useStatusHistoryStore().openStatusHistoryModal(originalStatus)
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
// =========
|
// =========
|
||||||
// EDIT
|
// EDIT
|
||||||
|
|
@ -216,8 +247,8 @@ export const BUTTONS = [{
|
||||||
icon: 'flag',
|
icon: 'flag',
|
||||||
label: 'user_card.report',
|
label: 'user_card.report',
|
||||||
if: ({ loggedIn }) => loggedIn,
|
if: ({ loggedIn }) => loggedIn,
|
||||||
action ({ dispatch, status }) {
|
action ({ status }) {
|
||||||
dispatch('openUserReportingModal', { userId: status.user.id, statusIds: [status.id] })
|
return useReportsStore().openUserReportingModal({ userId: status.user.id, statusIds: [status.id] })
|
||||||
}
|
}
|
||||||
}].map(button => {
|
}].map(button => {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ library.add(
|
||||||
|
|
||||||
const StatusActionButtons = {
|
const StatusActionButtons = {
|
||||||
props: ['status', 'replying'],
|
props: ['status', 'replying'],
|
||||||
emits: ['toggleReplying'],
|
emits: ['toggleReplying', 'interacted'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
showPin: false,
|
showPin: false,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
.StatusActionButtons {
|
.StatusActionButtons {
|
||||||
.quick-action-buttons {
|
.quick-action-buttons {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, 4em);
|
grid-template-columns: repeat(auto-fill, minmax(10%, 3em));
|
||||||
grid-auto-flow: row dense;
|
grid-auto-flow: row dense;
|
||||||
grid-auto-rows: 1fr;
|
grid-auto-rows: 1fr;
|
||||||
grid-gap: 1.25em 1em;
|
grid-gap: 1.25em 1em;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
:get-component="getComponent"
|
:get-component="getComponent"
|
||||||
:close="() => {}"
|
:close="() => {}"
|
||||||
:do-action="doAction"
|
:do-action="doAction"
|
||||||
|
@interacted="e => $emit('interacted')"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
|
|
@ -86,8 +87,9 @@
|
||||||
:func-arg="funcArg"
|
:func-arg="funcArg"
|
||||||
:get-class="getClass"
|
:get-class="getClass"
|
||||||
:get-component="getComponent"
|
:get-component="getComponent"
|
||||||
:outerClose="close"
|
:outer-close="close"
|
||||||
:do-action="doAction"
|
:do-action="doAction"
|
||||||
|
@interacted="e => $emit('interacted')"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@
|
||||||
class="input menu-checkbox -radio"
|
class="input menu-checkbox -radio"
|
||||||
:class="{ 'menu-checkbox-checked': status.bookmark_folder_id == folder.id }"
|
:class="{ 'menu-checkbox-checked': status.bookmark_folder_id == folder.id }"
|
||||||
/>
|
/>
|
||||||
<StillImage :src="folder.emoji_url" class="emoji" />
|
<StillImage
|
||||||
|
:src="folder.emoji_url"
|
||||||
|
class="emoji"
|
||||||
|
/>
|
||||||
{{ ' ' + folder.name }}
|
{{ ' ' + folder.name }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -231,8 +231,9 @@
|
||||||
"single_choice": "Single choice",
|
"single_choice": "Single choice",
|
||||||
"multiple_choices": "Multiple choices",
|
"multiple_choices": "Multiple choices",
|
||||||
"expiry": "Poll age",
|
"expiry": "Poll age",
|
||||||
"expires_in": "Poll ends in {0}",
|
"expires_at": "Poll ends {0}",
|
||||||
"expired": "Poll ended {0} ago",
|
"expired": "Poll ended {0} ago",
|
||||||
|
"expired_at": "Poll ended {0}",
|
||||||
"not_enough_options": "Too few unique options in poll",
|
"not_enough_options": "Too few unique options in poll",
|
||||||
"non_anonymous": "Public poll",
|
"non_anonymous": "Public poll",
|
||||||
"non_anonymous_title": "Other instances may display the options you voted for"
|
"non_anonymous_title": "Other instances may display the options you voted for"
|
||||||
|
|
|
||||||
|
|
@ -516,7 +516,7 @@ export const init = ({
|
||||||
.filter(c => virtualComponents.has(c) && !nonEditableComponents.has(c))
|
.filter(c => virtualComponents.has(c) && !nonEditableComponents.has(c))
|
||||||
} else if (liteMode) {
|
} else if (liteMode) {
|
||||||
validInnerComponents = (component.validInnerComponentsLite || component.validInnerComponents || [])
|
validInnerComponents = (component.validInnerComponentsLite || component.validInnerComponents || [])
|
||||||
} else if (component.name === 'Root') {
|
} else if (component.name === 'Root' || component.states != null) {
|
||||||
validInnerComponents = component.validInnerComponents || []
|
validInnerComponents = component.validInnerComponents || []
|
||||||
} else {
|
} else {
|
||||||
validInnerComponents = component
|
validInnerComponents = component
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export const useReportsStore = defineStore('reports', {
|
||||||
this.reportModal.activated = false
|
this.reportModal.activated = false
|
||||||
},
|
},
|
||||||
setReportState ({ id, state }) {
|
setReportState ({ id, state }) {
|
||||||
const oldState = window.vuex.state.reports.reports[id].state
|
const oldState = this.reports[id].state
|
||||||
this.reports[id].state = state
|
this.reports[id].state = state
|
||||||
window.vuex.state.api.backendInteractor.setReportState({ id, state }).catch(e => {
|
window.vuex.state.api.backendInteractor.setReportState({ id, state }).catch(e => {
|
||||||
console.error('Failed to set report state', e)
|
console.error('Failed to set report state', e)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Existing emojis we have
|
// Existing emojis we have
|
||||||
const oldEmojiFilename = '../static/emoji.json'
|
const oldEmojiFilename = '../src/assets/emoji.json'
|
||||||
|
|
||||||
// The file downloaded from https://gist.github.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb
|
// The file downloaded from https://gist.github.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb
|
||||||
const newEmojiFilename = 'emojis.json'
|
const newEmojiFilename = 'emojis.json'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue