diff --git a/fe_calckey/frontend/calckey-js/src/streaming.types.ts b/fe_calckey/frontend/calckey-js/src/streaming.types.ts
index 078f855..885fdd0 100644
--- a/fe_calckey/frontend/calckey-js/src/streaming.types.ts
+++ b/fe_calckey/frontend/calckey-js/src/streaming.types.ts
@@ -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;
diff --git a/fe_calckey/frontend/client/package.json b/fe_calckey/frontend/client/package.json
index 395060b..10efe95 100644
--- a/fe_calckey/frontend/client/package.json
+++ b/fe_calckey/frontend/client/package.json
@@ -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"
diff --git a/fe_calckey/frontend/client/src/components/MkLink.vue b/fe_calckey/frontend/client/src/components/MagLink.vue
similarity index 89%
rename from fe_calckey/frontend/client/src/components/MkLink.vue
rename to fe_calckey/frontend/client/src/components/MagLink.vue
index 5872500..d523c1e 100644
--- a/fe_calckey/frontend/client/src/components/MkLink.vue
+++ b/fe_calckey/frontend/client/src/components/MagLink.vue
@@ -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"
);
});
diff --git a/fe_calckey/frontend/client/src/components/MagMatrixMention.vue b/fe_calckey/frontend/client/src/components/MagMatrixMention.vue
index 7bcb105..cf4f78d 100644
--- a/fe_calckey/frontend/client/src/components/MagMatrixMention.vue
+++ b/fe_calckey/frontend/client/src/components/MagMatrixMention.vue
@@ -10,7 +10,7 @@
diff --git a/fe_calckey/frontend/client/src/components/MkMoved.vue b/fe_calckey/frontend/client/src/components/MagMoved.vue
similarity index 100%
rename from fe_calckey/frontend/client/src/components/MkMoved.vue
rename to fe_calckey/frontend/client/src/components/MagMoved.vue
diff --git a/fe_calckey/frontend/client/src/components/MagNote.vue b/fe_calckey/frontend/client/src/components/MagNote.vue
index 53b1899..404d64c 100644
--- a/fe_calckey/frontend/client/src/components/MagNote.vue
+++ b/fe_calckey/frontend/client/src/components/MagNote.vue
@@ -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));
diff --git a/fe_calckey/frontend/client/src/components/MagNoteHeader.vue b/fe_calckey/frontend/client/src/components/MagNoteHeader.vue
index fc69ed9..dcad883 100644
--- a/fe_calckey/frontend/client/src/components/MagNoteHeader.vue
+++ b/fe_calckey/frontend/client/src/components/MagNoteHeader.vue
@@ -12,7 +12,9 @@
bot
-
+
+
+
@@ -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 @@
diff --git a/fe_calckey/frontend/client/src/components/MkPostForm.vue b/fe_calckey/frontend/client/src/components/MkPostForm.vue
index b9144f3..308897c 100644
--- a/fe_calckey/frontend/client/src/components/MkPostForm.vue
+++ b/fe_calckey/frontend/client/src/components/MkPostForm.vue
@@ -89,8 +89,8 @@
reply
? 'ph-arrow-u-up-left ph-bold ph-lg'
: renote
- ? 'ph-quotes ph-bold ph-lg'
- : 'ph-paper-plane-tilt ph-bold ph-lg'
+ ? 'ph-quotes ph-bold ph-lg'
+ : 'ph-paper-plane-tilt ph-bold ph-lg'
"
>
@@ -101,8 +101,8 @@
- {{ i18n.ts.quoteAttached
- }}
@@ -127,8 +127,8 @@
>{{ i18n.ts.notSpecifiedMentionWarning }} -
{{ i18n.ts.add }}
-
+
+
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
(null);
let localOnly = ref(
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(null);
let draghover = ref(false);
let quoteId = ref(null);
let hasNotSpecifiedMentions = ref(false);
@@ -392,10 +393,10 @@ const submitText = computed((): string => {
return props.editId
? i18n.ts.edit
: props.renote
- ? i18n.ts.quote
- : props.reply
- ? i18n.ts.reply
- : i18n.ts.note;
+ ? i18n.ts.quote
+ : props.reply
+ ? i18n.ts.reply
+ : i18n.ts.note;
});
const textLength = computed((): number => {
@@ -419,9 +420,11 @@ const canPost = computed((): boolean => {
});
const withHashtags = computed(
- defaultStore.makeGetterSetter("postFormWithHashtags"),
+ defaultStore.makeGetterSetter("postFormWithHashtags")
+);
+const hashtags = computed(
+ defaultStore.makeGetterSetter("postFormHashtags")
);
-const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
watch(text, () => {
checkMissingMention();
@@ -434,7 +437,7 @@ watch(
},
{
deep: true,
- },
+ }
);
if (props.mention) {
@@ -464,8 +467,8 @@ if (props.reply && props.reply.text != null) {
const mention = x.host
? `@${x.username}@${toASCII(x.host)}`
: otherHost == null || otherHost === host
- ? `@${x.username}`
- : `@${x.username}@${toASCII(otherHost)}`;
+ ? `@${x.username}`
+ : `@${x.username}@${toASCII(otherHost)}`;
// 自分は除外
if ($i.username === x.username && (x.host == null || x.host === host))
@@ -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;
});
}
}
@@ -844,8 +851,8 @@ async function post() {
renoteId: props.renote
? props.renote.id
: quoteId.value
- ? quoteId.value
- : undefined,
+ ? quoteId.value
+ : undefined,
poll: poll.value,
cw: useCw.value ? cw.value || "" : undefined,
localOnly: localOnly.value,
@@ -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());
+});