Toggle between different CW styles

This commit is contained in:
Laura Hausmann 2023-08-20 19:33:07 +02:00
parent a112f77ae0
commit e3131e9b11
No known key found for this signature in database
GPG Key ID: D044E84C5BE01605
6 changed files with 240 additions and 42 deletions

View File

@ -2125,3 +2125,8 @@ _feeds:
rss: "RSS" rss: "RSS"
atom: "Atom" atom: "Atom"
jsonFeed: "JSON feed" jsonFeed: "JSON feed"
cwStyle: "Content Warning appearance"
_cwStyle:
modern: "Modern"
classic: "Classic (Misskey/Foundkey-like)"
alternative: "Alternative (Firefish-like)"

View File

@ -2,12 +2,12 @@
<button <button
ref="el" ref="el"
class="_button" class="_button"
:class="{ showLess: modelValue, fade: !modelValue }" :class="[cwButton, { showLess: modelValue, fade: !modelValue, }]"
@click.stop="toggle" @click.stop="toggle"
> >
<span <span class="cw-toggle-text"
>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }} >{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}
<span v-if="!modelValue">{{ label }}</span> <span class="cw-char-count" v-if="!modelValue">{{ label }}</span>
</span> </span>
</button> </button>
</template> </template>
@ -18,6 +18,7 @@ import { length } from "stringz";
import type * as misskey from "iceshrimp-js"; import type * as misskey from "iceshrimp-js";
import { concat } from "@/scripts/array"; import { concat } from "@/scripts/array";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import {defaultStore} from "@/store";
const props = defineProps<{ const props = defineProps<{
modelValue: boolean; modelValue: boolean;
@ -43,6 +44,8 @@ const label = computed(() => {
] as string[][]).join(", "); ] as string[][]).join(", ");
}); });
const cwButton = computed (() => `_button_${defaultStore.state.cwStyle}`);
const toggle = () => { const toggle = () => {
emit("update:modelValue", !props.modelValue); emit("update:modelValue", !props.modelValue);
}; };
@ -57,7 +60,7 @@ defineExpose({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
._button { ._button_modern {
font-weight: 700; font-weight: 700;
z-index: 5; z-index: 5;
> span { > span {
@ -118,4 +121,95 @@ defineExpose({
} }
} }
} }
._button_alternative {
font-weight: 700;
z-index: 5;
> span {
background: var(--cwBg) !important;
color: var(--cwFg);
transition:
background 0.2s,
color 0.2s;
> span {
font-weight: 500;
&::before {
content: "(";
}
&::after {
content: ")";
}
}
}
&:hover > span,
&:focus > span {
background: var(--cwFg) !important;
color: var(--cwBg) !important;
}
&.fade {
display: block;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
> span {
display: inline-block;
background: var(--panel);
padding: 0.4em 1em;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
&:hover {
> span {
background: var(--panelHighlight);
}
}
}
&.showLess {
width: 100%;
position: sticky;
bottom: calc(var(--stickyBottom) - 1em);
padding: 20px;
> span {
display: inline-block;
background: var(--panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 0 7px 7px var(--bg);
}
}
}
._button_classic {
display: inline-block;
padding: 4px 8px;
font-size: 0.7em;
color: var(--cwFg);
background: var(--cwBg);
border-radius: 2px;
&:hover {
background: var(--cwHoverBg);
}
> span.cw-toggle-text {
font-weight: bold;
> span.cw-char-count {
font-weight: normal;
&:before {
content: '(';
}
&:after {
content: ')';
}
}
}
}
</style> </style>

View File

@ -2,7 +2,7 @@
<button <button
ref="el" ref="el"
class="_button" class="_button"
:class="{ fade: modelValue, showLess: !modelValue }" :class="[cwButton, { fade: modelValue, showLess: !modelValue }]"
@click.stop="toggle" @click.stop="toggle"
> >
<span>{{ modelValue ? i18n.ts.showMore : i18n.ts.showLess }}</span> <span>{{ modelValue ? i18n.ts.showMore : i18n.ts.showLess }}</span>
@ -10,7 +10,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { ref } from "vue"; import { computed, ref } from "vue";
import {defaultStore} from "@/store";
const props = defineProps<{ const props = defineProps<{
modelValue: boolean; modelValue: boolean;
@ -22,6 +23,8 @@ const emit = defineEmits<{
(ev: "update:modelValue", v: boolean): void; (ev: "update:modelValue", v: boolean): void;
}>(); }>();
const cwButton = computed (() => `_button_${defaultStore.state.cwStyle}`);
const toggle = () => { const toggle = () => {
emit("update:modelValue", !props.modelValue); emit("update:modelValue", !props.modelValue);
}; };
@ -44,13 +47,25 @@ defineExpose({
padding: 20px; padding: 20px;
margin-bottom: -10px; margin-bottom: -10px;
z-index: 5; z-index: 5;
> span { &._button_modern {
display: inline-block; > span {
background: var(--panel); display: inline-block;
padding: 0.4em 3em; background: var(--panel);
font-size: 0.8em; padding: 0.4em 3em;
border-radius: 999px; font-size: 0.8em;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%); border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
}
&:not(._button_modern) {
> span {
display: inline-block;
background: var(--panel);
padding: 0.4em 1em;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
} }
&:hover { &:hover {
> span { > span {
@ -65,13 +80,26 @@ defineExpose({
padding: 20px; padding: 20px;
z-index: 5; z-index: 5;
> span { &._button_modern {
display: inline-block; > span {
background: var(--panel); display: inline-block;
padding: 0.4em 3em; background: var(--panel);
font-size: 0.8em; padding: 0.4em 3em;
border-radius: 999px; font-size: 0.8em;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%); border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
}
&:not(._button_modern) {
> span {
display: inline-block;
background: var(--panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 0 7px 7px var(--bg);
}
} }
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<p v-if="note.cw != null" class="cw"> <p v-if="note.cw != null" class="cw" :class="cwStyle">
<MkA <MkA
v-if="conversation && note.renoteId == parentId" v-if="conversation && note.renoteId == parentId"
:to=" :to="
@ -34,24 +34,32 @@
<i class="ph-lock ph-bold"></i> <i class="ph-lock ph-bold"></i>
</span> </span>
<Mfm <Mfm
v-if="note.cw != '' && showContent" v-if="note.cw != '' && (showContent || defaultStore.state.cwStyle !== 'modern')"
class="text" class="text"
:text="note.cw" :text="note.cw"
:author="note.user" :author="note.user"
:i="$i" :i="$i"
:custom-emojis="note.emojis" :custom-emojis="note.emojis"
/> />
<XCwButton
ref="cwButton"
v-if="note.cw && defaultStore.state.cwStyle === 'classic'"
v-model="showContent"
:note="note"
v-on:keydown="focusFooter"
v-on:update:model-value="(val) => emit('expanded', val)"
/>
</p> </p>
<div class="wrmlmaau"> <div class="wrmlmaau">
<div <div
class="content" class="content"
:class="{ :class="[{
collapsed, collapsed,
isLong, isLong,
manyImages: note.files.length > 4, manyImages: note.files.length > 4,
showContent: note.cw && !showContent, showContent: note.cw && !showContent,
animatedMfm: !disableMfm, animatedMfm: !disableMfm,
}" }, cwStyle]"
> >
<XShowMoreButton <XShowMoreButton
ref="showMoreButton" ref="showMoreButton"
@ -59,10 +67,10 @@
v-model="collapsed" v-model="collapsed"
v-on:keydown="focusFooter" v-on:keydown="focusFooter"
></XShowMoreButton> ></XShowMoreButton>
<Mfm v-if="note.cw && !showContent" class="hiddenNote" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> <Mfm v-if="note.cw && ((!showContent && defaultStore.state.cwStyle === 'modern'))" class="hiddenNote" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
<XCwButton <XCwButton
ref="cwButton" ref="cwButton"
v-if="note.cw && !showContent" v-if="note.cw && !showContent && defaultStore.state.cwStyle !== 'classic'"
v-model="showContent" v-model="showContent"
:note="note" :note="note"
v-on:keydown="focusFooter" v-on:keydown="focusFooter"
@ -167,7 +175,7 @@
v-model="collapsed" v-model="collapsed"
></XShowMoreButton> ></XShowMoreButton>
<XCwButton <XCwButton
v-if="note.cw && showContent" v-if="note.cw && showContent && defaultStore.state.cwStyle !== 'classic'"
v-model="showContent" v-model="showContent"
:note="note" :note="note"
/> />
@ -193,7 +201,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { computed, ref } from "vue";
import * as misskey from "iceshrimp-js"; import * as misskey from "iceshrimp-js";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import * as os from "@/os"; import * as os from "@/os";
@ -240,6 +248,7 @@ const urls = props.note.text
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5) ? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
: null; : null;
const cwStyle = computed (() => `_cw_style_${defaultStore.state.cwStyle}`);
let _showContent = $ref(null); let _showContent = $ref(null);
let showContent = $computed({ let showContent = $computed({
set(val) { _showContent = val }, set(val) { _showContent = val },
@ -309,7 +318,9 @@ function focusFooter(ev) {
display: block; display: block;
margin: 0; margin: 0;
padding: 0; padding: 0;
margin-bottom: 10px; &:not(._cw_style_classic) {
margin-bottom: 10px;
}
overflow-wrap: break-word; overflow-wrap: break-word;
> .text { > .text {
margin-right: 8px; margin-right: 8px;
@ -319,12 +330,23 @@ function focusFooter(ev) {
.wrmlmaau { .wrmlmaau {
.content { .content {
overflow-wrap: break-word; overflow-wrap: break-word;
> .hiddenNote { &._cw_style_modern {
> .hiddenNote {
display: block;
padding: 0.5em 0 0.5em;
font-weight: 700;
font-size: 1.1em;
text-align: center;
}
}
&._cw_style_classic {
overflow: clip;
cursor: default;
display: block; display: block;
padding: 0.5em 0 0.5em; margin: 0;
font-weight: 700; padding: 0;
font-size: 1.1em; overflow-wrap: break-word;
text-align: center;
} }
> .body { > .body {
transition: filter 0.1s; transition: filter 0.1s;
@ -375,7 +397,9 @@ function focusFooter(ev) {
&.collapsed, &.collapsed,
&.showContent { &.showContent {
position: relative; position: relative;
min-height: calc(1em + 100px); &._cw_style_modern {
min-height: calc(1em + 100px);
}
max-height: calc(15em + 100px); max-height: calc(15em + 100px);
> .body { > .body {
max-height: inherit; max-height: inherit;
@ -403,14 +427,39 @@ function focusFooter(ev) {
} }
} }
&.showContent { &.showContent {
> .body { &._cw_style_alternative {
min-height: 2em; > .body {
max-height: 5em; min-height: 2em;
visibility: hidden; max-height: 5em;
filter: blur(4px);
:deep(span) {
animation: none !important;
transform: none !important;
}
:deep(img) {
filter: blur(12px);
}
}
:deep(.fade) {
inset: 0;
top: 90px;
}
} }
:deep(.fade) { &._cw_style_modern {
inset: 0; > .body {
top: 0; min-height: 2em;
max-height: 5em;
visibility: hidden;
}
:deep(.fade) {
inset: 0;
top: 0;
}
}
&._cw_style_classic {
> .body {
display: none;
}
} }
} }

View File

@ -200,6 +200,20 @@
class="_formBlock" class="_formBlock"
>{{ i18n.ts.showAdminUpdates }}</FormSwitch >{{ i18n.ts.showAdminUpdates }}</FormSwitch
> >
<FormSelect v-model="cwStyle" class="_formBlock">
<template #label>{{ i18n.ts.cwStyle }}</template>
<option value="modern">
{{ i18n.ts._cwStyle.modern }}
</option>
<option value="classic">
{{ i18n.ts._cwStyle.classic }}
</option>
<option value="alternative">
{{ i18n.ts._cwStyle.alternative }}
</option>
</FormSelect>
<FormSelect v-model="instanceTicker" class="_formBlock"> <FormSelect v-model="instanceTicker" class="_formBlock">
<template #label>{{ i18n.ts.instanceTicker }}</template> <template #label>{{ i18n.ts.instanceTicker }}</template>
<option value="none">{{ i18n.ts._instanceTicker.none }}</option> <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
@ -353,6 +367,9 @@ const showAdminUpdates = computed(
const showTimelineReplies = computed( const showTimelineReplies = computed(
defaultStore.makeGetterSetter("showTimelineReplies"), defaultStore.makeGetterSetter("showTimelineReplies"),
); );
const cwStyle = computed(
defaultStore.makeGetterSetter("cwStyle"),
);
watch(swipeOnDesktop, () => { watch(swipeOnDesktop, () => {
defaultStore.set("swipeOnMobile", true); defaultStore.set("swipeOnMobile", true);
@ -397,6 +414,7 @@ watch(
advancedMfm, advancedMfm,
autoplayMfm, autoplayMfm,
expandOnNoteClick, expandOnNoteClick,
cwStyle
], ],
async () => { async () => {
await reloadAsk(); await reloadAsk();

View File

@ -334,6 +334,10 @@ export const defaultStore = markRaw(
where: "device", where: "device",
default: false, default: false,
}, },
cwStyle: {
where: "device",
default: "modern" as "modern" | "classic" | "alternative",
},
}), }),
); );