Merge branch 'develop' into fix/search-filter-behavior
This commit is contained in:
commit
8e46059d61
20 changed files with 267 additions and 61 deletions
|
@ -968,6 +968,9 @@ rateLimitExceeded: "Rate limit exceeded"
|
||||||
cropImage: "Crop image"
|
cropImage: "Crop image"
|
||||||
cropImageAsk: "Do you want to crop this image?"
|
cropImageAsk: "Do you want to crop this image?"
|
||||||
file: "File"
|
file: "File"
|
||||||
|
image: "Image"
|
||||||
|
video: "Video"
|
||||||
|
audio: "Audio"
|
||||||
recentNHours: "Last {n} hours"
|
recentNHours: "Last {n} hours"
|
||||||
recentNDays: "Last {n} days"
|
recentNDays: "Last {n} days"
|
||||||
noEmailServerWarning: "Email server not configured."
|
noEmailServerWarning: "Email server not configured."
|
||||||
|
@ -1442,6 +1445,14 @@ _time:
|
||||||
minute: "Minute(s)"
|
minute: "Minute(s)"
|
||||||
hour: "Hour(s)"
|
hour: "Hour(s)"
|
||||||
day: "Day(s)"
|
day: "Day(s)"
|
||||||
|
_filters:
|
||||||
|
fromUser: "From user"
|
||||||
|
withFile: "With file"
|
||||||
|
fromDomain: "From domain"
|
||||||
|
notesBefore: "Posts before"
|
||||||
|
notesAfter: "Posts after"
|
||||||
|
followingOnly: "Following only"
|
||||||
|
followersOnly: "Followers only"
|
||||||
_tutorial:
|
_tutorial:
|
||||||
title: "How to use Calckey"
|
title: "How to use Calckey"
|
||||||
step1_1: "Welcome!"
|
step1_1: "Welcome!"
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub fn convert_id(in_id: String, id_convert_type: IdConvertType) -> napi::Result
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
Status::InvalidArg,
|
Status::InvalidArg,
|
||||||
"Unable to parse ID as MasstodonId",
|
"Unable to parse ID as MastodonId",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { In } from "typeorm";
|
||||||
import { Notes } from "@/models/index.js";
|
import { Notes } from "@/models/index.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import es from "../../../../db/elasticsearch.js";
|
import es from "@/db/elasticsearch.js";
|
||||||
import sonic from "../../../../db/sonic.js";
|
import sonic from "@/db/sonic.js";
|
||||||
import meilisearch, { MeilisearchNote } from "../../../../db/meilisearch.js";
|
import meilisearch, { MeilisearchNote } from "@/db/meilisearch.js";
|
||||||
import define from "../../define.js";
|
import define from "../../define.js";
|
||||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
||||||
|
|
|
@ -20,8 +20,7 @@ export default define(meta, paramDef, async () => {
|
||||||
const cachedPatrons = await redisClient.get("patrons");
|
const cachedPatrons = await redisClient.get("patrons");
|
||||||
if (cachedPatrons) {
|
if (cachedPatrons) {
|
||||||
patrons = JSON.parse(cachedPatrons);
|
patrons = JSON.parse(cachedPatrons);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
patrons = await fetch(
|
patrons = await fetch(
|
||||||
"https://codeberg.org/calckey/calckey/raw/branch/develop/patrons.json",
|
"https://codeberg.org/calckey/calckey/raw/branch/develop/patrons.json",
|
||||||
).then((response) => response.json());
|
).then((response) => response.json());
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Instances, NoteReactions, Notes, Users } from "@/models/index.js";
|
import { Instances, NoteReactions, Notes, Users } from "@/models/index.js";
|
||||||
import define from "../define.js";
|
import define from "../define.js";
|
||||||
import {} from "@/services/chart/index.js";
|
import { driveChart, notesChart, usersChart } from "@/services/chart/index.js";
|
||||||
import { IsNull } from "typeorm";
|
import { IsNull } from "typeorm";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -60,19 +60,25 @@ export const paramDef = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default define(meta, paramDef, async () => {
|
export default define(meta, paramDef, async () => {
|
||||||
|
const notesChartData = await notesChart.getChart("hour", 1, null);
|
||||||
|
const notesCount =
|
||||||
|
notesChartData.local.total[0] + notesChartData.remote.total[0];
|
||||||
|
const originalNotesCount = notesChartData.local.total[0];
|
||||||
|
|
||||||
|
const usersChartData = await usersChart.getChart("hour", 1, null);
|
||||||
|
const usersCount =
|
||||||
|
usersChartData.local.total[0] + usersChartData.remote.total[0];
|
||||||
|
const originalUsersCount = usersChartData.local.total[0];
|
||||||
|
const driveChartData = await driveChart.getChart("hour", 1, null);
|
||||||
|
//TODO: fixme currently returns 0
|
||||||
|
const driveUsageLocal = driveChartData.local.incSize[0];
|
||||||
|
const driveUsageRemote = driveChartData.remote.incSize[0];
|
||||||
|
|
||||||
const [
|
const [
|
||||||
notesCount,
|
|
||||||
originalNotesCount,
|
|
||||||
usersCount,
|
|
||||||
originalUsersCount,
|
|
||||||
reactionsCount,
|
reactionsCount,
|
||||||
//originalReactionsCount,
|
//originalReactionsCount,
|
||||||
instances,
|
instances,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
Notes.count({ cache: 3600000 }), // 1 hour
|
|
||||||
Notes.count({ where: { userHost: IsNull() }, cache: 3600000 }),
|
|
||||||
Users.count({ cache: 3600000 }),
|
|
||||||
Users.count({ where: { host: IsNull() }, cache: 3600000 }),
|
|
||||||
NoteReactions.count({ cache: 3600000 }), // 1 hour
|
NoteReactions.count({ cache: 3600000 }), // 1 hour
|
||||||
//NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }),
|
//NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }),
|
||||||
Instances.count({ cache: 3600000 }),
|
Instances.count({ cache: 3600000 }),
|
||||||
|
@ -86,7 +92,7 @@ export default define(meta, paramDef, async () => {
|
||||||
reactionsCount,
|
reactionsCount,
|
||||||
//originalReactionsCount,
|
//originalReactionsCount,
|
||||||
instances,
|
instances,
|
||||||
driveUsageLocal: 0,
|
driveUsageLocal,
|
||||||
driveUsageRemote: 0,
|
driveUsageRemote,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,6 +37,12 @@ export const meta = {
|
||||||
code: "FORBIDDEN",
|
code: "FORBIDDEN",
|
||||||
id: "3c6a84db-d619-26af-ca14-06232a21df8a",
|
id: "3c6a84db-d619-26af-ca14-06232a21df8a",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
nullFollowers: {
|
||||||
|
message: "No followers found.",
|
||||||
|
code: "NULL_FOLLOWERS",
|
||||||
|
id: "174a6507-a6c2-4925-8e5d-92fd08aedc9e",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -97,7 +103,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
followerId: me.id,
|
followerId: me.id,
|
||||||
});
|
});
|
||||||
if (following == null) {
|
if (following == null) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.nullFollowers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<button
|
<button
|
||||||
v-if="!link"
|
v-if="!link"
|
||||||
class="bghgjjyj _button"
|
class="bghgjjyj _button"
|
||||||
:class="{ inline, primary, gradate, danger, rounded, full, mini }"
|
:class="{ inline, primary, gradate, danger, rounded, full, mini, chip }"
|
||||||
:type="type"
|
:type="type"
|
||||||
@click="emit('click', $event)"
|
@click="emit('click', $event)"
|
||||||
@mousedown="onMousedown"
|
@mousedown="onMousedown"
|
||||||
|
@ -41,7 +41,8 @@ const props = defineProps<{
|
||||||
wait?: boolean;
|
wait?: boolean;
|
||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
full?: boolean;
|
full?: boolean;
|
||||||
mini: boolean;
|
mini?: boolean;
|
||||||
|
chip?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -198,6 +199,13 @@ function onMousedown(evt: MouseEvent): void {
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.chip {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: max(12px, 0.9em);
|
||||||
|
min-width: unset;
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
<MkModal
|
<MkModal
|
||||||
ref="modal"
|
ref="modal"
|
||||||
:prefer-type="'dialog'"
|
:prefer-type="'dialog'"
|
||||||
:z-priority="'high'"
|
|
||||||
@click="done(true)"
|
@click="done(true)"
|
||||||
@closed="emit('closed')"
|
@closed="emit('closed')"
|
||||||
>
|
>
|
||||||
|
@ -56,16 +55,29 @@
|
||||||
</header>
|
</header>
|
||||||
<div v-if="text" :class="$style.text"><Mfm :text="text" /></div>
|
<div v-if="text" :class="$style.text"><Mfm :text="text" /></div>
|
||||||
<MkInput
|
<MkInput
|
||||||
|
ref="inputEl"
|
||||||
v-if="input && input.type !== 'paragraph'"
|
v-if="input && input.type !== 'paragraph'"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
autofocus
|
autofocus
|
||||||
:type="input.type || 'text'"
|
:type="input.type == 'search' ? 'search' : input.type || 'text'"
|
||||||
:placeholder="input.placeholder || undefined"
|
:placeholder="input.placeholder || undefined"
|
||||||
@keydown="onInputKeydown"
|
@keydown="onInputKeydown"
|
||||||
|
:style="{
|
||||||
|
width: input.type === 'search' ? '300px' : null,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template v-if="input.type === 'password'" #prefix
|
<template v-if="input.type === 'password'" #prefix
|
||||||
><i class="ph-password ph-bold ph-lg"></i
|
><i class="ph-password ph-bold ph-lg"></i
|
||||||
></template>
|
></template>
|
||||||
|
<template v-if="input.type === 'search'" #suffix>
|
||||||
|
<button
|
||||||
|
class="_buttonIcon"
|
||||||
|
@click.stop="openSearchFilters"
|
||||||
|
v-tooltip.noDelay="i18n.ts.filter"
|
||||||
|
>
|
||||||
|
<i class="ph-funnel ph-bold"></i>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkTextarea
|
<MkTextarea
|
||||||
v-if="input && input.type === 'paragraph'"
|
v-if="input && input.type === 'paragraph'"
|
||||||
|
@ -95,6 +107,7 @@
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</template>
|
</template>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="(showOkButton || showCancelButton) && !actions"
|
v-if="(showOkButton || showCancelButton) && !actions"
|
||||||
:class="$style.buttons"
|
:class="$style.buttons"
|
||||||
|
@ -162,7 +175,9 @@ import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import MkTextarea from "@/components/form/textarea.vue";
|
import MkTextarea from "@/components/form/textarea.vue";
|
||||||
import MkSelect from "@/components/form/select.vue";
|
import MkSelect from "@/components/form/select.vue";
|
||||||
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import * as Acct from "calckey-js/built/acct";
|
||||||
|
|
||||||
type Input = {
|
type Input = {
|
||||||
type: HTMLInputElement["type"];
|
type: HTMLInputElement["type"];
|
||||||
|
@ -193,7 +208,8 @@ const props = withDefaults(
|
||||||
| "warning"
|
| "warning"
|
||||||
| "info"
|
| "info"
|
||||||
| "question"
|
| "question"
|
||||||
| "waiting";
|
| "waiting"
|
||||||
|
| "search";
|
||||||
title: string;
|
title: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
input?: Input;
|
input?: Input;
|
||||||
|
@ -229,9 +245,11 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const inputValue = ref(props.input?.default || null);
|
const inputValue = ref(props.input?.default || "");
|
||||||
const selectedValue = ref(props.select?.default || null);
|
const selectedValue = ref(props.select?.default || null);
|
||||||
|
|
||||||
|
const inputEl = ref<typeof MkInput>();
|
||||||
|
|
||||||
function done(canceled: boolean, result?) {
|
function done(canceled: boolean, result?) {
|
||||||
emit("done", { canceled, result });
|
emit("done", { canceled, result });
|
||||||
modal.value?.close();
|
modal.value?.close();
|
||||||
|
@ -268,6 +286,115 @@ function onInputKeydown(evt: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDateToYYYYMMDD(date) {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = ("0" + (date.getMonth() + 1)).slice(-2);
|
||||||
|
const day = ("0" + date.getDate()).slice(-2);
|
||||||
|
return `${year}${month}${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openSearchFilters(ev) {
|
||||||
|
await os.popupMenu(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
icon: "ph-user ph-bold ph-lg",
|
||||||
|
text: i18n.ts._filters.fromUser,
|
||||||
|
action: () => {
|
||||||
|
os.selectUser().then((user) => {
|
||||||
|
inputValue.value += " from:@" + Acct.toString(user);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "parent",
|
||||||
|
text: i18n.ts._filters.withFile,
|
||||||
|
icon: "ph-paperclip ph-bold ph-lg",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: i18n.ts.image,
|
||||||
|
icon: "ph-image-square ph-bold ph-lg",
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " has:image";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.ts.video,
|
||||||
|
icon: "ph-video-camera ph-bold ph-lg",
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " has:video";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.ts.audio,
|
||||||
|
icon: "ph-music-note ph-bold ph-lg",
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " has:audio";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.ts.file,
|
||||||
|
icon: "ph-file ph-bold ph-lg",
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " has:file";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "ph-link ph-bold ph-lg",
|
||||||
|
text: i18n.ts._filters.fromDomain,
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " domain:";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "ph-calendar-blank ph-bold ph-lg",
|
||||||
|
text: i18n.ts._filters.notesBefore,
|
||||||
|
action: () => {
|
||||||
|
os.inputDate({
|
||||||
|
title: i18n.ts._filters.notesBefore,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.canceled) return;
|
||||||
|
inputValue.value +=
|
||||||
|
" before:" + formatDateToYYYYMMDD(res.result);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "ph-calendar-blank ph-bold ph-lg",
|
||||||
|
text: i18n.ts._filters.notesAfter,
|
||||||
|
action: () => {
|
||||||
|
os.inputDate({
|
||||||
|
title: i18n.ts._filters.notesAfter,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.canceled) return;
|
||||||
|
inputValue.value +=
|
||||||
|
" after:" + formatDateToYYYYMMDD(res.result);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "ph-eye ph-bold ph-lg",
|
||||||
|
text: i18n.ts._filters.followingOnly,
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " filter:following ";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "ph-users-three ph-bold ph-lg",
|
||||||
|
text: i18n.ts._filters.followersOnly,
|
||||||
|
action: () => {
|
||||||
|
inputValue.value += " filter:followers ";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ev.target,
|
||||||
|
{ noReturnFocus: true }
|
||||||
|
);
|
||||||
|
inputEl.value.focus();
|
||||||
|
inputEl.value.selectRange(inputValue.value.length, inputValue.value.length); // cursor at end
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener("keydown", onKeydown);
|
document.addEventListener("keydown", onKeydown);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<FocusTrap :active="false" ref="focusTrap">
|
<FocusTrap
|
||||||
|
:active="false"
|
||||||
|
ref="focusTrap"
|
||||||
|
:return-focus-on-deactivate="!noReturnFocus"
|
||||||
|
>
|
||||||
<div tabindex="-1">
|
<div tabindex="-1">
|
||||||
<div
|
<div
|
||||||
ref="itemsEl"
|
ref="itemsEl"
|
||||||
|
@ -205,6 +209,7 @@ const props = defineProps<{
|
||||||
align?: "center" | string;
|
align?: "center" | string;
|
||||||
width?: number;
|
width?: number;
|
||||||
maxHeight?: number;
|
maxHeight?: number;
|
||||||
|
noReturnFocus?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
@enter="emit('opening')"
|
@enter="emit('opening')"
|
||||||
@after-enter="onOpened"
|
@after-enter="onOpened"
|
||||||
>
|
>
|
||||||
<FocusTrap v-model:active="isActive">
|
<FocusTrap
|
||||||
|
v-model:active="isActive"
|
||||||
|
:return-focus-on-deactivate="!noReturnFocus"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-show="manualShowing != null ? manualShowing : showing"
|
v-show="manualShowing != null ? manualShowing : showing"
|
||||||
v-hotkey.global="keymap"
|
v-hotkey.global="keymap"
|
||||||
|
@ -102,6 +105,7 @@ const props = withDefaults(
|
||||||
zPriority?: "low" | "middle" | "high";
|
zPriority?: "low" | "middle" | "high";
|
||||||
noOverlap?: boolean;
|
noOverlap?: boolean;
|
||||||
transparentBg?: boolean;
|
transparentBg?: boolean;
|
||||||
|
noReturnFocus?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
manualShowing: null,
|
manualShowing: null,
|
||||||
|
@ -111,6 +115,7 @@ const props = withDefaults(
|
||||||
zPriority: "low",
|
zPriority: "low",
|
||||||
noOverlap: true,
|
noOverlap: true,
|
||||||
transparentBg: false,
|
transparentBg: false,
|
||||||
|
noReturnFocus: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -189,12 +194,16 @@ function close(ev, opts: { useSendAnimation?: boolean } = {}) {
|
||||||
if (props.src) props.src.style.pointerEvents = "auto";
|
if (props.src) props.src.style.pointerEvents = "auto";
|
||||||
showing = false;
|
showing = false;
|
||||||
emit("close");
|
emit("close");
|
||||||
focusedElement.focus();
|
if (!props.noReturnFocus) {
|
||||||
|
focusedElement.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBgClick() {
|
function onBgClick() {
|
||||||
if (contentClicking) return;
|
if (contentClicking) return;
|
||||||
focusedElement.focus();
|
if (!props.noReturnFocus) {
|
||||||
|
focusedElement.focus();
|
||||||
|
}
|
||||||
emit("click");
|
emit("click");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
:as-drawer="type === 'drawer'"
|
:as-drawer="type === 'drawer'"
|
||||||
class="sfhdhdhq"
|
class="sfhdhdhq"
|
||||||
:class="{ drawer: type === 'drawer' }"
|
:class="{ drawer: type === 'drawer' }"
|
||||||
|
:no-return-focus="noReturnFocus"
|
||||||
@close="modal.close()"
|
@close="modal.close()"
|
||||||
/>
|
/>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
|
@ -35,6 +36,7 @@ defineProps<{
|
||||||
width?: number;
|
width?: number;
|
||||||
viaKeyboard?: boolean;
|
viaKeyboard?: boolean;
|
||||||
src?: any;
|
src?: any;
|
||||||
|
noReturnFocus?;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -108,6 +108,7 @@ const suffixEl = ref<HTMLElement>();
|
||||||
const height = props.small ? 36 : props.large ? 40 : 38;
|
const height = props.small ? 36 : props.large ? 40 : 38;
|
||||||
|
|
||||||
const focus = () => inputEl.value.focus();
|
const focus = () => inputEl.value.focus();
|
||||||
|
const selectRange = (start, end) => inputEl.value.setSelectionRange(start, end);
|
||||||
const onInput = (ev: KeyboardEvent) => {
|
const onInput = (ev: KeyboardEvent) => {
|
||||||
changed.value = true;
|
changed.value = true;
|
||||||
emit("change", ev);
|
emit("change", ev);
|
||||||
|
@ -178,6 +179,11 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus,
|
||||||
|
selectRange,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -255,6 +261,9 @@ onMounted(() => {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
> :deep(button) {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .prefix {
|
> .prefix {
|
||||||
|
|
|
@ -346,7 +346,7 @@ export function yesno(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inputText(props: {
|
export function inputText(props: {
|
||||||
type?: "text" | "email" | "password" | "url";
|
type?: "text" | "email" | "password" | "url" | "search";
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
placeholder?: string | null;
|
placeholder?: string | null;
|
||||||
|
@ -366,6 +366,7 @@ export function inputText(props: {
|
||||||
delay: 1000,
|
delay: 1000,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
type: props.type,
|
||||||
title: props.title,
|
title: props.title,
|
||||||
text: props.text,
|
text: props.text,
|
||||||
input: {
|
input: {
|
||||||
|
@ -491,7 +492,7 @@ export function inputDate(props: {
|
||||||
{
|
{
|
||||||
done: (result) => {
|
done: (result) => {
|
||||||
resolve(
|
resolve(
|
||||||
result
|
(result && isFinite(new Date(result.result)))
|
||||||
? { result: new Date(result.result), canceled: false }
|
? { result: new Date(result.result), canceled: false }
|
||||||
: { canceled: true },
|
: { canceled: true },
|
||||||
);
|
);
|
||||||
|
@ -837,6 +838,7 @@ export function popupMenu(
|
||||||
align?: string;
|
align?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
viaKeyboard?: boolean;
|
viaKeyboard?: boolean;
|
||||||
|
noReturnFocus?: boolean;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -853,6 +855,7 @@ export function popupMenu(
|
||||||
width: options?.width,
|
width: options?.width,
|
||||||
align: options?.align,
|
align: options?.align,
|
||||||
viaKeyboard: options?.viaKeyboard,
|
viaKeyboard: options?.viaKeyboard,
|
||||||
|
noReturnFocus: options?.noReturnFocus,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
closed: () => {
|
closed: () => {
|
||||||
|
|
|
@ -472,12 +472,24 @@ let preview_quote = $ref(`> ${i18n.ts._mfm.dummy}`);
|
||||||
let preview_search = $ref(
|
let preview_search = $ref(
|
||||||
`${i18n.ts._mfm.dummy} [search]\n${i18n.ts._mfm.dummy} [検索]\n${i18n.ts._mfm.dummy} 検索`
|
`${i18n.ts._mfm.dummy} [search]\n${i18n.ts._mfm.dummy} [検索]\n${i18n.ts._mfm.dummy} 検索`
|
||||||
);
|
);
|
||||||
let preview_jelly = $ref("$[jelly 🍮] $[jelly.speed=3s 🍮] $[jelly.delay=3s 🍮] $[jelly.loop=3 🍮]");
|
let preview_jelly = $ref(
|
||||||
let preview_tada = $ref("$[tada 🍮] $[tada.speed=3s 🍮] $[tada.delay=3s 🍮] $[tada.loop=3 🍮]");
|
"$[jelly 🍮] $[jelly.speed=3s 🍮] $[jelly.delay=3s 🍮] $[jelly.loop=3 🍮]"
|
||||||
let preview_jump = $ref("$[jump 🍮] $[jump.speed=3s 🍮] $[jump.delay=3s 🍮] $[jump.loop=3 🍮]");
|
);
|
||||||
let preview_bounce = $ref("$[bounce 🍮] $[bounce.speed=3s 🍮] $[bounce.delay=3s 🍮] $[bounce.loop=3 🍮]");
|
let preview_tada = $ref(
|
||||||
let preview_shake = $ref("$[shake 🍮] $[shake.speed=3s 🍮] $[shake.delay=3s 🍮] $[shake.loop=3 🍮]");
|
"$[tada 🍮] $[tada.speed=3s 🍮] $[tada.delay=3s 🍮] $[tada.loop=3 🍮]"
|
||||||
let preview_twitch = $ref("$[twitch 🍮] $[twitch.speed=3s 🍮] $[twitch.delay=3s 🍮] $[twitch.loop=3 🍮]");
|
);
|
||||||
|
let preview_jump = $ref(
|
||||||
|
"$[jump 🍮] $[jump.speed=3s 🍮] $[jump.delay=3s 🍮] $[jump.loop=3 🍮]"
|
||||||
|
);
|
||||||
|
let preview_bounce = $ref(
|
||||||
|
"$[bounce 🍮] $[bounce.speed=3s 🍮] $[bounce.delay=3s 🍮] $[bounce.loop=3 🍮]"
|
||||||
|
);
|
||||||
|
let preview_shake = $ref(
|
||||||
|
"$[shake 🍮] $[shake.speed=3s 🍮] $[shake.delay=3s 🍮] $[shake.loop=3 🍮]"
|
||||||
|
);
|
||||||
|
let preview_twitch = $ref(
|
||||||
|
"$[twitch 🍮] $[twitch.speed=3s 🍮] $[twitch.delay=3s 🍮] $[twitch.loop=3 🍮]"
|
||||||
|
);
|
||||||
let preview_spin = $ref(
|
let preview_spin = $ref(
|
||||||
"$[spin 🍮] $[spin.left 🍮] $[spin.alternate 🍮]\n$[spin.x 🍮] $[spin.x,left 🍮] $[spin.x,alternate 🍮]\n$[spin.y 🍮] $[spin.y,left 🍮] $[spin.y,alternate 🍮]\n\n$[spin.speed=3s 🍮] $[spin.delay=3s 🍮] $[spin.loop=3 🍮]"
|
"$[spin 🍮] $[spin.left 🍮] $[spin.alternate 🍮]\n$[spin.x 🍮] $[spin.x,left 🍮] $[spin.x,alternate 🍮]\n$[spin.y 🍮] $[spin.y,left 🍮] $[spin.y,alternate 🍮]\n\n$[spin.speed=3s 🍮] $[spin.delay=3s 🍮] $[spin.loop=3 🍮]"
|
||||||
);
|
);
|
||||||
|
@ -491,14 +503,14 @@ let preview_x2 = $ref("$[x2 🍮]");
|
||||||
let preview_x3 = $ref("$[x3 🍮]");
|
let preview_x3 = $ref("$[x3 🍮]");
|
||||||
let preview_x4 = $ref("$[x4 🍮]");
|
let preview_x4 = $ref("$[x4 🍮]");
|
||||||
let preview_blur = $ref(`$[blur ${i18n.ts._mfm.dummy}]`);
|
let preview_blur = $ref(`$[blur ${i18n.ts._mfm.dummy}]`);
|
||||||
let preview_rainbow = $ref("$[rainbow 🍮] $[rainbow.speed=3s 🍮] $[rainbow.delay=3s 🍮] $[rainbow.loop=3 🍮]");
|
let preview_rainbow = $ref(
|
||||||
|
"$[rainbow 🍮] $[rainbow.speed=3s 🍮] $[rainbow.delay=3s 🍮] $[rainbow.loop=3 🍮]"
|
||||||
|
);
|
||||||
let preview_sparkle = $ref("$[sparkle 🍮]");
|
let preview_sparkle = $ref("$[sparkle 🍮]");
|
||||||
let preview_rotate = $ref(
|
let preview_rotate = $ref(
|
||||||
"$[rotate 🍮]\n$[rotate.deg=45 🍮]\n$[rotate.x,deg=45 Hello, world!]"
|
"$[rotate 🍮]\n$[rotate.deg=45 🍮]\n$[rotate.x,deg=45 Hello, world!]"
|
||||||
);
|
);
|
||||||
let preview_position = $ref(
|
let preview_position = $ref("$[position.y=-1 🍮]\n$[position.x=-1 🍮]");
|
||||||
"$[position.y=-1 🍮]\n$[position.x=-1 🍮]"
|
|
||||||
);
|
|
||||||
let preview_crop = $ref(
|
let preview_crop = $ref(
|
||||||
"$[crop.top=50 🍮] $[crop.right=50 🍮] $[crop.bottom=50 🍮] $[crop.left=50 🍮]"
|
"$[crop.top=50 🍮] $[crop.right=50 🍮] $[crop.bottom=50 🍮] $[crop.left=50 🍮]"
|
||||||
);
|
);
|
||||||
|
@ -510,7 +522,9 @@ let preview_bg = $ref("$[bg.color=31748f Background color]");
|
||||||
let preview_plain = $ref(
|
let preview_plain = $ref(
|
||||||
"<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>"
|
"<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>"
|
||||||
);
|
);
|
||||||
let preview_fade = $ref("$[fade 🍮] $[fade.out 🍮] $[fade.speed=3s 🍮] $[fade.delay=3s 🍮]");
|
let preview_fade = $ref(
|
||||||
|
"$[fade 🍮] $[fade.out 🍮] $[fade.speed=3s 🍮] $[fade.delay=3s 🍮]"
|
||||||
|
);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts._mfm.cheatSheet,
|
title: i18n.ts._mfm.cheatSheet,
|
||||||
|
|
|
@ -1,26 +1,13 @@
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { mainRouter } from "@/router";
|
import { mainRouter } from "@/router";
|
||||||
// import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
|
|
||||||
export async function search() {
|
export async function search() {
|
||||||
// const searchOptions =
|
|
||||||
// "Advanced search operators\n" +
|
|
||||||
// "from:user => filter by user\n" +
|
|
||||||
// "has:image/video/audio/text/file => filter by attachment types\n" +
|
|
||||||
// "domain:domain.com => filter by domain\n" +
|
|
||||||
// "before:Date => show posts made before Date\n" +
|
|
||||||
// "after:Date => show posts made after Date\n" +
|
|
||||||
// '"text" => get posts with exact text between quotes\n' +
|
|
||||||
// "filter:following => show results only from users you follow\n" +
|
|
||||||
// "filter:followers => show results only from followers\n";
|
|
||||||
|
|
||||||
// const searchFiltersAvailable = instance.searchFilters;
|
|
||||||
|
|
||||||
const { canceled, result: query } = await os.inputText({
|
const { canceled, result: query } = await os.inputText({
|
||||||
|
type: instance.features.searchFilters ? "search" : "text",
|
||||||
title: i18n.ts.search,
|
title: i18n.ts.search,
|
||||||
placeholder: i18n.ts.searchPlaceholder,
|
placeholder: i18n.ts.searchPlaceholder,
|
||||||
// text: searchOptions,
|
|
||||||
});
|
});
|
||||||
if (canceled || query == null || query === "") return;
|
if (canceled || query == null || query === "") return;
|
||||||
|
|
||||||
|
|
|
@ -482,6 +482,17 @@ hr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
._flexList {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: .2em;
|
||||||
|
width: min-content;
|
||||||
|
min-width: 100%;
|
||||||
|
&._center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
._formLinks {
|
._formLinks {
|
||||||
> *:not(:last-child) {
|
> *:not(:last-child) {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
|
@ -39,7 +39,9 @@
|
||||||
:meta="meta"
|
:meta="meta"
|
||||||
/>
|
/>
|
||||||
<XMeili
|
<XMeili
|
||||||
v-else-if="instance.searchFilters && widgetProps.view === 5"
|
v-else-if="
|
||||||
|
instance.features.searchFilters && widgetProps.view === 5
|
||||||
|
"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:meta="meta"
|
:meta="meta"
|
||||||
/>
|
/>
|
||||||
|
@ -110,8 +112,8 @@ os.api("server-info", {}).then((res) => {
|
||||||
|
|
||||||
const toggleView = () => {
|
const toggleView = () => {
|
||||||
if (
|
if (
|
||||||
(widgetProps.view === 5 && instance.searchFilters) ||
|
(widgetProps.view === 5 && instance.features.searchFilters) ||
|
||||||
(widgetProps.view === 4 && !instance.searchFilters)
|
(widgetProps.view === 4 && !instance.features.searchFilters)
|
||||||
) {
|
) {
|
||||||
widgetProps.view = 0;
|
widgetProps.view = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
v-tooltip="i18n.ts.meiliIndexCount"
|
v-tooltip="i18n.ts.meiliIndexCount"
|
||||||
class="pie"
|
class="pie"
|
||||||
:value="progress"
|
:value="progress"
|
||||||
|
:reverse="true"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p><i class="ph-file-search ph-bold ph-lg"></i>MeiliSearch</p>
|
<p><i class="ph-file-search ph-bold ph-lg"></i>MeiliSearch</p>
|
||||||
|
|
|
@ -29,11 +29,17 @@ import {} from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
value: number;
|
value: number;
|
||||||
|
reverse?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const r = 0.45;
|
const r = 0.45;
|
||||||
|
|
||||||
const color = $computed(() => `hsl(${180 - props.value * 180}, 80%, 70%)`);
|
const color = $computed(
|
||||||
|
() =>
|
||||||
|
`hsl(${
|
||||||
|
props.reverse ? props.value * 180 : 180 - props.value * 180
|
||||||
|
}, 80%, 70%)`
|
||||||
|
);
|
||||||
const strokeDashoffset = $computed(
|
const strokeDashoffset = $computed(
|
||||||
() => (1 - props.value) * (Math.PI * (r * 2))
|
() => (1 - props.value) * (Math.PI * (r * 2))
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue