Compare commits
3 Commits
ea4751400b
...
b74f2d69a4
Author | SHA1 | Date |
---|---|---|
Natty | b74f2d69a4 | |
Natty | e06906dd6e | |
Natty | 155e458806 |
|
@ -1,3 +1,34 @@
|
|||
use std::future::Future;
|
||||
|
||||
use chrono::Utc;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use redis::IntoConnectionInfo;
|
||||
pub use sea_orm;
|
||||
use sea_orm::{ActiveValue::Set, ConnectionTrait};
|
||||
use sea_orm::{
|
||||
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter,
|
||||
TransactionTrait,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde::de::Error;
|
||||
use serde_json::Value;
|
||||
use strum::IntoStaticStr;
|
||||
use thiserror::Error;
|
||||
use tokio::select;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{error, info, trace, warn};
|
||||
use tracing::log::LevelFilter;
|
||||
use url::Host;
|
||||
|
||||
pub use ck;
|
||||
use ck::*;
|
||||
use ext_model_migration::{Migrator, MigratorTrait};
|
||||
use user_model::UserResolver;
|
||||
|
||||
use crate::model_ext::IdShape;
|
||||
use crate::note_model::NoteResolver;
|
||||
use crate::notification_model::NotificationResolver;
|
||||
|
||||
pub mod emoji;
|
||||
pub mod model_ext;
|
||||
pub mod note_model;
|
||||
|
@ -5,35 +36,6 @@ pub mod notification_model;
|
|||
pub mod poll;
|
||||
pub mod user_model;
|
||||
|
||||
pub use ck;
|
||||
use ck::*;
|
||||
pub use sea_orm;
|
||||
use url::Host;
|
||||
use user_model::UserResolver;
|
||||
|
||||
use crate::model_ext::IdShape;
|
||||
use crate::note_model::NoteResolver;
|
||||
use crate::notification_model::NotificationResolver;
|
||||
use chrono::Utc;
|
||||
use ext_model_migration::{Migrator, MigratorTrait};
|
||||
use futures_util::StreamExt;
|
||||
use redis::IntoConnectionInfo;
|
||||
use sea_orm::{ActiveValue::Set, ConnectionTrait};
|
||||
use sea_orm::{
|
||||
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter,
|
||||
TransactionTrait,
|
||||
};
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::future::Future;
|
||||
use strum::IntoStaticStr;
|
||||
use thiserror::Error;
|
||||
use tokio::select;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::log::LevelFilter;
|
||||
use tracing::{error, info, trace, warn};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectorConfig {
|
||||
pub url: String,
|
||||
|
@ -120,6 +122,15 @@ impl CalckeyModel {
|
|||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_user_and_profile_by_id(&self, id: &str) -> Result<Option<(user::Model, user_profile::Model)>, CalckeyDbError> {
|
||||
Ok(user::Entity::find()
|
||||
.filter(user::Column::Id.eq(id))
|
||||
.find_also_related(user_profile::Entity)
|
||||
.one(&self.0)
|
||||
.await?
|
||||
.and_then(|(u, p)| p.map(|pp| (u, pp))))
|
||||
}
|
||||
|
||||
pub async fn get_user_security_keys_by_id(
|
||||
&self,
|
||||
id: &str,
|
||||
|
@ -154,6 +165,22 @@ impl CalckeyModel {
|
|||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_user_and_profile_by_token(
|
||||
&self,
|
||||
token: &str,
|
||||
) -> Result<Option<(user::Model, user_profile::Model)>, CalckeyDbError> {
|
||||
Ok(user::Entity::find()
|
||||
.filter(
|
||||
user::Column::Token
|
||||
.eq(token)
|
||||
.and(user::Column::Host.is_null()),
|
||||
)
|
||||
.find_also_related(user_profile::Entity)
|
||||
.one(&self.0)
|
||||
.await?
|
||||
.and_then(|(u, p)| p.map(|pp| (u, pp))))
|
||||
}
|
||||
|
||||
pub async fn get_user_by_uri(&self, uri: &str) -> Result<Option<user::Model>, CalckeyDbError> {
|
||||
Ok(user::Entity::find()
|
||||
.filter(user::Column::Uri.eq(uri))
|
||||
|
@ -439,7 +466,7 @@ pub enum InternalStreamMessage {
|
|||
}
|
||||
|
||||
impl CalckeyCacheClient {
|
||||
pub async fn subscribe<F: Future<Output = ()> + Send + 'static>(
|
||||
pub async fn subscribe<F: Future<Output=()> + Send + 'static>(
|
||||
self,
|
||||
prefix: &str,
|
||||
handler: impl Fn(SubMessage) -> F + Send + Sync + 'static,
|
||||
|
@ -451,7 +478,7 @@ impl CalckeyCacheClient {
|
|||
pub struct CalckeySub(CancellationToken);
|
||||
|
||||
impl CalckeySub {
|
||||
async fn new<F: Future<Output = ()> + Send + 'static>(
|
||||
async fn new<F: Future<Output=()> + Send + 'static>(
|
||||
conn: redis::aio::Connection,
|
||||
prefix: &str,
|
||||
handler: impl Fn(SubMessage) -> F + Send + Sync + 'static,
|
||||
|
|
|
@ -5,7 +5,6 @@ import type {
|
|||
MeDetailed,
|
||||
Note,
|
||||
Notification,
|
||||
PageEvent,
|
||||
User,
|
||||
} from "./entities";
|
||||
|
||||
|
@ -23,7 +22,6 @@ export type Channels = {
|
|||
followed: (payload: User) => void; // 他人が自分をフォローしたとき
|
||||
unfollow: (payload: User) => void; // 自分が他人をフォロー解除したとき
|
||||
meUpdated: (payload: MeDetailed) => void;
|
||||
pageEvent: (payload: PageEvent) => void;
|
||||
urlUploadFinished: (payload: { marker: string; file: DriveFile }) => void;
|
||||
readAllNotifications: () => void;
|
||||
unreadNotification: (payload: Notification) => void;
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
"prismjs": "1.29.0",
|
||||
"punycode": "2.1.1",
|
||||
"rndstr": "1.0.0",
|
||||
"rollup": "^4.17.1",
|
||||
"rollup": "^4.17.2",
|
||||
"s-age": "1.1.2",
|
||||
"sass": "1.62.1",
|
||||
"seedrandom": "3.0.5",
|
||||
|
@ -88,7 +88,7 @@
|
|||
"vite": "^5.2.10",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vue": "^3.4.26",
|
||||
"vue-component-type-helpers": "^2.0.14",
|
||||
"vue-component-type-helpers": "^2.0.15",
|
||||
"vue-isyourpasswordsafe": "^2.0.0",
|
||||
"vue3-otp-input": "^0.4.4",
|
||||
"vuedraggable": "4.1.0"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
:is="self ? 'MkA' : 'a'"
|
||||
ref="el"
|
||||
class="xlcxczvw _link"
|
||||
:[attr]="self ? url.substr(local.length) : url"
|
||||
:[attr]="self ? url.substring(local.length) : url"
|
||||
:rel="rel"
|
||||
:target="target"
|
||||
:title="url"
|
||||
|
@ -28,7 +28,7 @@ const props = withDefaults(
|
|||
url: string;
|
||||
rel?: null | string;
|
||||
}>(),
|
||||
{},
|
||||
{}
|
||||
);
|
||||
|
||||
const self = props.url.startsWith(local);
|
||||
|
@ -40,7 +40,7 @@ const el = ref();
|
|||
useTooltip(el, (showing) => {
|
||||
os.popup(
|
||||
defineAsyncComponent(
|
||||
() => import("@/components/MkUrlPreviewPopup.vue"),
|
||||
() => import("@/components/MkUrlPreviewPopup.vue")
|
||||
),
|
||||
{
|
||||
showing,
|
||||
|
@ -48,7 +48,7 @@ useTooltip(el, (showing) => {
|
|||
source: el.value,
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
</script>
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { toUnicode } from "punycode";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
import MkLink from "@/components/MagLink.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -19,7 +19,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const url = computed(
|
||||
() => `https://matrix.to/#/@${props.username}:${props.host}`,
|
||||
() => `https://matrix.to/#/@${props.username}:${props.host}`
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ import XReactionsViewer from "@/components/MkReactionsViewer.vue";
|
|||
import XStarButton from "@/components/MkStarButton.vue";
|
||||
import XStarButtonNoEmoji from "@/components/MkStarButtonNoEmoji.vue";
|
||||
import XQuoteButton from "@/components/MagQuoteButton.vue";
|
||||
import MkVisibility from "@/components/MkVisibility.vue";
|
||||
import MkVisibility from "@/components/MagVisibility.vue";
|
||||
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
||||
import { url } from "@/config";
|
||||
import { pleaseLogin } from "@/scripts/please-login";
|
||||
|
@ -611,15 +611,18 @@ defineExpose({
|
|||
padding: 0 32px 0 32px;
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
> :not(.line) {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
> .line {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
@ -634,6 +637,7 @@ defineExpose({
|
|||
> div > i {
|
||||
margin-left: -0.5px;
|
||||
}
|
||||
|
||||
> .info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -683,6 +687,7 @@ defineExpose({
|
|||
color: inherit;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
> .dropdownIcon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
@ -693,6 +698,7 @@ defineExpose({
|
|||
&.collapsedReply {
|
||||
.line {
|
||||
opacity: 0.25;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
@ -705,10 +711,12 @@ defineExpose({
|
|||
height: calc(50% + 5px);
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
color: var(--fgTransparentWeak);
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
|
@ -717,14 +725,17 @@ defineExpose({
|
|||
margin-right: 0.4em;
|
||||
background: var(--panelHighlight);
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
max-width: 30%;
|
||||
|
||||
&::after {
|
||||
content: ": ";
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.info {
|
||||
|
@ -754,6 +765,7 @@ defineExpose({
|
|||
display: flex;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
> .avatar {
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
|
@ -764,30 +776,36 @@ defineExpose({
|
|||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
> .header {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> .main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
> .body {
|
||||
margin-top: 0.7em;
|
||||
|
||||
> .translation {
|
||||
border: solid 0.5px var(--divider);
|
||||
border-radius: var(--radius);
|
||||
padding: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
> .renote {
|
||||
padding-top: 8px;
|
||||
|
||||
> * {
|
||||
padding: 16px;
|
||||
border: solid 1px var(--renote);
|
||||
border-radius: 8px;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
background-color: var(--panelHighlight);
|
||||
|
@ -795,6 +813,7 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -804,6 +823,7 @@ defineExpose({
|
|||
opacity: 0.7;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
> .footer {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
@ -811,6 +831,7 @@ defineExpose({
|
|||
flex-wrap: wrap;
|
||||
pointer-events: none; // Allow clicking anything w/out pointer-events: all; to open post
|
||||
margin-top: 0.4em;
|
||||
|
||||
> :deep(.button) {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
|
@ -823,6 +844,7 @@ defineExpose({
|
|||
pointer-events: all;
|
||||
height: auto;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
@ -832,17 +854,21 @@ defineExpose({
|
|||
z-index: -1;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: -0.5em;
|
||||
|
||||
&::before {
|
||||
border-radius: 100px 0 0 100px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
&::before {
|
||||
border-radius: 0 100px 100px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
|
@ -873,25 +899,32 @@ defineExpose({
|
|||
font-size: 0.975em;
|
||||
--avatarSize: 46px;
|
||||
padding-top: 6px;
|
||||
|
||||
> .note-context {
|
||||
padding-inline: 16px;
|
||||
margin-top: 8px;
|
||||
|
||||
> :not(.line) {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
> .line {
|
||||
margin-right: 10px;
|
||||
|
||||
&::before {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .article {
|
||||
padding: 18px 16px 8px;
|
||||
|
||||
&:first-child,
|
||||
&:nth-child(2) {
|
||||
padding-top: 104px;
|
||||
}
|
||||
|
||||
> .main > .header-container > .avatar {
|
||||
margin-right: 10px;
|
||||
// top: calc(14px + var(--stickyTop, 0px));
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
<span v-if="note.user.is_bot" class="is-bot">bot</span>
|
||||
</MkUserName>
|
||||
</MkA>
|
||||
<div class="username"><MkAcct :user="note.user" /></div>
|
||||
<div class="username">
|
||||
<MkAcct :user="note.user" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="info">
|
||||
|
@ -23,10 +25,10 @@
|
|||
v-tooltip.noDelay="
|
||||
i18n.t('edited', {
|
||||
date: new Date(
|
||||
note.updated_at,
|
||||
note.updated_at
|
||||
).toLocaleDateString(),
|
||||
time: new Date(
|
||||
note.updated_at,
|
||||
note.updated_at
|
||||
).toLocaleTimeString(),
|
||||
})
|
||||
"
|
||||
|
@ -48,7 +50,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { defaultStore } from "@/store";
|
||||
import MkVisibility from "@/components/MkVisibility.vue";
|
||||
import MkVisibility from "@/components/MagVisibility.vue";
|
||||
import MkInstanceTicker from "@/components/MkInstanceTicker.vue";
|
||||
import { notePage } from "@/filters/note";
|
||||
import { userPage } from "@/filters/user";
|
||||
|
@ -76,17 +78,20 @@ const showTicker =
|
|||
border-radius: 100px;
|
||||
font-size: 0.8em;
|
||||
text-shadow: 0 2px 2px var(--shadow);
|
||||
|
||||
> .avatar {
|
||||
width: 3.7em;
|
||||
height: 3.7em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
> .user-info {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
font-size: 1.2em;
|
||||
|
||||
> div {
|
||||
&:first-child {
|
||||
flex-grow: 1;
|
||||
|
@ -95,22 +100,27 @@ const showTicker =
|
|||
text-overflow: ellipsis;
|
||||
gap: 0.1em 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
max-width: 50%;
|
||||
gap: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
.article > .main & {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
&:last-child {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
> * {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
// flex: 1 1 0px;
|
||||
display: inline;
|
||||
|
@ -150,6 +160,7 @@ const showTicker =
|
|||
flex-shrink: 0;
|
||||
margin-left: 0.5em;
|
||||
font-size: 0.9em;
|
||||
|
||||
.created-at {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
|
@ -161,6 +172,7 @@ const showTicker =
|
|||
display: inline-flex;
|
||||
margin-left: 0.5em;
|
||||
vertical-align: middle;
|
||||
|
||||
> .name {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { reactive, watch } from "vue";
|
||||
import gsap from "gsap";
|
||||
import number from "@/filters/number";
|
||||
|
||||
|
@ -22,6 +22,6 @@ watch(
|
|||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -101,8 +101,8 @@
|
|||
<XNoteSimple v-if="renote" class="preview" :note="renote" />
|
||||
<div v-if="quoteId" class="with-quote">
|
||||
<i class="ph-quotes ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.quoteAttached
|
||||
}}<button class="_button" @click="quoteId = null">
|
||||
{{ i18n.ts.quoteAttached }}
|
||||
<button class="_button" @click="quoteId = null">
|
||||
<i class="ph-x ph-bold ph-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -127,8 +127,8 @@
|
|||
>{{ i18n.ts.notSpecifiedMentionWarning }} -
|
||||
<button class="_textButton" @click="addMissingMention()">
|
||||
{{ i18n.ts.add }}
|
||||
</button></MkInfo
|
||||
>
|
||||
</button>
|
||||
</MkInfo>
|
||||
<input
|
||||
v-show="useCw"
|
||||
ref="cwInputEl"
|
||||
|
@ -236,13 +236,14 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
defineAsyncComponent,
|
||||
inject,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
watch,
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
} from "vue";
|
||||
import * as mfm from "mfm-js";
|
||||
import * as misskey from "calckey-js";
|
||||
|
@ -285,7 +286,7 @@ const props = withDefaults(
|
|||
mention?: misskey.entities.User;
|
||||
specified?: misskey.entities.User;
|
||||
initialText?: string;
|
||||
initialVisibility?: typeof misskey.noteVisibilities;
|
||||
initialVisibility?: (typeof misskey.noteVisibilities)[number];
|
||||
initialFiles?: (
|
||||
| packed.PackDriveFileBase
|
||||
| misskey.entities.DriveFile
|
||||
|
@ -303,7 +304,7 @@ const props = withDefaults(
|
|||
initialVisibleUsers: () => [],
|
||||
autofocus: true,
|
||||
showMfmCheatSheet: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -332,20 +333,20 @@ let cw = ref<string | null>(null);
|
|||
let localOnly = ref<boolean>(
|
||||
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
|
||||
? defaultStore.state.localOnly
|
||||
: defaultStore.state.defaultNoteLocalOnly,
|
||||
: defaultStore.state.defaultNoteLocalOnly
|
||||
);
|
||||
let visibility = ref(
|
||||
props.initialVisibility ??
|
||||
((defaultStore.state.rememberNoteVisibility
|
||||
? defaultStore.state.visibility
|
||||
: defaultStore.state
|
||||
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
|
||||
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number])
|
||||
);
|
||||
let visibleUsers = ref([]);
|
||||
if (props.initialVisibleUsers) {
|
||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||
}
|
||||
let autocomplete = ref(null);
|
||||
let autocomplete = ref<Autocomplete[] | null>(null);
|
||||
let draghover = ref(false);
|
||||
let quoteId = ref<string | null>(null);
|
||||
let hasNotSpecifiedMentions = ref(false);
|
||||
|
@ -419,9 +420,11 @@ const canPost = computed((): boolean => {
|
|||
});
|
||||
|
||||
const withHashtags = computed(
|
||||
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
||||
defaultStore.makeGetterSetter("postFormWithHashtags")
|
||||
);
|
||||
const hashtags = computed<string>(
|
||||
defaultStore.makeGetterSetter("postFormHashtags")
|
||||
);
|
||||
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
||||
|
||||
watch(text, () => {
|
||||
checkMissingMention();
|
||||
|
@ -434,7 +437,7 @@ watch(
|
|||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (props.mention) {
|
||||
|
@ -482,7 +485,7 @@ if (props.reply && props.reply.text != null) {
|
|||
if (
|
||||
props.reply &&
|
||||
["home", "followers", "specified"].includes(
|
||||
magLegacyVisibility(props.reply.visibility),
|
||||
magLegacyVisibility(props.reply.visibility)
|
||||
)
|
||||
) {
|
||||
if (
|
||||
|
@ -492,7 +495,7 @@ if (
|
|||
visibility.value = "followers";
|
||||
} else if (
|
||||
["home", "followers"].includes(
|
||||
magLegacyVisibility(props.reply.visibility),
|
||||
magLegacyVisibility(props.reply.visibility)
|
||||
) &&
|
||||
visibility.value === "specified"
|
||||
) {
|
||||
|
@ -505,7 +508,7 @@ if (
|
|||
if (ids) {
|
||||
os.api("users/show", {
|
||||
userIds: ids.filter(
|
||||
(uid) => uid !== $i.id && uid !== props.reply!.user.id,
|
||||
(uid) => uid !== $i.id && uid !== props.reply!.user.id
|
||||
),
|
||||
}).then((users) => {
|
||||
users.forEach(pushVisibleUser);
|
||||
|
@ -516,7 +519,7 @@ if (
|
|||
os.api("users/show", { userId: props.reply.user.id }).then(
|
||||
(user) => {
|
||||
pushVisibleUser(user);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -550,7 +553,7 @@ function checkMissingMention() {
|
|||
for (const x of extractMentions(ast)) {
|
||||
if (
|
||||
!visibleUsers.value.some(
|
||||
(u) => u.username === x.username && u.host === x.host,
|
||||
(u) => u.username === x.username && u.host === x.host
|
||||
)
|
||||
) {
|
||||
hasNotSpecifiedMentions.value = true;
|
||||
|
@ -567,13 +570,13 @@ function addMissingMention() {
|
|||
for (const x of extractMentions(ast)) {
|
||||
if (
|
||||
!visibleUsers.value.some(
|
||||
(u) => u.username === x.username && u.host === x.host,
|
||||
(u) => u.username === x.username && u.host === x.host
|
||||
)
|
||||
) {
|
||||
os.api("users/show", { username: x.username, host: x.host }).then(
|
||||
(user) => {
|
||||
visibleUsers.value.push(user);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -601,7 +604,7 @@ function focus() {
|
|||
textareaEl.value.focus();
|
||||
textareaEl.value.setSelectionRange(
|
||||
textareaEl.value.value.length,
|
||||
textareaEl.value.value.length,
|
||||
textareaEl.value.value.length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -612,7 +615,7 @@ function chooseFileFrom(ev) {
|
|||
for (const file of files_) {
|
||||
files.value.push(file);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -644,7 +647,7 @@ function upload(file: File, name?: string) {
|
|||
function setVisibility() {
|
||||
os.popup(
|
||||
defineAsyncComponent(
|
||||
() => import("@/components/MkVisibilityPicker.vue"),
|
||||
() => import("@/components/MkVisibilityPicker.vue")
|
||||
),
|
||||
{
|
||||
currentVisibility: visibility.value,
|
||||
|
@ -665,14 +668,14 @@ function setVisibility() {
|
|||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
}
|
||||
|
||||
function pushVisibleUser(user) {
|
||||
if (
|
||||
!visibleUsers.value.some(
|
||||
(u) => u.username === user.username && u.host === user.host,
|
||||
(u) => u.username === user.username && u.host === user.host
|
||||
)
|
||||
) {
|
||||
visibleUsers.value.push(user);
|
||||
|
@ -697,13 +700,9 @@ function clear() {
|
|||
}
|
||||
|
||||
function onKeydown(ev: KeyboardEvent) {
|
||||
if (
|
||||
(ev.which === 10 || ev.which === 13) &&
|
||||
(ev.ctrlKey || ev.metaKey) &&
|
||||
canPost.value
|
||||
)
|
||||
if (ev.key === "Enter" && (ev.ctrlKey || ev.metaKey) && canPost.value)
|
||||
post();
|
||||
if (ev.which === 27) emit("esc");
|
||||
if (ev.key === "Escape") emit("esc");
|
||||
}
|
||||
|
||||
function onCompositionUpdate(ev: CompositionEvent) {
|
||||
|
@ -715,16 +714,24 @@ function onCompositionEnd(ev: CompositionEvent) {
|
|||
}
|
||||
|
||||
async function onPaste(ev: ClipboardEvent) {
|
||||
if (!ev.clipboardData) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const { item, i } of Array.from(ev.clipboardData.items).map(
|
||||
(item, i) => ({ item, i }),
|
||||
(item, i) => ({ item, i })
|
||||
)) {
|
||||
if (item.kind === "file") {
|
||||
const file = item.getAsFile();
|
||||
if (!file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const lio = file.name.lastIndexOf(".");
|
||||
const ext = lio >= 0 ? file.name.slice(lio) : "";
|
||||
const formatted = `${formatTimeString(
|
||||
new Date(file.lastModified),
|
||||
defaultStore.state.pastedFileName,
|
||||
defaultStore.state.pastedFileName
|
||||
).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
|
||||
upload(file, formatted);
|
||||
}
|
||||
|
@ -744,9 +751,9 @@ async function onPaste(ev: ClipboardEvent) {
|
|||
return;
|
||||
}
|
||||
|
||||
quoteId.value = paste
|
||||
.substring(url.length)
|
||||
.match(/^\/notes\/(.+?)\/?$/)[1];
|
||||
quoteId.value =
|
||||
paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ??
|
||||
null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -872,7 +879,7 @@ async function post() {
|
|||
if (postAccount.value) {
|
||||
const storedAccounts = await getAccounts();
|
||||
token = storedAccounts.find(
|
||||
(x) => x.id === postAccount.value.id,
|
||||
(x) => x.id === postAccount.value.id
|
||||
)?.token;
|
||||
}
|
||||
|
||||
|
@ -889,11 +896,11 @@ async function post() {
|
|||
.filter((x) => x.type === "hashtag")
|
||||
.map((x) => x.props.hashtag);
|
||||
const history = JSON.parse(
|
||||
localStorage.getItem("hashtags") || "[]",
|
||||
localStorage.getItem("hashtags") || "[]"
|
||||
) as string[];
|
||||
localStorage.setItem(
|
||||
"hashtags",
|
||||
JSON.stringify(unique(hashtags_.concat(history))),
|
||||
JSON.stringify(unique(hashtags_.concat(history)))
|
||||
);
|
||||
}
|
||||
posting.value = false;
|
||||
|
@ -943,7 +950,7 @@ function openAccountMenu(ev: MouseEvent) {
|
|||
}
|
||||
},
|
||||
},
|
||||
ev,
|
||||
ev
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -957,9 +964,11 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
// TODO: detach when unmount
|
||||
new Autocomplete(textareaEl.value, text);
|
||||
new Autocomplete(cwInputEl.value, cw);
|
||||
new Autocomplete(hashtagsInputEl.value, hashtags);
|
||||
autocomplete.value = [
|
||||
new Autocomplete(textareaEl.value!, text),
|
||||
new Autocomplete(cwInputEl.value!, cw),
|
||||
new Autocomplete(hashtagsInputEl.value!, hashtags),
|
||||
];
|
||||
|
||||
nextTick(() => {
|
||||
// 書きかけの投稿を復元
|
||||
|
@ -974,7 +983,7 @@ onMounted(() => {
|
|||
visibility.value = draft.data.visibility;
|
||||
localOnly.value = draft.data.localOnly;
|
||||
files.value = (draft.data.files || []).filter(
|
||||
(draftFile) => draftFile,
|
||||
(draftFile) => draftFile
|
||||
);
|
||||
if (draft.data.poll) {
|
||||
poll.value = draft.data.poll;
|
||||
|
@ -1007,6 +1016,10 @@ onMounted(() => {
|
|||
nextTick(() => watchForDraft());
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
autocomplete.value?.forEach((a) => a.detach());
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -64,15 +64,14 @@ const prependMany = async () => {
|
|||
{ context: true, attachments: true },
|
||||
{
|
||||
id: note,
|
||||
},
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.filter((p) => p.status === "fulfilled")
|
||||
.map(
|
||||
(p) =>
|
||||
(p as PromiseFulfilledResult<packed.PackNoteMaybeFull>).value,
|
||||
(p) => (p as PromiseFulfilledResult<packed.PackNoteMaybeFull>).value
|
||||
);
|
||||
|
||||
for (const n of notes) {
|
||||
|
@ -247,11 +246,4 @@ onUnmounted(() => {
|
|||
connection.dispose();
|
||||
if (connection2) connection2.dispose();
|
||||
});
|
||||
|
||||
/* TODO
|
||||
const timetravel = (date?: Date) => {
|
||||
this.date = date;
|
||||
this.$refs.tl.reload();
|
||||
};
|
||||
*/
|
||||
</script>
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
:target="target"
|
||||
@click.stop
|
||||
>
|
||||
<img class="inner" :src="url" decoding="async" />
|
||||
<img width="32" height="32" class="inner" :src="url" decoding="async" />
|
||||
</MkA>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, ref } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
|
||||
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
|
||||
import { acct, userPage } from "@/filters/user";
|
||||
|
@ -50,7 +50,7 @@ const props = withDefaults(
|
|||
target: null,
|
||||
disableLink: false,
|
||||
disablePreview: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -60,7 +60,7 @@ const emit = defineEmits<{
|
|||
const url = computed(() =>
|
||||
defaultStore.state.disableShowingAnimatedImages
|
||||
? getStaticImageUrl(props.user.avatar_url)
|
||||
: props.user.avatar_url,
|
||||
: props.user.avatar_url
|
||||
);
|
||||
|
||||
function onClick(ev: MouseEvent) {
|
||||
|
@ -76,7 +76,7 @@ watch(
|
|||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { h, shallowRef, VNodeArrayChildren, VNodeChild, watch } from "vue";
|
||||
import * as mfm from "mfm-js";
|
||||
import MkUrl from "@/components/global/MkUrl.vue";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
import MkLink from "@/components/MagLink.vue";
|
||||
import MagMention from "@/components/MagMention.vue";
|
||||
import MagMatrixMention from "@/components/MagMatrixMention.vue";
|
||||
import { concat } from "@/scripts/array";
|
||||
|
@ -50,7 +50,7 @@ const props = withDefaults(
|
|||
nowrap: false,
|
||||
author: null,
|
||||
isNote: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function render() {
|
||||
|
@ -59,7 +59,7 @@ function render() {
|
|||
let result: Result<MagNode[], MagnetarParseError> | null = null;
|
||||
if (props.mm) {
|
||||
result = parseMagnetarMarkdownXml(props.mm).flatMap(
|
||||
magnetarMarkdownToMfm,
|
||||
magnetarMarkdownToMfm
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ function render() {
|
|||
case "text": {
|
||||
const text = token.props.text.replace(
|
||||
/(\r\n|\n|\r)/g,
|
||||
"\n",
|
||||
"\n"
|
||||
);
|
||||
|
||||
if (!props.plain) {
|
||||
|
@ -129,7 +129,7 @@ function render() {
|
|||
{
|
||||
style: "font-style: oblique;",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ function render() {
|
|||
{
|
||||
class: "mfm-x2",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ function render() {
|
|||
{
|
||||
class: "mfm-x3",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -299,7 +299,7 @@ function render() {
|
|||
{
|
||||
class: "mfm-x4",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ function render() {
|
|||
{
|
||||
class: "_blur_text",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -344,26 +344,26 @@ function render() {
|
|||
}
|
||||
case "position": {
|
||||
const x = parseFloat(
|
||||
"" + token.props.args.x ?? "0",
|
||||
"" + token.props.args.x ?? "0"
|
||||
);
|
||||
const y = parseFloat(
|
||||
"" + token.props.args.y ?? "0",
|
||||
"" + token.props.args.y ?? "0"
|
||||
);
|
||||
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
||||
break;
|
||||
}
|
||||
case "crop": {
|
||||
const top = parseFloat(
|
||||
"" + token.props.args.top ?? "0",
|
||||
"" + token.props.args.top ?? "0"
|
||||
);
|
||||
const right = parseFloat(
|
||||
"" + token.props.args.right ?? "0",
|
||||
"" + token.props.args.right ?? "0"
|
||||
);
|
||||
const bottom = parseFloat(
|
||||
"" + token.props.args.bottom ?? "0",
|
||||
"" + token.props.args.bottom ?? "0"
|
||||
);
|
||||
const left = parseFloat(
|
||||
"" + token.props.args.left ?? "0",
|
||||
"" + token.props.args.left ?? "0"
|
||||
);
|
||||
style = `clip-path: inset(${top}% ${right}% ${bottom}% ${left}%);`;
|
||||
break;
|
||||
|
@ -371,11 +371,11 @@ function render() {
|
|||
case "scale": {
|
||||
const x = Math.min(
|
||||
parseFloat("" + token.props.args.x ?? "1"),
|
||||
5,
|
||||
5
|
||||
);
|
||||
const y = Math.min(
|
||||
parseFloat("" + token.props.args.y ?? "1"),
|
||||
5,
|
||||
5
|
||||
);
|
||||
style = `transform: scale(${x}, ${y});`;
|
||||
break;
|
||||
|
@ -401,7 +401,7 @@ function render() {
|
|||
{
|
||||
style: "opacity: 0.7;",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -412,7 +412,7 @@ function render() {
|
|||
{
|
||||
style: "text-align: center;",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ function render() {
|
|||
{
|
||||
style: `display: inline-block;${style}`,
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -447,7 +447,7 @@ function render() {
|
|||
{
|
||||
style: "opacity: 0.7;",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ function render() {
|
|||
{
|
||||
style: "text-align: center;",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ function render() {
|
|||
url: token.props.url,
|
||||
rel: "nofollow noopener",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -520,11 +520,11 @@ function render() {
|
|||
{
|
||||
key: Math.random(),
|
||||
to: `/tags/${encodeURIComponent(
|
||||
token.props.hashtag,
|
||||
token.props.hashtag
|
||||
)}`,
|
||||
style: "color:var(--hashtag);",
|
||||
},
|
||||
`#${token.props.hashtag}`,
|
||||
`#${token.props.hashtag}`
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ function render() {
|
|||
{
|
||||
class: "quote",
|
||||
},
|
||||
genEl(token.children),
|
||||
genEl(token.children)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -576,12 +576,12 @@ function render() {
|
|||
`:${magTransProperty(
|
||||
e,
|
||||
"shortcode",
|
||||
"name",
|
||||
)}:`,
|
||||
"name"
|
||||
)}:`
|
||||
),
|
||||
{ name, host, url: null! },
|
||||
),
|
||||
)?.url ?? null!,
|
||||
{ name, host, url: null! }
|
||||
)
|
||||
)?.url ?? null!
|
||||
);
|
||||
|
||||
if (magIsMissingEmoji(emoji)) {
|
||||
|
@ -630,7 +630,7 @@ function render() {
|
|||
case "search": {
|
||||
const sentinel = "#";
|
||||
let ast2 = (props.plain ? mfm.parseSimple : mfm.parse)(
|
||||
token.props.content + sentinel,
|
||||
token.props.content + sentinel
|
||||
);
|
||||
|
||||
const lastNode = ast2[ast2.length - 1];
|
||||
|
@ -644,7 +644,7 @@ function render() {
|
|||
) {
|
||||
lastNode.props.text = lastNode.props.text.slice(
|
||||
0,
|
||||
-1,
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -672,13 +672,13 @@ function render() {
|
|||
default: {
|
||||
console.error(
|
||||
"unrecognized ast type:",
|
||||
(token as any)?.type,
|
||||
(token as any)?.type
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
return h("span", genEl(ast));
|
||||
|
@ -692,6 +692,6 @@ watch(
|
|||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -27,7 +27,7 @@ import { ComponentProps } from "vue-component-type-helpers";
|
|||
export const pendingApiRequestsCount = ref(0);
|
||||
|
||||
const apiClient = new Misskey.api.APIClient({
|
||||
origin: url,
|
||||
origin: url
|
||||
});
|
||||
|
||||
const magnetarApiClient = new MagApiClient(`${url}/mag/v1`);
|
||||
|
@ -49,7 +49,7 @@ export async function magApi<
|
|||
| string
|
||||
| number;
|
||||
},
|
||||
token?: string | null | undefined,
|
||||
token?: string | null | undefined
|
||||
): Promise<
|
||||
T["paginated"] extends true ? PaginatedResult<T["response"]> : T["response"]
|
||||
> {
|
||||
|
@ -60,7 +60,7 @@ export async function magApi<
|
|||
endpoint,
|
||||
data,
|
||||
pathParams,
|
||||
token || $i?.token,
|
||||
token || $i?.token
|
||||
);
|
||||
} finally {
|
||||
pendingApiRequestsCount.value--;
|
||||
|
@ -75,7 +75,7 @@ export async function feApi<T extends keyof FrontendApiEndpoints & string>(
|
|||
FrontendApiEndpoints[T]["response"]
|
||||
>,
|
||||
data: FrontendApiEndpoints[T]["request"],
|
||||
token?: string | null | undefined,
|
||||
token?: string | null | undefined
|
||||
): Promise<FrontendApiEndpoints[T]["response"]> {
|
||||
type Response = FrontendApiEndpoints[T]["response"];
|
||||
|
||||
|
@ -104,7 +104,7 @@ export async function feApi<T extends keyof FrontendApiEndpoints & string>(
|
|||
endpointDef.method !== "GET" ? JSON.stringify(data) : undefined,
|
||||
credentials: "omit",
|
||||
cache: "no-cache",
|
||||
headers: authorization ? { authorization } : {},
|
||||
headers: authorization ? { authorization } : {}
|
||||
})
|
||||
.then(async (res) => {
|
||||
const body = res.status === 204 ? null : await res.json();
|
||||
|
@ -130,7 +130,7 @@ export async function feApi<T extends keyof FrontendApiEndpoints & string>(
|
|||
export const api = ((
|
||||
endpoint: string,
|
||||
data: Record<string, any> = {},
|
||||
token?: string | null | undefined,
|
||||
token?: string | null | undefined
|
||||
) => {
|
||||
pendingApiRequestsCount.value++;
|
||||
|
||||
|
@ -151,8 +151,8 @@ export const api = ((
|
|||
body: JSON.stringify(data),
|
||||
credentials: "omit",
|
||||
cache: "no-cache",
|
||||
headers: authorization ? { authorization } : {},
|
||||
},
|
||||
headers: authorization ? { authorization } : {}
|
||||
}
|
||||
)
|
||||
.then(async (res) => {
|
||||
const body = res.status === 204 ? null : await res.json();
|
||||
|
@ -176,7 +176,7 @@ export const api = ((
|
|||
export const apiGet = ((
|
||||
endpoint: string,
|
||||
data: Record<string, any> = {},
|
||||
token?: string | null | undefined,
|
||||
token?: string | null | undefined
|
||||
) => {
|
||||
pendingApiRequestsCount.value++;
|
||||
|
||||
|
@ -197,7 +197,7 @@ export const apiGet = ((
|
|||
method: "GET",
|
||||
credentials: "omit",
|
||||
cache: "default",
|
||||
headers: authorization ? { authorization } : {},
|
||||
headers: authorization ? { authorization } : {}
|
||||
})
|
||||
.then(async (res) => {
|
||||
const body = res.status === 204 ? null : await res.json();
|
||||
|
@ -221,13 +221,13 @@ export const apiGet = ((
|
|||
export const apiWithDialog = ((
|
||||
endpoint: string,
|
||||
data: Record<string, any> = {},
|
||||
token?: string | null | undefined,
|
||||
token?: string | null | undefined
|
||||
) => {
|
||||
const promise = api(endpoint, data, token);
|
||||
promiseDialog(promise, null, (err) => {
|
||||
alert({
|
||||
type: "error",
|
||||
text: err.message + "\n" + (err as any).id,
|
||||
text: err.message + "\n" + (err as any).id
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -238,7 +238,7 @@ export function promiseDialog<T extends Promise<any>>(
|
|||
promise: T,
|
||||
onSuccess?: ((res: any) => void) | null,
|
||||
onFailure?: ((err: Error) => void) | null,
|
||||
text?: string,
|
||||
text?: string
|
||||
): T {
|
||||
const showing = ref(true);
|
||||
const success = ref(false);
|
||||
|
@ -262,7 +262,7 @@ export function promiseDialog<T extends Promise<any>>(
|
|||
} else {
|
||||
alert({
|
||||
type: "error",
|
||||
text: err,
|
||||
text: err
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -273,10 +273,10 @@ export function promiseDialog<T extends Promise<any>>(
|
|||
{
|
||||
success: success,
|
||||
showing: showing,
|
||||
text: text,
|
||||
text: text
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
|
||||
return promise;
|
||||
|
@ -294,16 +294,18 @@ export const popups = ref([]) as Ref<
|
|||
const zIndexes = {
|
||||
low: 1000000,
|
||||
middle: 2000000,
|
||||
high: 3000000,
|
||||
high: 3000000
|
||||
};
|
||||
|
||||
export function claimZIndex(
|
||||
priority: "low" | "middle" | "high" = "low",
|
||||
priority: "low" | "middle" | "high" = "low"
|
||||
): number {
|
||||
zIndexes[priority] += 100;
|
||||
return zIndexes[priority];
|
||||
}
|
||||
|
||||
let uniqueId = 0;
|
||||
|
||||
export function getUniqueId(): string {
|
||||
return uniqueId++ + "";
|
||||
}
|
||||
|
@ -350,7 +352,7 @@ export async function popup<C extends Component>(
|
|||
component: C,
|
||||
props: ComponentPropsRef<C>,
|
||||
events: Partial<ComponentEmit<C>> = {} as ComponentEmit<C>,
|
||||
disposeEvent?: keyof ComponentEmit<C>,
|
||||
disposeEvent?: keyof ComponentEmit<C>
|
||||
) {
|
||||
markRaw(component);
|
||||
|
||||
|
@ -367,16 +369,16 @@ export async function popup<C extends Component>(
|
|||
events: disposeEvent
|
||||
? {
|
||||
...events,
|
||||
[disposeEvent]: dispose,
|
||||
[disposeEvent]: dispose
|
||||
}
|
||||
: events,
|
||||
id,
|
||||
id
|
||||
};
|
||||
|
||||
popups.value.push(state);
|
||||
|
||||
return {
|
||||
dispose,
|
||||
dispose
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -385,13 +387,13 @@ export function pageWindow(path: string) {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkPageWindow.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
initialPath: path,
|
||||
initialPath: path
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -400,13 +402,13 @@ export function modalPageWindow(path: string) {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkModalPageWindow.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
initialPath: path,
|
||||
initialPath: path
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -414,10 +416,10 @@ export function toast(message: string) {
|
|||
popup(
|
||||
MkToast,
|
||||
{
|
||||
message,
|
||||
message
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -436,9 +438,9 @@ export function alert(props: {
|
|||
{
|
||||
done: (result) => {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -455,14 +457,14 @@ export function confirm(props: {
|
|||
MkDialog,
|
||||
{
|
||||
...props,
|
||||
showCancelButton: true,
|
||||
showCancelButton: true
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -477,19 +479,19 @@ export function yesno(props: {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
...props,
|
||||
showCancelButton: true,
|
||||
isYesNo: true,
|
||||
isYesNo: true
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -508,7 +510,7 @@ export function inputText(props: {
|
|||
| {
|
||||
canceled: false;
|
||||
result: string;
|
||||
}
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
|
@ -522,15 +524,15 @@ export function inputText(props: {
|
|||
autocomplete: props.autocomplete,
|
||||
default: props.default,
|
||||
minLength: props.minLength,
|
||||
maxLength: props.maxLength,
|
||||
},
|
||||
maxLength: props.maxLength
|
||||
}
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -545,14 +547,14 @@ export function inputParagraph(props: {
|
|||
| {
|
||||
canceled: false;
|
||||
result: string;
|
||||
}
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
title: props.title,
|
||||
|
@ -560,15 +562,15 @@ export function inputParagraph(props: {
|
|||
input: {
|
||||
type: "paragraph",
|
||||
placeholder: props.placeholder,
|
||||
default: props.default,
|
||||
},
|
||||
default: props.default
|
||||
}
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -584,14 +586,14 @@ export function inputNumber(props: {
|
|||
| {
|
||||
canceled: false;
|
||||
result: number;
|
||||
}
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
title: props.title,
|
||||
|
@ -600,15 +602,15 @@ export function inputNumber(props: {
|
|||
type: "number",
|
||||
placeholder: props.placeholder,
|
||||
autocomplete: props.autocomplete,
|
||||
default: props.default,
|
||||
},
|
||||
default: props.default
|
||||
}
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -623,7 +625,7 @@ export function inputDate(props: {
|
|||
| {
|
||||
canceled: false;
|
||||
result: Date;
|
||||
}
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
|
@ -634,8 +636,8 @@ export function inputDate(props: {
|
|||
input: {
|
||||
type: "date",
|
||||
placeholder: props.placeholder,
|
||||
default: props.default,
|
||||
},
|
||||
default: props.default
|
||||
}
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
|
@ -643,13 +645,13 @@ export function inputDate(props: {
|
|||
result
|
||||
? {
|
||||
result: new Date(result.result),
|
||||
canceled: false,
|
||||
canceled: false
|
||||
}
|
||||
: { canceled: true },
|
||||
: { canceled: true }
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -675,13 +677,13 @@ export function select<C = any>(
|
|||
}[];
|
||||
}[];
|
||||
}
|
||||
),
|
||||
)
|
||||
): Promise<
|
||||
| { canceled: true; result: undefined }
|
||||
| {
|
||||
canceled: false;
|
||||
result: C;
|
||||
}
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
|
@ -692,15 +694,15 @@ export function select<C = any>(
|
|||
select: {
|
||||
items: props.items,
|
||||
groupedItems: props.groupedItems,
|
||||
default: props.default,
|
||||
},
|
||||
default: props.default
|
||||
}
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -715,12 +717,12 @@ export function success(): Promise<void> {
|
|||
MkWaitingDialog,
|
||||
{
|
||||
success: true,
|
||||
showing: showing,
|
||||
showing: showing
|
||||
},
|
||||
{
|
||||
done: () => resolve(),
|
||||
done: () => resolve()
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -732,12 +734,12 @@ export function waiting(): Promise<void> {
|
|||
MkWaitingDialog,
|
||||
{
|
||||
success: false,
|
||||
showing: showing,
|
||||
showing: showing
|
||||
},
|
||||
{
|
||||
done: () => resolve(),
|
||||
done: () => resolve()
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -748,34 +750,34 @@ export function form(title, form) {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkFormDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{ title, form },
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result);
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export async function selectUser() {
|
||||
export async function selectUser(): Promise<Misskey.entities.User> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkUserSelectDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{},
|
||||
{
|
||||
ok: (user) => {
|
||||
resolve(user);
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -786,15 +788,15 @@ export async function selectInstance(): Promise<Misskey.entities.Instance> {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkInstanceSelectDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{},
|
||||
{
|
||||
ok: (instance) => {
|
||||
resolve(instance);
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -805,20 +807,20 @@ export async function selectDriveFile(multiple: boolean) {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDriveSelectDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
type: "file",
|
||||
multiple,
|
||||
multiple
|
||||
},
|
||||
{
|
||||
done: (files) => {
|
||||
if (files) {
|
||||
resolve(multiple ? files : files[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -829,20 +831,20 @@ export async function selectDriveFolder(multiple: boolean) {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDriveSelectDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
type: "folder",
|
||||
multiple,
|
||||
multiple
|
||||
},
|
||||
{
|
||||
done: (folders) => {
|
||||
if (folders) {
|
||||
resolve(multiple ? folders : folders[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -853,18 +855,18 @@ export async function pickEmoji(src: HTMLElement | null, opts) {
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkEmojiPickerDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
src,
|
||||
...opts,
|
||||
...opts
|
||||
},
|
||||
{
|
||||
done: (emoji: types.Reaction) => {
|
||||
resolve(magReactionToLegacy(emoji));
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -873,25 +875,25 @@ export async function cropImage(
|
|||
image: Misskey.entities.DriveFile,
|
||||
options: {
|
||||
aspectRatio: number;
|
||||
},
|
||||
}
|
||||
): Promise<Misskey.entities.DriveFile> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkCropperDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
file: image,
|
||||
aspectRatio: options.aspectRatio,
|
||||
aspectRatio: options.aspectRatio
|
||||
},
|
||||
{
|
||||
ok: (x) => {
|
||||
resolve(x);
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
"closed"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -904,10 +906,11 @@ type AwaitType<T> =
|
|||
: T;
|
||||
let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null;
|
||||
let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null;
|
||||
|
||||
export async function openEmojiPicker(
|
||||
src?: HTMLElement,
|
||||
opts,
|
||||
initialTextarea: typeof activeTextarea,
|
||||
initialTextarea: typeof activeTextarea
|
||||
) {
|
||||
if (openingEmojiPicker) return;
|
||||
|
||||
|
@ -923,11 +926,11 @@ export async function openEmojiPicker(
|
|||
const observer = new MutationObserver((records) => {
|
||||
for (const record of records) {
|
||||
for (const node of Array.from(record.addedNodes).filter(
|
||||
(node) => node instanceof HTMLElement,
|
||||
(node) => node instanceof HTMLElement
|
||||
) as HTMLElement[]) {
|
||||
const textareas = node.querySelectorAll("textarea, input");
|
||||
for (const textarea of Array.from(textareas).filter(
|
||||
(textarea) => textarea.dataset.preventEmojiInsert == null,
|
||||
(textarea) => textarea.dataset.preventEmojiInsert == null
|
||||
)) {
|
||||
if (document.activeElement === textarea)
|
||||
activeTextarea = textarea;
|
||||
|
@ -943,18 +946,18 @@ export async function openEmojiPicker(
|
|||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false,
|
||||
characterData: false
|
||||
});
|
||||
|
||||
openingEmojiPicker = await popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkEmojiPickerDialog.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
src,
|
||||
...opts,
|
||||
...opts
|
||||
},
|
||||
{
|
||||
chosen: (emoji: types.Reaction) => {
|
||||
|
@ -967,8 +970,8 @@ export async function openEmojiPicker(
|
|||
openingEmojiPicker!.dispose();
|
||||
openingEmojiPicker = null;
|
||||
observer.disconnect();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -980,7 +983,7 @@ export function popupMenu(
|
|||
width?: number;
|
||||
viaKeyboard?: boolean;
|
||||
noReturnFocus?: boolean;
|
||||
},
|
||||
}
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let dispose;
|
||||
|
@ -988,7 +991,7 @@ export function popupMenu(
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkPopupMenu.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
items,
|
||||
|
@ -996,14 +999,14 @@ export function popupMenu(
|
|||
width: options?.width,
|
||||
align: options?.align,
|
||||
viaKeyboard: options?.viaKeyboard,
|
||||
noReturnFocus: options?.noReturnFocus,
|
||||
noReturnFocus: options?.noReturnFocus
|
||||
},
|
||||
{
|
||||
closed: () => {
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
).then((res) => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
|
@ -1012,7 +1015,7 @@ export function popupMenu(
|
|||
|
||||
export function contextMenu(
|
||||
items: MenuItem[] | Ref<MenuItem[]>,
|
||||
ev: MouseEvent,
|
||||
ev: MouseEvent
|
||||
) {
|
||||
ev.preventDefault();
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1021,18 +1024,18 @@ export function contextMenu(
|
|||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkContextMenu.vue"),
|
||||
loadingComponent: MkWaitingDialog,
|
||||
delay: 1000,
|
||||
delay: 1000
|
||||
}),
|
||||
{
|
||||
items,
|
||||
ev,
|
||||
ev
|
||||
},
|
||||
{
|
||||
closed: () => {
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
).then((res) => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
|
@ -1051,7 +1054,7 @@ export function post(props: Record<string, any> = {}) {
|
|||
closed: () => {
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
}
|
||||
}).then((res) => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
|
|
|
@ -137,10 +137,8 @@
|
|||
<template #caption>
|
||||
<MkLink
|
||||
url="https://codeberg.org/calckey/calckey/activity"
|
||||
>{{
|
||||
i18n.ts._aboutMisskey.allContributors
|
||||
}}</MkLink
|
||||
>
|
||||
>{{ i18n.ts._aboutMisskey.allContributors }}
|
||||
</MkLink>
|
||||
</template>
|
||||
</FormSection>
|
||||
</div>
|
||||
|
@ -150,11 +148,11 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount, ref, computed } from "vue";
|
||||
import { computed, nextTick, onBeforeUnmount, ref } from "vue";
|
||||
import { version } from "@/config";
|
||||
import FormLink from "@/components/form/link.vue";
|
||||
import FormSection from "@/components/form/section.vue";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
import MkLink from "@/components/MagLink.vue";
|
||||
import { physics } from "@/scripts/physics";
|
||||
import { i18n } from "@/i18n";
|
||||
import { defaultStore } from "@/store";
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader
|
||||
<template #header>
|
||||
<MkPageHeader
|
||||
v-model:tab="tab"
|
||||
:actions="headerActions"
|
||||
:tabs="headerTabs"
|
||||
/></template>
|
||||
/>
|
||||
</template>
|
||||
<MkSpacer
|
||||
v-if="instance"
|
||||
:content-max="600"
|
||||
|
@ -64,9 +65,9 @@
|
|||
>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>{{
|
||||
i18n.ts.administrator
|
||||
}}</template>
|
||||
<template #key
|
||||
>{{ i18n.ts.administrator }}
|
||||
</template>
|
||||
<template #value
|
||||
>{{
|
||||
instance.maintainerName ||
|
||||
|
@ -75,14 +76,14 @@
|
|||
({{
|
||||
instance.maintainerEmail ||
|
||||
`(${i18n.ts.unknown})`
|
||||
}})</template
|
||||
>
|
||||
}})
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.description }}</template>
|
||||
<template #value>{{
|
||||
instance.description
|
||||
}}</template>
|
||||
<template #value
|
||||
>{{ instance.description }}
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
|
||||
<FormSection v-if="iAmAdmin">
|
||||
|
@ -92,84 +93,86 @@
|
|||
v-model="suspended"
|
||||
class="_formBlock"
|
||||
@update:modelValue="toggleSuspend"
|
||||
>{{
|
||||
i18n.ts.stopActivityDelivery
|
||||
}}</FormSwitch
|
||||
>
|
||||
>{{ i18n.ts.stopActivityDelivery }}
|
||||
</FormSwitch>
|
||||
<FormSwitch
|
||||
v-model="isBlocked"
|
||||
class="_formBlock"
|
||||
@update:modelValue="toggleBlock"
|
||||
>{{ i18n.ts.blockThisInstance }}</FormSwitch
|
||||
>
|
||||
>{{ i18n.ts.blockThisInstance }}
|
||||
</FormSwitch>
|
||||
<FormSwitch
|
||||
v-model="isSilenced"
|
||||
class="_formBlock"
|
||||
@update:modelValue="toggleSilence"
|
||||
>{{
|
||||
i18n.ts.silenceThisInstance
|
||||
}}</FormSwitch
|
||||
>
|
||||
>{{ i18n.ts.silenceThisInstance }}
|
||||
</FormSwitch>
|
||||
</FormSuspense>
|
||||
<MkButton @click="refreshMetadata"
|
||||
><i
|
||||
class="ph-arrows-clockwise ph-bold ph-lg"
|
||||
></i>
|
||||
Refresh metadata</MkButton
|
||||
>
|
||||
Refresh metadata
|
||||
</MkButton>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>{{
|
||||
i18n.ts.registeredAt
|
||||
}}</template>
|
||||
<template #value
|
||||
><MkTime
|
||||
<template #key
|
||||
>{{ i18n.ts.registeredAt }}
|
||||
</template>
|
||||
<template #value>
|
||||
<MkTime
|
||||
mode="detail"
|
||||
:time="instance.caughtAt"
|
||||
/></template>
|
||||
/>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>{{
|
||||
i18n.ts.updatedAt
|
||||
}}</template>
|
||||
<template #value
|
||||
><MkTime
|
||||
<template #key
|
||||
>{{ i18n.ts.updatedAt }}
|
||||
</template>
|
||||
<template #value>
|
||||
<MkTime
|
||||
mode="detail"
|
||||
:time="instance.infoUpdatedAt"
|
||||
/></template>
|
||||
/>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>{{
|
||||
i18n.ts.latestRequestSentAt
|
||||
}}</template>
|
||||
<template #value
|
||||
><MkTime
|
||||
<template #key
|
||||
>{{ i18n.ts.latestRequestSentAt }}
|
||||
</template>
|
||||
<template #value>
|
||||
<MkTime
|
||||
v-if="instance.latestRequestSentAt"
|
||||
:time="instance.latestRequestSentAt"
|
||||
/><span v-else>N/A</span></template
|
||||
/>
|
||||
<span v-else>N/A</span></template
|
||||
>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>{{
|
||||
i18n.ts.latestStatus
|
||||
}}</template>
|
||||
<template #value>{{
|
||||
<template #key
|
||||
>{{ i18n.ts.latestStatus }}
|
||||
</template>
|
||||
<template #value
|
||||
>{{
|
||||
instance.latestStatus
|
||||
? instance.latestStatus
|
||||
: "N/A"
|
||||
}}</template>
|
||||
}}
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>{{
|
||||
i18n.ts.latestRequestReceivedAt
|
||||
}}</template>
|
||||
<template #value
|
||||
><MkTime
|
||||
<template #key
|
||||
>{{ i18n.ts.latestRequestReceivedAt }}
|
||||
</template>
|
||||
<template #value>
|
||||
<MkTime
|
||||
v-if="instance.latestRequestReceivedAt"
|
||||
:time="instance.latestRequestReceivedAt"
|
||||
/><span v-else>N/A</span></template
|
||||
/>
|
||||
<span v-else>N/A</span></template
|
||||
>
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
|
@ -177,15 +180,15 @@
|
|||
<FormSection>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>Following (Pub)</template>
|
||||
<template #value>{{
|
||||
number(instance.followingCount)
|
||||
}}</template>
|
||||
<template #value
|
||||
>{{ number(instance.followingCount) }}
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0">
|
||||
<template #key>Followers (Sub)</template>
|
||||
<template #value>{{
|
||||
number(instance.followersCount)
|
||||
}}</template>
|
||||
<template #value
|
||||
>{{ number(instance.followersCount) }}
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
|
||||
|
@ -195,32 +198,32 @@
|
|||
:to="`https://${host}/.well-known/host-meta`"
|
||||
external
|
||||
style="margin-bottom: 8px"
|
||||
>host-meta</FormLink
|
||||
>
|
||||
>host-meta
|
||||
</FormLink>
|
||||
<FormLink
|
||||
:to="`https://${host}/.well-known/host-meta.json`"
|
||||
external
|
||||
style="margin-bottom: 8px"
|
||||
>host-meta.json</FormLink
|
||||
>
|
||||
>host-meta.json
|
||||
</FormLink>
|
||||
<FormLink
|
||||
:to="`https://${host}/.well-known/nodeinfo`"
|
||||
external
|
||||
style="margin-bottom: 8px"
|
||||
>nodeinfo</FormLink
|
||||
>
|
||||
>nodeinfo
|
||||
</FormLink>
|
||||
<FormLink
|
||||
:to="`https://${host}/robots.txt`"
|
||||
external
|
||||
style="margin-bottom: 8px"
|
||||
>robots.txt</FormLink
|
||||
>
|
||||
>robots.txt
|
||||
</FormLink>
|
||||
<FormLink
|
||||
:to="`https://${host}/manifest.json`"
|
||||
external
|
||||
style="margin-bottom: 8px"
|
||||
>manifest.json</FormLink
|
||||
>
|
||||
>manifest.json
|
||||
</FormLink>
|
||||
</FormSection>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
|
@ -316,7 +319,7 @@
|
|||
:key="user.id"
|
||||
v-tooltip.mfm="
|
||||
`Last posted: ${new Date(
|
||||
user.updatedAt,
|
||||
user.updatedAt
|
||||
).toLocaleString()}`
|
||||
"
|
||||
class="user"
|
||||
|
@ -329,7 +332,7 @@
|
|||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="_formRoot">
|
||||
<MkObjectView tall :value="instance"> </MkObjectView>
|
||||
<MkObjectView tall :value="instance"></MkObjectView>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
|
@ -338,14 +341,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, ref, computed } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { Virtual } from "swiper";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import type * as calckey from "calckey-js";
|
||||
import MkChart from "@/components/MkChart.vue";
|
||||
import MkObjectView from "@/components/MkObjectView.vue";
|
||||
import FormLink from "@/components/form/link.vue";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
import MkLink from "@/components/MagLink.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import FormSection from "@/components/form/section.vue";
|
||||
import MkKeyValue from "@/components/MkKeyValue.vue";
|
||||
|
@ -425,7 +428,7 @@ async function toggleBlock() {
|
|||
blockedHosts = meta.value.blockedHosts.concat([instance.value.host]);
|
||||
} else {
|
||||
blockedHosts = meta.value.blockedHosts.filter(
|
||||
(x) => x !== instance.value!.host,
|
||||
(x) => x !== instance.value!.host
|
||||
);
|
||||
}
|
||||
await os.api("admin/update-meta", {
|
||||
|
@ -443,7 +446,7 @@ async function toggleSilence() {
|
|||
silencedHosts = meta.value.silencedHosts.concat([instance.value.host]);
|
||||
} else {
|
||||
silencedHosts = meta.value.silencedHosts.filter(
|
||||
(x) => x !== instance.value!.host,
|
||||
(x) => x !== instance.value!.host
|
||||
);
|
||||
}
|
||||
await os.api("admin/update-meta", {
|
||||
|
@ -503,7 +506,7 @@ if (iAmAdmin) {
|
|||
key: "raw",
|
||||
title: "Raw",
|
||||
icon: "ph-code ph-bold ph-lg",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<I18n :src="i18n.ts.i18nInfo" tag="span">
|
||||
<template #link>
|
||||
<MkLink url="https://hosted.weblate.org/engage/calckey/"
|
||||
>Weblate</MkLink
|
||||
>
|
||||
>Weblate
|
||||
</MkLink>
|
||||
</template>
|
||||
</I18n>
|
||||
</template>
|
||||
|
@ -34,33 +34,33 @@
|
|||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.behavior }}</template>
|
||||
<FormSwitch v-model="imageNewTab" class="_formBlock">{{
|
||||
i18n.ts.openImageInNewTab
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{
|
||||
i18n.ts.enableInfiniteScroll
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="imageNewTab" class="_formBlock"
|
||||
>{{ i18n.ts.openImageInNewTab }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="enableInfiniteScroll" class="_formBlock"
|
||||
>{{ i18n.ts.enableInfiniteScroll }}
|
||||
</FormSwitch>
|
||||
<FormSwitch
|
||||
v-model="useReactionPickerForContextMenu"
|
||||
class="_formBlock"
|
||||
>{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch
|
||||
>
|
||||
<FormSwitch v-model="swipeOnDesktop" class="_formBlock">{{
|
||||
i18n.ts.swipeOnDesktop
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="enterSendsMessage" class="_formBlock">{{
|
||||
i18n.ts.enterSendsMessage
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="disablePagesScript" class="_formBlock">{{
|
||||
i18n.ts.disablePagesScript
|
||||
}}</FormSwitch>
|
||||
>{{ i18n.ts.useReactionPickerForContextMenu }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="swipeOnDesktop" class="_formBlock"
|
||||
>{{ i18n.ts.swipeOnDesktop }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="enterSendsMessage" class="_formBlock"
|
||||
>{{ i18n.ts.enterSendsMessage }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="disablePagesScript" class="_formBlock"
|
||||
>{{ i18n.ts.disablePagesScript }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="showTimelineReplies" class="_formBlock"
|
||||
>{{ i18n.ts.flagShowTimelineReplies
|
||||
}}<template #caption
|
||||
>{{ i18n.ts.flagShowTimelineReplies }}
|
||||
<template #caption
|
||||
>{{ i18n.ts.flagShowTimelineRepliesDescription }}
|
||||
{{ i18n.ts.reflectMayTakeTime }}</template
|
||||
></FormSwitch
|
||||
>
|
||||
{{ i18n.ts.reflectMayTakeTime }}
|
||||
</template>
|
||||
</FormSwitch>
|
||||
|
||||
<FormSelect v-model="serverDisconnectedBehavior" class="_formBlock">
|
||||
<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
|
||||
|
@ -82,16 +82,16 @@
|
|||
<FormSection>
|
||||
<template #label>{{ i18n.ts.accessibility }}</template>
|
||||
<FormSwitch v-model="expandOnNoteClick" class="_formBlock"
|
||||
>{{ i18n.ts.expandOnNoteClick
|
||||
}}<template #caption>{{
|
||||
i18n.ts.expandOnNoteClickDesc
|
||||
}}</template>
|
||||
>{{ i18n.ts.expandOnNoteClick }}
|
||||
<template #caption
|
||||
>{{ i18n.ts.expandOnNoteClickDesc }}
|
||||
</template>
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="advancedMfm" class="_formBlock">
|
||||
{{ i18n.ts._mfm.advanced
|
||||
}}<template #caption>{{
|
||||
i18n.ts._mfm.advancedDescription
|
||||
}}</template>
|
||||
{{ i18n.ts._mfm.advanced }}
|
||||
<template #caption
|
||||
>{{ i18n.ts._mfm.advancedDescription }}
|
||||
</template>
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="autoplayMfm" class="_formBlock">
|
||||
{{ i18n.ts._mfm.alwaysPlay }}
|
||||
|
@ -103,14 +103,14 @@
|
|||
{{ i18n.ts._mfm.warn }}
|
||||
</template>
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="reduceAnimation" class="_formBlock">{{
|
||||
i18n.ts.reduceUiAnimation
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="reduceAnimation" class="_formBlock"
|
||||
>{{ i18n.ts.reduceUiAnimation }}
|
||||
</FormSwitch>
|
||||
<FormSwitch
|
||||
v-model="disableShowingAnimatedImages"
|
||||
class="_formBlock"
|
||||
>{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch
|
||||
>
|
||||
>{{ i18n.ts.disableShowingAnimatedImages }}
|
||||
</FormSwitch>
|
||||
<FormRadios v-model="fontSize" class="_formBlock">
|
||||
<template #label>{{ i18n.ts.fontSize }}</template>
|
||||
<option :value="null">
|
||||
|
@ -146,44 +146,44 @@
|
|||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.appearance }}</template>
|
||||
<FormSwitch v-model="useBlurEffect" class="_formBlock">{{
|
||||
i18n.ts.useBlurEffect
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{
|
||||
i18n.ts.useBlurEffectForModal
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="useBlurEffect" class="_formBlock"
|
||||
>{{ i18n.ts.useBlurEffect }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="useBlurEffectForModal" class="_formBlock"
|
||||
>{{ i18n.ts.useBlurEffectForModal }}
|
||||
</FormSwitch>
|
||||
<FormSwitch
|
||||
v-model="showGapBetweenNotesInTimeline"
|
||||
class="_formBlock"
|
||||
>{{ i18n.ts.showGapBetweenNotesInTimeline }}</FormSwitch
|
||||
>
|
||||
<FormSwitch v-model="loadRawImages" class="_formBlock">{{
|
||||
i18n.ts.loadRawImages
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="squareAvatars" class="_formBlock">{{
|
||||
i18n.ts.squareAvatars
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="seperateRenoteQuote" class="_formBlock">{{
|
||||
i18n.ts.seperateRenoteQuote
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="useSystemFont" class="_formBlock">{{
|
||||
i18n.ts.useSystemFont
|
||||
}}</FormSwitch>
|
||||
>{{ i18n.ts.showGapBetweenNotesInTimeline }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="loadRawImages" class="_formBlock"
|
||||
>{{ i18n.ts.loadRawImages }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="squareAvatars" class="_formBlock"
|
||||
>{{ i18n.ts.squareAvatars }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="seperateRenoteQuote" class="_formBlock"
|
||||
>{{ i18n.ts.seperateRenoteQuote }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="useSystemFont" class="_formBlock"
|
||||
>{{ i18n.ts.useSystemFont }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="useOsNativeEmojis" class="_formBlock">
|
||||
{{ i18n.ts.useOsNativeEmojis }}
|
||||
<div>
|
||||
<Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" />
|
||||
</div>
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="disableDrawer" class="_formBlock">{{
|
||||
i18n.ts.disableDrawer
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="showUpdates" class="_formBlock">{{
|
||||
i18n.ts.showUpdates
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="showFixedPostForm" class="_formBlock">{{
|
||||
i18n.ts.showFixedPostForm
|
||||
}}</FormSwitch>
|
||||
<FormSwitch v-model="disableDrawer" class="_formBlock"
|
||||
>{{ i18n.ts.disableDrawer }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="showUpdates" class="_formBlock"
|
||||
>{{ i18n.ts.showUpdates }}
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="showFixedPostForm" class="_formBlock"
|
||||
>{{ i18n.ts.showFixedPostForm }}
|
||||
</FormSwitch>
|
||||
<FormSelect v-model="instanceTicker" class="_formBlock">
|
||||
<template #label>{{ i18n.ts.instanceTicker }}</template>
|
||||
<option value="none">{{ i18n.ts._instanceTicker.none }}</option>
|
||||
|
@ -212,19 +212,19 @@
|
|||
class="_formBlock"
|
||||
>
|
||||
<template #label>{{ i18n.ts.numberOfPageCache }}</template>
|
||||
<template #caption>{{
|
||||
i18n.ts.numberOfPageCacheDescription
|
||||
}}</template>
|
||||
<template #caption
|
||||
>{{ i18n.ts.numberOfPageCacheDescription }}
|
||||
</template>
|
||||
</FormRange>
|
||||
|
||||
<FormLink to="/settings/deck" class="_formBlock">{{
|
||||
i18n.ts.deck
|
||||
}}</FormLink>
|
||||
<FormLink to="/settings/deck" class="_formBlock"
|
||||
>{{ i18n.ts.deck }}
|
||||
</FormLink>
|
||||
|
||||
<FormLink to="/settings/custom-katex-macro" class="_formBlock"
|
||||
><template #icon><i class="ph-radical ph-bold ph-lg"></i></template
|
||||
>{{ i18n.ts.customKaTeXMacro }}</FormLink
|
||||
>
|
||||
<FormLink to="/settings/custom-katex-macro" class="_formBlock">
|
||||
<template #icon><i class="ph-radical ph-bold ph-lg"></i></template>
|
||||
{{ i18n.ts.customKaTeXMacro }}
|
||||
</FormLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -236,7 +236,7 @@ import FormRadios from "@/components/form/radios.vue";
|
|||
import FormRange from "@/components/form/range.vue";
|
||||
import FormSection from "@/components/form/section.vue";
|
||||
import FormLink from "@/components/form/link.vue";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
import MkLink from "@/components/MagLink.vue";
|
||||
import { langs } from "@/config";
|
||||
import { defaultStore } from "@/store";
|
||||
import * as os from "@/os";
|
||||
|
@ -259,24 +259,24 @@ async function reloadAsk() {
|
|||
}
|
||||
|
||||
const overridedDeviceKind = computed(
|
||||
defaultStore.makeGetterSetter("overridedDeviceKind"),
|
||||
defaultStore.makeGetterSetter("overridedDeviceKind")
|
||||
);
|
||||
const serverDisconnectedBehavior = computed(
|
||||
defaultStore.makeGetterSetter("serverDisconnectedBehavior"),
|
||||
defaultStore.makeGetterSetter("serverDisconnectedBehavior")
|
||||
);
|
||||
const reduceAnimation = computed(
|
||||
defaultStore.makeGetterSetter(
|
||||
"animation",
|
||||
(v) => !v,
|
||||
(v) => !v,
|
||||
),
|
||||
(v) => !v
|
||||
)
|
||||
);
|
||||
const useBlurEffectForModal = computed(
|
||||
defaultStore.makeGetterSetter("useBlurEffectForModal"),
|
||||
defaultStore.makeGetterSetter("useBlurEffectForModal")
|
||||
);
|
||||
const useBlurEffect = computed(defaultStore.makeGetterSetter("useBlurEffect"));
|
||||
const showGapBetweenNotesInTimeline = computed(
|
||||
defaultStore.makeGetterSetter("showGapBetweenNotesInTimeline"),
|
||||
defaultStore.makeGetterSetter("showGapBetweenNotesInTimeline")
|
||||
);
|
||||
const showAds = computed(defaultStore.makeGetterSetter("showAds"));
|
||||
const advancedMfm = computed(defaultStore.makeGetterSetter("advancedMfm"));
|
||||
|
@ -284,53 +284,53 @@ const autoplayMfm = computed(
|
|||
defaultStore.makeGetterSetter(
|
||||
"animatedMfm",
|
||||
(v) => !v,
|
||||
(v) => !v,
|
||||
),
|
||||
(v) => !v
|
||||
)
|
||||
);
|
||||
const useOsNativeEmojis = computed(
|
||||
defaultStore.makeGetterSetter("useOsNativeEmojis"),
|
||||
defaultStore.makeGetterSetter("useOsNativeEmojis")
|
||||
);
|
||||
const disableDrawer = computed(defaultStore.makeGetterSetter("disableDrawer"));
|
||||
const disableShowingAnimatedImages = computed(
|
||||
defaultStore.makeGetterSetter("disableShowingAnimatedImages"),
|
||||
defaultStore.makeGetterSetter("disableShowingAnimatedImages")
|
||||
);
|
||||
const loadRawImages = computed(defaultStore.makeGetterSetter("loadRawImages"));
|
||||
const imageNewTab = computed(defaultStore.makeGetterSetter("imageNewTab"));
|
||||
const nsfw = computed(defaultStore.makeGetterSetter("nsfw"));
|
||||
const disablePagesScript = computed(
|
||||
defaultStore.makeGetterSetter("disablePagesScript"),
|
||||
defaultStore.makeGetterSetter("disablePagesScript")
|
||||
);
|
||||
const expandOnNoteClick = computed(
|
||||
defaultStore.makeGetterSetter("expandOnNoteClick"),
|
||||
defaultStore.makeGetterSetter("expandOnNoteClick")
|
||||
);
|
||||
const showFixedPostForm = computed(
|
||||
defaultStore.makeGetterSetter("showFixedPostForm"),
|
||||
defaultStore.makeGetterSetter("showFixedPostForm")
|
||||
);
|
||||
const numberOfPageCache = computed(
|
||||
defaultStore.makeGetterSetter("numberOfPageCache"),
|
||||
defaultStore.makeGetterSetter("numberOfPageCache")
|
||||
);
|
||||
const instanceTicker = computed(
|
||||
defaultStore.makeGetterSetter("instanceTicker"),
|
||||
defaultStore.makeGetterSetter("instanceTicker")
|
||||
);
|
||||
const enableInfiniteScroll = computed(
|
||||
defaultStore.makeGetterSetter("enableInfiniteScroll"),
|
||||
defaultStore.makeGetterSetter("enableInfiniteScroll")
|
||||
);
|
||||
const enterSendsMessage = computed(
|
||||
defaultStore.makeGetterSetter("enterSendsMessage"),
|
||||
defaultStore.makeGetterSetter("enterSendsMessage")
|
||||
);
|
||||
const useReactionPickerForContextMenu = computed(
|
||||
defaultStore.makeGetterSetter("useReactionPickerForContextMenu"),
|
||||
defaultStore.makeGetterSetter("useReactionPickerForContextMenu")
|
||||
);
|
||||
const seperateRenoteQuote = computed(
|
||||
defaultStore.makeGetterSetter("seperateRenoteQuote"),
|
||||
defaultStore.makeGetterSetter("seperateRenoteQuote")
|
||||
);
|
||||
const squareAvatars = computed(defaultStore.makeGetterSetter("squareAvatars"));
|
||||
const showUpdates = computed(defaultStore.makeGetterSetter("showUpdates"));
|
||||
const swipeOnDesktop = computed(
|
||||
defaultStore.makeGetterSetter("swipeOnDesktop"),
|
||||
defaultStore.makeGetterSetter("swipeOnDesktop")
|
||||
);
|
||||
const showTimelineReplies = computed(
|
||||
defaultStore.makeGetterSetter("showTimelineReplies"),
|
||||
defaultStore.makeGetterSetter("showTimelineReplies")
|
||||
);
|
||||
|
||||
watch(lang, () => {
|
||||
|
@ -374,7 +374,7 @@ watch(
|
|||
],
|
||||
async () => {
|
||||
await reloadAsk();
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<div class="main">
|
||||
<div class="profile">
|
||||
<MkMoved
|
||||
<MagMoved
|
||||
v-if="user.moved_to"
|
||||
:host="user.moved_to.host"
|
||||
:acct="user.moved_to.username"
|
||||
|
@ -274,10 +274,12 @@
|
|||
<dd class="value">
|
||||
{{
|
||||
new Date(
|
||||
user.created_at,
|
||||
user.created_at
|
||||
).toLocaleString()
|
||||
}}
|
||||
(<MkTime :time="user.created_at" />)
|
||||
(
|
||||
<MkTime :time="user.created_at" />
|
||||
)
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
@ -337,8 +339,8 @@
|
|||
<MkInfo
|
||||
v-else-if="$i && $i.id === user.id"
|
||||
style="margin: 12px 0"
|
||||
>{{ i18n.ts.userPagePinTip }}</MkInfo
|
||||
>
|
||||
>{{ i18n.ts.userPagePinTip }}
|
||||
</MkInfo>
|
||||
<template v-if="narrow">
|
||||
<XPhotos :key="user.id" :user="user" />
|
||||
<XActivity
|
||||
|
@ -366,11 +368,11 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
ref,
|
||||
computed,
|
||||
} from "vue";
|
||||
import calcAge from "s-age";
|
||||
import cityTimezones from "city-timezones";
|
||||
|
@ -378,7 +380,7 @@ import XUserTimeline from "./index.timeline.vue";
|
|||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||
import MkRemoteCaution from "@/components/MkRemoteCaution.vue";
|
||||
import MkInfo from "@/components/MkInfo.vue";
|
||||
import MkMoved from "@/components/MkMoved.vue";
|
||||
import MagMoved from "@/components/MagMoved.vue";
|
||||
import XNote from "@/components/MagNote.vue";
|
||||
import { getScrollPosition } from "@/scripts/scroll";
|
||||
import { userPage } from "@/filters/user";
|
||||
|
@ -400,7 +402,7 @@ const props = withDefaults(
|
|||
defineProps<{
|
||||
user: packed.PackUserMaybeAll;
|
||||
}>(),
|
||||
{},
|
||||
{}
|
||||
);
|
||||
|
||||
let parallaxAnimationId = ref<null | number>(null);
|
||||
|
@ -425,16 +427,16 @@ const timeForThem = computed(() => {
|
|||
props.user
|
||||
.location!.replace(
|
||||
/[^A-Za-z0-9ÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź\-'.s].*/,
|
||||
"",
|
||||
""
|
||||
)
|
||||
.trim(),
|
||||
props.user.location!.replace(
|
||||
/[^A-Za-zÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź\-'.].*/,
|
||||
"",
|
||||
""
|
||||
),
|
||||
props.user.location!.replace(
|
||||
/[^A-Za-zÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź].*/,
|
||||
"",
|
||||
""
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -668,6 +670,7 @@ onUnmounted(() => {
|
|||
|
||||
> .nameColumn {
|
||||
display: block;
|
||||
|
||||
> .name {
|
||||
margin: 0;
|
||||
align-content: center;
|
||||
|
@ -876,6 +879,7 @@ onUnmounted(() => {
|
|||
height: auto;
|
||||
border-bottom: 1px solid var(--divider);
|
||||
padding-bottom: 5px;
|
||||
|
||||
> .actions {
|
||||
position: static;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { nextTick, Ref, ref, defineAsyncComponent } from "vue";
|
||||
import { defineAsyncComponent, nextTick, ref, Ref } from "vue";
|
||||
import getCaretCoordinates from "textarea-caret";
|
||||
import { toASCII } from "punycode/";
|
||||
import { popup } from "@/os";
|
||||
|
@ -28,7 +28,7 @@ export class Autocomplete {
|
|||
*/
|
||||
constructor(
|
||||
textarea: HTMLInputElement | HTMLTextAreaElement,
|
||||
textRef: Ref<string>,
|
||||
textRef: Ref<string>
|
||||
) {
|
||||
//#region BIND
|
||||
this.onInput = this.onInput.bind(this);
|
||||
|
@ -63,8 +63,9 @@ export class Autocomplete {
|
|||
* テキスト入力時
|
||||
*/
|
||||
private onInput() {
|
||||
const caretPos = this.textarea.selectionStart;
|
||||
const text = this.text.substr(0, caretPos).split("\n").pop()!;
|
||||
const caretPos = this.textarea.selectionStart ?? undefined;
|
||||
|
||||
const text = this.text.substring(0, caretPos).split("\n").pop()!;
|
||||
|
||||
const mentionIndex = text.lastIndexOf("@");
|
||||
const hashtagIndex = text.lastIndexOf("#");
|
||||
|
@ -75,7 +76,7 @@ export class Autocomplete {
|
|||
mentionIndex,
|
||||
hashtagIndex,
|
||||
emojiIndex,
|
||||
mfmTagIndex,
|
||||
mfmTagIndex
|
||||
);
|
||||
|
||||
if (max === -1) {
|
||||
|
@ -96,7 +97,7 @@ export class Autocomplete {
|
|||
let opened = false;
|
||||
|
||||
if (isMention) {
|
||||
const username = text.substr(mentionIndex + 1);
|
||||
const username = text.substring(mentionIndex + 1);
|
||||
if (username !== "" && username.match(/^[a-zA-Z0-9_]+$/)) {
|
||||
this.open("user", username);
|
||||
opened = true;
|
||||
|
@ -107,7 +108,7 @@ export class Autocomplete {
|
|||
}
|
||||
|
||||
if (isHashtag && !opened) {
|
||||
const hashtag = text.substr(hashtagIndex + 1);
|
||||
const hashtag = text.substring(hashtagIndex + 1);
|
||||
if (!hashtag.includes(" ")) {
|
||||
this.open("hashtag", hashtag);
|
||||
opened = true;
|
||||
|
@ -115,7 +116,7 @@ export class Autocomplete {
|
|||
}
|
||||
|
||||
if (isEmoji && !opened) {
|
||||
const emoji = text.substr(emojiIndex + 1);
|
||||
const emoji = text.substring(emojiIndex + 1);
|
||||
if (!emoji.includes(" ")) {
|
||||
this.open("emoji", emoji);
|
||||
opened = true;
|
||||
|
@ -123,7 +124,7 @@ export class Autocomplete {
|
|||
}
|
||||
|
||||
if (isMfmTag && !opened) {
|
||||
const mfmTag = text.substr(mfmTagIndex + 1);
|
||||
const mfmTag = text.substring(mfmTagIndex + 1);
|
||||
if (!mfmTag.includes(" ")) {
|
||||
this.open("mfmTag", mfmTag.replace("[", ""));
|
||||
opened = true;
|
||||
|
@ -149,7 +150,7 @@ export class Autocomplete {
|
|||
//#region サジェストを表示すべき位置を計算
|
||||
const caretPosition = getCaretCoordinates(
|
||||
this.textarea,
|
||||
this.textarea.selectionStart,
|
||||
this.textarea.selectionStart
|
||||
);
|
||||
|
||||
const rect = this.textarea.getBoundingClientRect();
|
||||
|
@ -171,7 +172,7 @@ export class Autocomplete {
|
|||
|
||||
const { dispose } = await popup(
|
||||
defineAsyncComponent(
|
||||
() => import("@/components/MkAutocomplete.vue"),
|
||||
() => import("@/components/MkAutocomplete.vue")
|
||||
),
|
||||
{
|
||||
textarea: this.textarea,
|
||||
|
@ -185,7 +186,7 @@ export class Autocomplete {
|
|||
done: (res) => {
|
||||
this.complete(res);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.suggestion = {
|
||||
|
@ -217,14 +218,14 @@ export class Autocomplete {
|
|||
private complete({ type, value }) {
|
||||
this.close();
|
||||
|
||||
const caret = this.textarea.selectionStart;
|
||||
const caret = this.textarea.selectionStart ?? undefined;
|
||||
|
||||
if (type === "user") {
|
||||
const source = this.text;
|
||||
|
||||
const before = source.substr(0, caret);
|
||||
const before = source.substring(0, caret);
|
||||
const trimmedBefore = before.substring(0, before.lastIndexOf("@"));
|
||||
const after = source.substr(caret);
|
||||
const after = source.substring(caret);
|
||||
|
||||
const acct =
|
||||
value.host === null
|
||||
|
@ -243,9 +244,9 @@ export class Autocomplete {
|
|||
} else if (type === "hashtag") {
|
||||
const source = this.text;
|
||||
|
||||
const before = source.substr(0, caret);
|
||||
const before = source.substring(0, caret);
|
||||
const trimmedBefore = before.substring(0, before.lastIndexOf("#"));
|
||||
const after = source.substr(caret);
|
||||
const after = source.substring(caret);
|
||||
|
||||
// 挿入
|
||||
this.text = `${trimmedBefore}#${value} ${after}`;
|
||||
|
@ -259,9 +260,9 @@ export class Autocomplete {
|
|||
} else if (type === "emoji") {
|
||||
const source = this.text;
|
||||
|
||||
const before = source.substr(0, caret);
|
||||
const before = source.substring(0, caret);
|
||||
const trimmedBefore = before.substring(0, before.lastIndexOf(":"));
|
||||
const after = source.substr(caret);
|
||||
const after = source.substring(caret);
|
||||
|
||||
// 挿入
|
||||
this.text = trimmedBefore + value + after;
|
||||
|
@ -275,9 +276,9 @@ export class Autocomplete {
|
|||
} else if (type === "mfmTag") {
|
||||
const source = this.text;
|
||||
|
||||
const before = source.substr(0, caret);
|
||||
const before = source.substring(0, caret);
|
||||
const trimmedBefore = before.substring(0, before.lastIndexOf("$"));
|
||||
const after = source.substr(caret);
|
||||
const after = source.substring(caret);
|
||||
|
||||
// 挿入
|
||||
this.text = `${trimmedBefore}$[${value} ]${after}`;
|
||||
|
|
|
@ -118,13 +118,13 @@ importers:
|
|||
version: 2.1.1
|
||||
'@rollup/plugin-alias':
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0(rollup@4.17.1)
|
||||
version: 5.1.0(rollup@4.17.2)
|
||||
'@rollup/plugin-json':
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(rollup@4.17.1)
|
||||
version: 6.1.0(rollup@4.17.2)
|
||||
'@rollup/pluginutils':
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0(rollup@4.17.1)
|
||||
version: 5.1.0(rollup@4.17.2)
|
||||
'@types/escape-regexp':
|
||||
specifier: 0.0.1
|
||||
version: 0.0.1
|
||||
|
@ -273,8 +273,8 @@ importers:
|
|||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
rollup:
|
||||
specifier: ^4.17.1
|
||||
version: 4.17.1
|
||||
specifier: ^4.17.2
|
||||
version: 4.17.2
|
||||
s-age:
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2
|
||||
|
@ -345,8 +345,8 @@ importers:
|
|||
specifier: ^3.4.26
|
||||
version: 3.4.26(typescript@5.4.5)
|
||||
vue-component-type-helpers:
|
||||
specifier: ^2.0.14
|
||||
version: 2.0.14
|
||||
specifier: ^2.0.15
|
||||
version: 2.0.15
|
||||
vue-isyourpasswordsafe:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
|
@ -426,13 +426,13 @@ packages:
|
|||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-validator-identifier@7.22.20:
|
||||
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
|
||||
/@babel/helper-validator-identifier@7.22.5:
|
||||
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-validator-identifier@7.22.5:
|
||||
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
|
||||
/@babel/helper-validator-identifier@7.24.5:
|
||||
resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
|
@ -445,12 +445,12 @@ packages:
|
|||
js-tokens: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@babel/parser@7.24.4:
|
||||
resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==}
|
||||
/@babel/parser@7.24.5:
|
||||
resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/types': 7.24.0
|
||||
'@babel/types': 7.24.5
|
||||
dev: true
|
||||
|
||||
/@babel/runtime@7.20.7:
|
||||
|
@ -460,19 +460,19 @@ packages:
|
|||
regenerator-runtime: 0.13.11
|
||||
dev: true
|
||||
|
||||
/@babel/runtime@7.24.4:
|
||||
resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==}
|
||||
/@babel/runtime@7.24.5:
|
||||
resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
dev: true
|
||||
|
||||
/@babel/types@7.24.0:
|
||||
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
|
||||
/@babel/types@7.24.5:
|
||||
resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.24.1
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
'@babel/helper-validator-identifier': 7.24.5
|
||||
to-fast-properties: 2.0.0
|
||||
dev: true
|
||||
|
||||
|
@ -979,7 +979,7 @@ packages:
|
|||
resolution: {integrity: sha512-QjrfbItu5Rb2i37GzsKxmrRHfZPTVk3oXSPBnQ2+oACDbQRWGAeB0AsvZw263n1nFouQuff+khOCtRbrc6+k+A==}
|
||||
dev: true
|
||||
|
||||
/@rollup/plugin-alias@5.1.0(rollup@4.17.1):
|
||||
/@rollup/plugin-alias@5.1.0(rollup@4.17.2):
|
||||
resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -988,11 +988,11 @@ packages:
|
|||
rollup:
|
||||
optional: true
|
||||
dependencies:
|
||||
rollup: 4.17.1
|
||||
rollup: 4.17.2
|
||||
slash: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@rollup/plugin-json@6.1.0(rollup@4.17.1):
|
||||
/@rollup/plugin-json@6.1.0(rollup@4.17.2):
|
||||
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -1001,11 +1001,11 @@ packages:
|
|||
rollup:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.0(rollup@4.17.1)
|
||||
rollup: 4.17.1
|
||||
'@rollup/pluginutils': 5.1.0(rollup@4.17.2)
|
||||
rollup: 4.17.2
|
||||
dev: true
|
||||
|
||||
/@rollup/pluginutils@5.1.0(rollup@4.17.1):
|
||||
/@rollup/pluginutils@5.1.0(rollup@4.17.2):
|
||||
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -1017,131 +1017,131 @@ packages:
|
|||
'@types/estree': 1.0.5
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 2.3.1
|
||||
rollup: 4.17.1
|
||||
rollup: 4.17.2
|
||||
dev: true
|
||||
|
||||
/@rollup/rollup-android-arm-eabi@4.17.1:
|
||||
resolution: {integrity: sha512-P6Wg856Ou/DLpR+O0ZLneNmrv7QpqBg+hK4wE05ijbC/t349BRfMfx+UFj5Ha3fCFopIa6iSZlpdaB4agkWp2Q==}
|
||||
/@rollup/rollup-android-arm-eabi@4.17.2:
|
||||
resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-android-arm64@4.17.1:
|
||||
resolution: {integrity: sha512-piwZDjuW2WiHr05djVdUkrG5JbjnGbtx8BXQchYCMfib/nhjzWoiScelZ+s5IJI7lecrwSxHCzW026MWBL+oJQ==}
|
||||
/@rollup/rollup-android-arm64@4.17.2:
|
||||
resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-arm64@4.17.1:
|
||||
resolution: {integrity: sha512-LsZXXIsN5Q460cKDT4Y+bzoPDhBmO5DTr7wP80d+2EnYlxSgkwdPfE3hbE+Fk8dtya+8092N9srjBTJ0di8RIA==}
|
||||
/@rollup/rollup-darwin-arm64@4.17.2:
|
||||
resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-x64@4.17.1:
|
||||
resolution: {integrity: sha512-S7TYNQpWXB9APkxu/SLmYHezWwCoZRA9QLgrDeml+SR2A1LLPD2DBUdUlvmCF7FUpRMKvbeeWky+iizQj65Etw==}
|
||||
/@rollup/rollup-darwin-x64@4.17.2:
|
||||
resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.17.1:
|
||||
resolution: {integrity: sha512-Lq2JR5a5jsA5um2ZoLiXXEaOagnVyCpCW7xvlcqHC7y46tLwTEgUSTM3a2TfmmTMmdqv+jknUioWXlmxYxE9Yw==}
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.17.2:
|
||||
resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm-musleabihf@4.17.1:
|
||||
resolution: {integrity: sha512-9BfzwyPNV0IizQoR+5HTNBGkh1KXE8BqU0DBkqMngmyFW7BfuIZyMjQ0s6igJEiPSBvT3ZcnIFohZ19OqjhDPg==}
|
||||
/@rollup/rollup-linux-arm-musleabihf@4.17.2:
|
||||
resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-gnu@4.17.1:
|
||||
resolution: {integrity: sha512-e2uWaoxo/rtzA52OifrTSXTvJhAXb0XeRkz4CdHBK2KtxrFmuU/uNd544Ogkpu938BzEfvmWs8NZ8Axhw33FDw==}
|
||||
/@rollup/rollup-linux-arm64-gnu@4.17.2:
|
||||
resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-musl@4.17.1:
|
||||
resolution: {integrity: sha512-ekggix/Bc/d/60H1Mi4YeYb/7dbal1kEDZ6sIFVAE8pUSx7PiWeEh+NWbL7bGu0X68BBIkgF3ibRJe1oFTksQQ==}
|
||||
/@rollup/rollup-linux-arm64-musl@4.17.2:
|
||||
resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-powerpc64le-gnu@4.17.1:
|
||||
resolution: {integrity: sha512-UGV0dUo/xCv4pkr/C8KY7XLFwBNnvladt8q+VmdKrw/3RUd3rD0TptwjisvE2TTnnlENtuY4/PZuoOYRiGp8Gw==}
|
||||
/@rollup/rollup-linux-powerpc64le-gnu@4.17.2:
|
||||
resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-riscv64-gnu@4.17.1:
|
||||
resolution: {integrity: sha512-gEYmYYHaehdvX46mwXrU49vD6Euf1Bxhq9pPb82cbUU9UT2NV+RSckQ5tKWOnNXZixKsy8/cPGtiUWqzPuAcXQ==}
|
||||
/@rollup/rollup-linux-riscv64-gnu@4.17.2:
|
||||
resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-s390x-gnu@4.17.1:
|
||||
resolution: {integrity: sha512-xeae5pMAxHFp6yX5vajInG2toST5lsCTrckSRUFwNgzYqnUjNBcQyqk1bXUxX5yhjWFl2Mnz3F8vQjl+2FRIcw==}
|
||||
/@rollup/rollup-linux-s390x-gnu@4.17.2:
|
||||
resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-gnu@4.17.1:
|
||||
resolution: {integrity: sha512-AsdnINQoDWfKpBzCPqQWxSPdAWzSgnYbrJYtn6W0H2E9It5bZss99PiLA8CgmDRfvKygt20UpZ3xkhFlIfX9zQ==}
|
||||
/@rollup/rollup-linux-x64-gnu@4.17.2:
|
||||
resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-musl@4.17.1:
|
||||
resolution: {integrity: sha512-KoB4fyKXTR+wYENkIG3fFF+5G6N4GFvzYx8Jax8BR4vmddtuqSb5oQmYu2Uu067vT/Fod7gxeQYKupm8gAcMSQ==}
|
||||
/@rollup/rollup-linux-x64-musl@4.17.2:
|
||||
resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-arm64-msvc@4.17.1:
|
||||
resolution: {integrity: sha512-J0d3NVNf7wBL9t4blCNat+d0PYqAx8wOoY+/9Q5cujnafbX7BmtYk3XvzkqLmFECaWvXGLuHmKj/wrILUinmQg==}
|
||||
/@rollup/rollup-win32-arm64-msvc@4.17.2:
|
||||
resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-ia32-msvc@4.17.1:
|
||||
resolution: {integrity: sha512-xjgkWUwlq7IbgJSIxvl516FJ2iuC/7ttjsAxSPpC9kkI5iQQFHKyEN5BjbhvJ/IXIZ3yIBcW5QDlWAyrA+TFag==}
|
||||
/@rollup/rollup-win32-ia32-msvc@4.17.2:
|
||||
resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-x64-msvc@4.17.1:
|
||||
resolution: {integrity: sha512-0QbCkfk6cnnVKWqqlC0cUrrUMDMfu5ffvYMTUHf+qMN2uAb3MKP31LPcwiMXBNsvoFGs/kYdFOsuLmvppCopXA==}
|
||||
/@rollup/rollup-win32-x64-msvc@4.17.2:
|
||||
resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
|
@ -1567,7 +1567,7 @@ packages:
|
|||
resolution: {integrity: sha512-pvZdJ004TpC4Ohk9l0CxEXzS9E0L72b5n6lkIEIaWUIy/RlqnkDMHVtEC6InDjd4rt0jZKcvTrDKxeT96WUYnw==}
|
||||
dependencies:
|
||||
'@types/node': 20.12.7
|
||||
'@types/vinyl': 2.0.11
|
||||
'@types/vinyl': 2.0.12
|
||||
dev: true
|
||||
|
||||
/@types/gulp@4.0.11:
|
||||
|
@ -1700,13 +1700,6 @@ packages:
|
|||
'@types/vinyl': 2.0.12
|
||||
dev: true
|
||||
|
||||
/@types/vinyl@2.0.11:
|
||||
resolution: {integrity: sha512-vPXzCLmRp74e9LsP8oltnWKTH+jBwt86WgRUb4Pc9Lf3pkMVGyvIo2gm9bODeGfCay2DBB/hAWDuvf07JcK4rw==}
|
||||
dependencies:
|
||||
'@types/expect': 1.20.4
|
||||
'@types/node': 20.8.10
|
||||
dev: true
|
||||
|
||||
/@types/vinyl@2.0.12:
|
||||
resolution: {integrity: sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==}
|
||||
dependencies:
|
||||
|
@ -1753,7 +1746,7 @@ packages:
|
|||
/@vue/compiler-core@3.4.26:
|
||||
resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==}
|
||||
dependencies:
|
||||
'@babel/parser': 7.24.4
|
||||
'@babel/parser': 7.24.5
|
||||
'@vue/shared': 3.4.26
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
|
@ -1770,7 +1763,7 @@ packages:
|
|||
/@vue/compiler-sfc@3.4.26:
|
||||
resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==}
|
||||
dependencies:
|
||||
'@babel/parser': 7.24.4
|
||||
'@babel/parser': 7.24.5
|
||||
'@vue/compiler-core': 3.4.26
|
||||
'@vue/compiler-dom': 3.4.26
|
||||
'@vue/compiler-ssr': 3.4.26
|
||||
|
@ -2245,7 +2238,7 @@ packages:
|
|||
resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==}
|
||||
dependencies:
|
||||
browserslist: 1.7.7
|
||||
caniuse-db: 1.0.30001610
|
||||
caniuse-db: 1.0.30001614
|
||||
normalize-range: 0.1.2
|
||||
num2fraction: 1.2.2
|
||||
postcss: 5.2.18
|
||||
|
@ -2330,8 +2323,8 @@ packages:
|
|||
find-versions: 5.1.0
|
||||
dev: true
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
/binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
|
@ -2395,8 +2388,8 @@ packages:
|
|||
deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-db: 1.0.30001610
|
||||
electron-to-chromium: 1.4.736
|
||||
caniuse-db: 1.0.30001614
|
||||
electron-to-chromium: 1.4.751
|
||||
dev: true
|
||||
|
||||
/browserslist@4.23.0:
|
||||
|
@ -2480,13 +2473,13 @@ packages:
|
|||
resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==}
|
||||
dependencies:
|
||||
browserslist: 1.7.7
|
||||
caniuse-db: 1.0.30001610
|
||||
caniuse-db: 1.0.30001614
|
||||
lodash.memoize: 4.1.2
|
||||
lodash.uniq: 4.5.0
|
||||
dev: true
|
||||
|
||||
/caniuse-db@1.0.30001610:
|
||||
resolution: {integrity: sha512-GLdKwZR0S1EwOBIJlCze89enIbFf0Om/wzHF1GU8nIqf82NmxkB8JsfkOR5A6cZR57wiPJf0WBU+7gnNf0+EyQ==}
|
||||
/caniuse-db@1.0.30001614:
|
||||
resolution: {integrity: sha512-AVDISTXTRe7bHFnwAthHh1tNe3FRRap/Ce8rPw+kmNY0k4fV5RVtxvDudXoIyTjfOtRt0Jf7gZvQuBfsjLZI3A==}
|
||||
dev: true
|
||||
|
||||
/caniuse-lite@1.0.30001606:
|
||||
|
@ -2975,7 +2968,7 @@ packages:
|
|||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||
engines: {node: '>=0.11'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.4
|
||||
'@babel/runtime': 7.24.5
|
||||
dev: true
|
||||
|
||||
/dayjs@1.11.11:
|
||||
|
@ -3086,8 +3079,8 @@ packages:
|
|||
resolution: {integrity: sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==}
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium@1.4.736:
|
||||
resolution: {integrity: sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==}
|
||||
/electron-to-chromium@1.4.751:
|
||||
resolution: {integrity: sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
|
@ -3854,7 +3847,7 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
'@types/node': 20.12.7
|
||||
'@types/vinyl': 2.0.11
|
||||
'@types/vinyl': 2.0.12
|
||||
istextorbinary: 3.3.0
|
||||
replacestream: 4.0.3
|
||||
yargs-parser: 21.1.1
|
||||
|
@ -3865,7 +3858,7 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
plugin-error: 1.0.1
|
||||
terser: 5.30.3
|
||||
terser: 5.31.0
|
||||
through2: 4.0.2
|
||||
vinyl-sourcemaps-apply: 0.2.1
|
||||
dev: true
|
||||
|
@ -4106,7 +4099,7 @@ packages:
|
|||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
binary-extensions: 2.2.0
|
||||
binary-extensions: 2.3.0
|
||||
dev: true
|
||||
|
||||
/is-ci@3.0.1:
|
||||
|
@ -5596,29 +5589,29 @@ packages:
|
|||
seedrandom: 2.4.2
|
||||
dev: true
|
||||
|
||||
/rollup@4.17.1:
|
||||
resolution: {integrity: sha512-0gG94inrUtg25sB2V/pApwiv1lUb0bQ25FPNuzO89Baa+B+c0ccaaBKM5zkZV/12pUUdH+lWCSm9wmHqyocuVQ==}
|
||||
/rollup@4.17.2:
|
||||
resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.17.1
|
||||
'@rollup/rollup-android-arm64': 4.17.1
|
||||
'@rollup/rollup-darwin-arm64': 4.17.1
|
||||
'@rollup/rollup-darwin-x64': 4.17.1
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.17.1
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.17.1
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.17.1
|
||||
'@rollup/rollup-linux-arm64-musl': 4.17.1
|
||||
'@rollup/rollup-linux-powerpc64le-gnu': 4.17.1
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.17.1
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.17.1
|
||||
'@rollup/rollup-linux-x64-gnu': 4.17.1
|
||||
'@rollup/rollup-linux-x64-musl': 4.17.1
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.17.1
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.17.1
|
||||
'@rollup/rollup-win32-x64-msvc': 4.17.1
|
||||
'@rollup/rollup-android-arm-eabi': 4.17.2
|
||||
'@rollup/rollup-android-arm64': 4.17.2
|
||||
'@rollup/rollup-darwin-arm64': 4.17.2
|
||||
'@rollup/rollup-darwin-x64': 4.17.2
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.17.2
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.17.2
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.17.2
|
||||
'@rollup/rollup-linux-arm64-musl': 4.17.2
|
||||
'@rollup/rollup-linux-powerpc64le-gnu': 4.17.2
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.17.2
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.17.2
|
||||
'@rollup/rollup-linux-x64-gnu': 4.17.2
|
||||
'@rollup/rollup-linux-x64-musl': 4.17.2
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.17.2
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.17.2
|
||||
'@rollup/rollup-win32-x64-msvc': 4.17.2
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
|
@ -6187,6 +6180,17 @@ packages:
|
|||
source-map-support: 0.5.21
|
||||
dev: true
|
||||
|
||||
/terser@5.31.0:
|
||||
resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@jridgewell/source-map': 0.3.6
|
||||
acorn: 8.11.3
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
dev: true
|
||||
|
||||
/textarea-caret@3.1.0:
|
||||
resolution: {integrity: sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==}
|
||||
dev: true
|
||||
|
@ -6723,14 +6727,14 @@ packages:
|
|||
'@types/node': 20.12.7
|
||||
esbuild: 0.20.2
|
||||
postcss: 8.4.38
|
||||
rollup: 4.17.1
|
||||
rollup: 4.17.2
|
||||
sass: 1.62.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/vue-component-type-helpers@2.0.14:
|
||||
resolution: {integrity: sha512-DInfgOyXlMyliyqAAD9frK28tTfch0+tMi4qoWJcZlRxUf+NFAtraJBnAsKLep+FOyLMiajkhfyEb3xLK08i7w==}
|
||||
/vue-component-type-helpers@2.0.15:
|
||||
resolution: {integrity: sha512-jR/Hw52gzNQxMovJBsOQ/F9E1UQ8K1Np0CVG3RnueLkaCKqWuyL9XHl/5tUBAGJx+bk5xZ+co7vK23+Pzt75Lg==}
|
||||
dev: true
|
||||
|
||||
/vue-demi@0.14.7(vue@3.4.26):
|
||||
|
|
|
@ -122,12 +122,11 @@ async fn main() -> miette::Result<()> {
|
|||
let listener = TcpListener::bind(addr).await.into_diagnostic()?;
|
||||
tracing::info!("Serving...");
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.with_graceful_shutdown(shutdown_signal())
|
||||
.await
|
||||
.map_err(|e| miette!("Error running server: {}", e))
|
||||
}
|
||||
|
||||
// FIXME: Plug this back in when Axum reimplements graceful shutdown
|
||||
|
||||
async fn shutdown_signal() {
|
||||
let ctrl_c = async {
|
||||
if let Err(e) = signal::ctrl_c().await {
|
||||
|
|
|
@ -93,11 +93,11 @@ async fn main() -> miette::Result<()> {
|
|||
let listener = TcpListener::bind(addr).await.into_diagnostic()?;
|
||||
info!("Serving...");
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.with_graceful_shutdown(shutdown_signal())
|
||||
.await
|
||||
.map_err(|e| miette!("Error running server: {}", e))
|
||||
}
|
||||
|
||||
// FIXME: Plug this back in when Axum reimplements graceful shutdown
|
||||
async fn shutdown_signal() {
|
||||
let ctrl_c = async {
|
||||
if let Err(e) = signal::ctrl_c().await {
|
||||
|
|
|
@ -23,6 +23,21 @@ pub enum UserCacheError {
|
|||
RedisError(#[from] CalckeyCacheError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CachedLocalUser {
|
||||
pub user: Arc<ck::user::Model>,
|
||||
pub profile: Arc<ck::user_profile::Model>,
|
||||
}
|
||||
|
||||
impl From<(ck::user::Model, ck::user_profile::Model)> for CachedLocalUser {
|
||||
fn from((user, profile): (ck::user::Model, ck::user_profile::Model)) -> Self {
|
||||
CachedLocalUser {
|
||||
user: Arc::new(user),
|
||||
profile: Arc::new(profile),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UserCacheError> for ApiError {
|
||||
fn from(err: UserCacheError) -> Self {
|
||||
let mut api_error: ApiError = match err {
|
||||
|
@ -38,44 +53,42 @@ impl From<UserCacheError> for ApiError {
|
|||
|
||||
struct LocalUserCache {
|
||||
lifetime: TimedCache<String, ()>,
|
||||
id_to_user: HashMap<String, Arc<ck::user::Model>>,
|
||||
token_to_user: HashMap<String, Arc<ck::user::Model>>,
|
||||
id_to_user: HashMap<String, CachedLocalUser>,
|
||||
token_to_user: HashMap<String, CachedLocalUser>,
|
||||
}
|
||||
|
||||
impl LocalUserCache {
|
||||
fn purge(&mut self, user: impl AsRef<ck::user::Model>) {
|
||||
let user = user.as_ref();
|
||||
fn purge(&mut self, user: &CachedLocalUser) {
|
||||
self.lifetime.cache_remove(&user.user.id);
|
||||
|
||||
self.lifetime.cache_remove(&user.id);
|
||||
|
||||
if let Some(user) = self.id_to_user.remove(&user.id) {
|
||||
if let Some(token) = user.token.clone() {
|
||||
if let Some(user) = self.id_to_user.remove(&user.user.id) {
|
||||
if let Some(token) = user.user.token.clone() {
|
||||
self.token_to_user.remove(&token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh(&mut self, user: Arc<ck::user::Model>) {
|
||||
self.purge(&user);
|
||||
fn refresh(&mut self, user: &CachedLocalUser) {
|
||||
self.purge(user);
|
||||
|
||||
self.lifetime.cache_set(user.id.clone(), ());
|
||||
self.lifetime.cache_set(user.user.id.clone(), ());
|
||||
|
||||
self.id_to_user.insert(user.id.clone(), user.clone());
|
||||
self.id_to_user.insert(user.user.id.clone(), user.clone());
|
||||
|
||||
if let Some(token) = user.token.clone() {
|
||||
if let Some(token) = user.user.token.clone() {
|
||||
self.token_to_user.insert(token, user.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Low-priority refresh. Only refreshes the cache if the user is not there.
|
||||
/// Used mostly for getters that would otherwise data race with more important refreshes.
|
||||
fn maybe_refresh(&mut self, user: &Arc<ck::user::Model>) {
|
||||
if self.lifetime.cache_get(&user.id).is_none() {
|
||||
self.refresh(user.clone());
|
||||
fn maybe_refresh(&mut self, user: &CachedLocalUser) {
|
||||
if self.lifetime.cache_get(&user.user.id).is_none() {
|
||||
self.refresh(user);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_by_id(&mut self, id: &str) -> Option<Arc<ck::user::Model>> {
|
||||
fn get_by_id(&mut self, id: &str) -> Option<CachedLocalUser> {
|
||||
if let Some(user) = self.id_to_user.get(id).cloned() {
|
||||
if self.lifetime.cache_get(id).is_none() {
|
||||
self.purge(&user);
|
||||
|
@ -88,9 +101,9 @@ impl LocalUserCache {
|
|||
None
|
||||
}
|
||||
|
||||
fn get_by_token(&mut self, token: &str) -> Option<Arc<ck::user::Model>> {
|
||||
fn get_by_token(&mut self, token: &str) -> Option<CachedLocalUser> {
|
||||
if let Some(user) = self.token_to_user.get(token).cloned() {
|
||||
if self.lifetime.cache_get(&user.id).is_none() {
|
||||
if self.lifetime.cache_get(&user.user.id).is_none() {
|
||||
self.purge(&user);
|
||||
return None;
|
||||
}
|
||||
|
@ -143,8 +156,8 @@ impl LocalUserCacheService {
|
|||
| InternalStreamMessage::UserChangeSuspendedState { id, .. }
|
||||
| InternalStreamMessage::RemoteUserUpdated { id }
|
||||
| InternalStreamMessage::UserTokenRegenerated { id, .. } => {
|
||||
let user = match db.get_user_by_id(&id).await {
|
||||
Ok(Some(user)) => user,
|
||||
let user_profile = match db.get_user_and_profile_by_id(&id).await {
|
||||
Ok(Some(m)) => m,
|
||||
Ok(None) => return,
|
||||
Err(e) => {
|
||||
error!("Error fetching user from database: {}", e);
|
||||
|
@ -152,7 +165,7 @@ impl LocalUserCacheService {
|
|||
}
|
||||
};
|
||||
|
||||
cache.lock().await.refresh(Arc::new(user));
|
||||
cache.lock().await.refresh(&CachedLocalUser::from(user_profile));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
@ -169,10 +182,9 @@ impl LocalUserCacheService {
|
|||
|
||||
async fn map_cache_user(
|
||||
&self,
|
||||
user: Option<ck::user::Model>,
|
||||
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||
user: Option<CachedLocalUser>,
|
||||
) -> Result<Option<CachedLocalUser>, UserCacheError> {
|
||||
if let Some(user) = user {
|
||||
let user = Arc::new(user);
|
||||
self.cache.lock().await.maybe_refresh(&user);
|
||||
return Ok(Some(user));
|
||||
}
|
||||
|
@ -183,29 +195,27 @@ impl LocalUserCacheService {
|
|||
pub async fn get_by_token(
|
||||
&self,
|
||||
token: &str,
|
||||
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||
) -> Result<Option<CachedLocalUser>, UserCacheError> {
|
||||
let result = self.cache.lock().await.get_by_token(token);
|
||||
|
||||
if let Some(user) = result {
|
||||
return Ok(Some(user));
|
||||
}
|
||||
|
||||
self.map_cache_user(self.db.get_user_by_token(token).await?)
|
||||
self.map_cache_user(self.db.get_user_and_profile_by_token(token).await?.map(CachedLocalUser::from))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||
) -> Result<Option<CachedLocalUser>, UserCacheError> {
|
||||
let result = self.cache.lock().await.get_by_id(id);
|
||||
|
||||
if let Some(user) = result {
|
||||
return Ok(Some(user));
|
||||
}
|
||||
|
||||
let user = self.db.get_user_by_id(id).await?;
|
||||
|
||||
self.map_cache_user(user).await
|
||||
self.map_cache_user(self.db.get_user_and_profile_by_id(id).await?.map(CachedLocalUser::from)).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
use crate::service::local_user_cache::UserCacheError;
|
||||
use crate::service::MagnetarService;
|
||||
use crate::web::{ApiError, IntoErrorCode};
|
||||
use axum::async_trait;
|
||||
use axum::extract::rejection::ExtensionRejection;
|
||||
use axum::extract::{FromRequestParts, Request, State};
|
||||
use axum::http::request::Parts;
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
use axum::middleware::Next;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use headers::authorization::Bearer;
|
||||
use headers::{Authorization, HeaderMapExt};
|
||||
use magnetar_model::{ck, CalckeyDbError};
|
||||
use std::convert::Infallible;
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::async_trait;
|
||||
use axum::extract::{FromRequestParts, Request, State};
|
||||
use axum::extract::rejection::ExtensionRejection;
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
use axum::http::request::Parts;
|
||||
use axum::middleware::Next;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use headers::{Authorization, HeaderMapExt};
|
||||
use headers::authorization::Bearer;
|
||||
use strum::IntoStaticStr;
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
|
||||
use magnetar_model::{CalckeyDbError, ck};
|
||||
|
||||
use crate::service::local_user_cache::{CachedLocalUser, UserCacheError};
|
||||
use crate::service::MagnetarService;
|
||||
use crate::web::{ApiError, IntoErrorCode};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AuthMode {
|
||||
User {
|
||||
|
@ -178,7 +181,7 @@ impl AuthState {
|
|||
let user_cache = &self.service.local_user_cache;
|
||||
let user = user_cache.get_by_token(token).await?;
|
||||
|
||||
if let Some(user) = user {
|
||||
if let Some(CachedLocalUser { user, .. }) = user {
|
||||
return Ok(AuthMode::User { user });
|
||||
}
|
||||
|
||||
|
@ -205,7 +208,7 @@ impl AuthState {
|
|||
});
|
||||
}
|
||||
|
||||
let user = user.unwrap();
|
||||
let CachedLocalUser { user, .. } = user.unwrap();
|
||||
|
||||
if let Some(app_id) = &access_token.app_id {
|
||||
return match self.service.db.get_app_by_id(app_id).await? {
|
||||
|
|
Loading…
Reference in New Issue