Merge branch 'note-improvements' of https://codeberg.org/Freeplay/calckey into note-improvements

This commit is contained in:
Freeplay 2023-01-19 19:59:42 -05:00
commit 3a932b79e3
24 changed files with 169 additions and 151 deletions

View File

@ -6,12 +6,12 @@
**🌎 **[Calckey](https://i.calckey.cloud/)** is an open source, decentralized social media platform that's free forever! 🚀** **🌎 **[Calckey](https://i.calckey.cloud/)** is an open source, decentralized social media platform that's free forever! 🚀**
[![no github badge](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page/) [![no github badge](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page/)
[![status badge](https://ci.lavaforge.org/api/badges/calckey/calckey/status.svg)](https://ci.lavaforge.org/calckey/calckey) [![status badge](https://ci.codeberg.org/api/badges/calckey/calckey/status.svg)](https://ci.codeberg.org/calckey/calckey)
[![liberapay badge](https://img.shields.io/liberapay/receives/ThatOneCalculator?logo=liberapay)](https://liberapay.com/ThatOneCalculator) [![liberapay badge](https://img.shields.io/liberapay/receives/ThatOneCalculator?logo=liberapay)](https://liberapay.com/ThatOneCalculator)
[![translate-badge](https://hosted.weblate.org/widgets/calckey/-/svg-badge.svg)](https://hosted.weblate.org/engage/calckey/) [![translate-badge](https://hosted.weblate.org/widgets/calckey/-/svg-badge.svg)](https://hosted.weblate.org/engage/calckey/)
[![docker badge](https://img.shields.io/docker/pulls/thatonecalculator/calckey?logo=docker)](https://hub.docker.com/r/thatonecalculator/calckey) [![docker badge](https://img.shields.io/docker/pulls/thatonecalculator/calckey?logo=docker)](https://hub.docker.com/r/thatonecalculator/calckey)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](./CODE_OF_CONDUCT.md) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](./CODE_OF_CONDUCT.md)
[![lavaforge badge](https://custom-icon-badges.demolab.com/badge/hosted%20on-lavaforge-FF8066.svg?logo=lavaforge&logoColor=white)](https://lavaforge.org/calckey/calckey/) [![lavaforge badge](https://custom-icon-badges.demolab.com/badge/hosted%20on-lavaforge-FF8066.svg?logo=lavaforge&logoColor=white)](https://codeberg.org/calckey/calckey/)
</div> </div>
@ -92,7 +92,7 @@ If you have access to a server that supports one of the sources below, I recomme
## 👀 Get folder ready ## 👀 Get folder ready
```sh ```sh
git clone https://lavaforge.org/calckey/calckey.git git clone https://codeberg.org/calckey/calckey.git
cd calckey/ cd calckey/
# git checkout main # if you want only stable versions # git checkout main # if you want only stable versions
``` ```

View File

@ -64,7 +64,7 @@ body:
id: terms id: terms
attributes: attributes:
label: Contribution Guidelines label: Contribution Guidelines
description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://lavaforge.org/calckey/calckey/src/branch/develop/CONTRIBUTING.md) description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://codeberg.org/calckey/calckey/src/branch/develop/CONTRIBUTING.md)
options: options:
- label: I agree to follow this project's Contribution Guidelines - label: I agree to follow this project's Contribution Guidelines
required: true required: true

View File

@ -64,7 +64,7 @@ body:
id: terms id: terms
attributes: attributes:
label: Contribution Guidelines label: Contribution Guidelines
description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://lavaforge.org/calckey/calckey/src/branch/develop/CONTRIBUTING.md) description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://codeberg.org/calckey/calckey/src/branch/develop/CONTRIBUTING.md)
options: options:
- label: I agree to follow this project's Contribution Guidelines - label: I agree to follow this project's Contribution Guidelines
required: true required: true

View File

@ -4,7 +4,7 @@
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://lavaforge.org/calckey/calckey.git" "url": "https://codeberg.org/calckey/calckey.git"
}, },
"packageManager": "pnpm@7.25.0", "packageManager": "pnpm@7.25.0",
"private": true, "private": true,

View File

@ -48,7 +48,7 @@ function greet() {
136, 136,
0, 0,
)( )(
" If you like Calckey, please consider starring or contributing to the repo. https://lavaforge.org/calckey/calckey", " If you like Calckey, please consider starring or contributing to the repo. https://codeberg.org/calckey/calckey",
), ),
); );

View File

@ -64,11 +64,11 @@ export async function toDbReaction(
// 文字列タイプのリアクションを絵文字に変換 // 文字列タイプのリアクションを絵文字に変換
if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
// Unicode emoji // Unicode絵文字
const match = emojiRegex.exec(reaction); const match = emojiRegex.exec(reaction);
if (match) { if (match) {
//return only first emoji const unicode = match[0];
return match[0]; return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, '');
} }
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/); const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);

View File

@ -394,14 +394,14 @@ export class Meta {
@Column('varchar', { @Column('varchar', {
length: 512, length: 512,
default: 'https://lavaforge.org/calckey/calckey', default: 'https://codeberg.org/calckey/calckey',
nullable: false, nullable: false,
}) })
public repositoryUrl: string; public repositoryUrl: string;
@Column('varchar', { @Column('varchar', {
length: 512, length: 512,
default: 'https://lavaforge.org/calckey/calckey/issues/new', default: 'https://codeberg.org/calckey/calckey/issues/new',
nullable: true, nullable: true,
}) })
public feedbackUrl: string | null; public feedbackUrl: string | null;

View File

@ -16,7 +16,7 @@ export const paramDef = {
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
let tag_name; let tag_name;
await fetch( await fetch(
"https://lavaforge.org/api/v1/repos/calckey/calckey/releases?draft=false&pre-release=false&page=1&limit=1", "https://codeberg.org/api/v1/repos/calckey/calckey/releases?draft=false&pre-release=false&page=1&limit=1",
) )
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {

View File

@ -68,13 +68,13 @@ export const meta = {
type: "string", type: "string",
optional: false, optional: false,
nullable: false, nullable: false,
default: "https://lavaforge.org/calckey/calckey", default: "https://codeberg.org/calckey/calckey",
}, },
feedbackUrl: { feedbackUrl: {
type: "string", type: "string",
optional: false, optional: false,
nullable: false, nullable: false,
default: "https://lavaforge.org/calckey/calckey/issues", default: "https://codeberg.org/calckey/calckey/issues",
}, },
defaultDarkTheme: { defaultDarkTheme: {
type: "string", type: "string",

View File

@ -17,7 +17,7 @@ export const paramDef = {
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
let patrons; let patrons;
await fetch( await fetch(
"https://lavaforge.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())
.then((data) => { .then((data) => {

View File

@ -18,7 +18,7 @@ export default define(meta, paramDef, async () => {
let release; let release;
await fetch( await fetch(
"https://lavaforge.org/calckey/calckey/raw/branch/develop/release.json", "https://codeberg.org/calckey/calckey/raw/branch/develop/release.json",
) )
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {

View File

@ -15,7 +15,7 @@ export function genOpenapiSpec() {
externalDocs: { externalDocs: {
description: "Repository", description: "Repository",
url: "https://lavaforge.org/calckey/calckey", url: "https://codeberg.org/calckey/calckey",
}, },
servers: [ servers: [
@ -106,7 +106,7 @@ export function genOpenapiSpec() {
description: desc, description: desc,
externalDocs: { externalDocs: {
description: "Source code", description: "Source code",
url: `https://lavaforge.org/calckey/calckey/src/branch/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, url: `https://codeberg.org/calckey/calckey/src/branch/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`,
}, },
tags: endpoint.meta.tags || undefined, tags: endpoint.meta.tags || undefined,
security, security,

View File

@ -16,7 +16,7 @@ doctype html
Thank you for using Calckey! Thank you for using Calckey!
If you are reading this message... how about joining the development? If you are reading this message... how about joining the development?
https://lavaforge.org/calckey/calckey https://codeberg.org/calckey/calckey
html html

View File

@ -1,5 +1,5 @@
<template> <template>
<MkPagination ref="pagingComponent" :pagination="pagination"> <MkPagination ref="pagingComponent" :pagination="pagination" disableReload="true">
<template #empty> <template #empty>
<div class="_fullinfo"> <div class="_fullinfo">
<img src="/static-assets/badges/info.png" class="_ghost" alt="Info"/> <img src="/static-assets/badges/info.png" class="_ghost" alt="Info"/>

View File

@ -1,34 +1,37 @@
<template> <template>
<transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> <transition :name="$store.state.animation ? 'fade' : ''" mode="out-in">
<MkLoading v-if="fetching"/> <MkLoading v-if="fetching" />
<MkError v-else-if="error" @retry="init()"/> <MkError v-else-if="error" @retry="init()" />
<div v-else-if="empty" key="_empty_" class="empty"> <div v-else-if="empty" key="_empty_" class="empty">
<slot name="empty"> <slot name="empty">
<div class="_fullinfo"> <div class="_fullinfo">
<img src="/static-assets/badges/info.png" class="_ghost" alt="Error"/> <img src="/static-assets/badges/info.png" class="_ghost" alt="Error" />
<div>{{ i18n.ts.nothing }}</div> <div>{{ i18n.ts.nothing }}</div>
</div>
</slot>
</div>
<div v-else ref="rootEl">
<div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" class="button" :disabled="moreFetching"
:style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading" />
</div>
<slot :items="items"></slot>
<div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching"
v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button"
:disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading" />
</div> </div>
</slot>
</div>
<div v-else ref="rootEl">
<div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading"/>
</div> </div>
<slot :items="items"></slot> </transition>
<div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading"/>
</div>
</div>
</transition>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -66,6 +69,7 @@ const props = withDefaults(defineProps<{
disableAutoLoad?: boolean; disableAutoLoad?: boolean;
displayLimit?: number; displayLimit?: number;
externalItemArray?: Ref<Array<any>>; externalItemArray?: Ref<Array<any>>;
disableReload?: boolean;
}>(), { }>(), {
displayLimit: 30, displayLimit: 30,
}); });
@ -74,7 +78,7 @@ const emit = defineEmits<{
(ev: 'queue', count: number): void; (ev: 'queue', count: number): void;
}>(); }>();
type Item = { id: string; [another: string]: unknown; }; type Item = { id: string;[another: string]: unknown; };
const rootEl = ref<HTMLElement>(); const rootEl = ref<HTMLElement>();
const items = ref<Item[]>([]); const items = ref<Item[]>([]);
@ -87,6 +91,7 @@ const backed = ref(false); // 遡り中か否か
const isBackTop = ref(false); const isBackTop = ref(false);
const empty = computed(() => items.value.length === 0); const empty = computed(() => items.value.length === 0);
const error = ref(false); const error = ref(false);
const alreadyInitedOnce = ref(false);
const init = async (): Promise<void> => { const init = async (): Promise<void> => {
queue.value = []; queue.value = [];
@ -107,14 +112,17 @@ const init = async (): Promise<void> => {
if (!props.pagination.noPaging && (res.length > (props.pagination.limit || 10))) { if (!props.pagination.noPaging && (res.length > (props.pagination.limit || 10))) {
res.pop(); res.pop();
items.value = props.pagination.reversed ? [...res].reverse() : res; items.value = props.pagination.reversed ? [...res].reverse() : res;
if (props.externalItemArray) {
props.externalItemArray.value = items.value;
}
more.value = true; more.value = true;
} else { } else {
items.value = props.pagination.reversed ? [...res].reverse() : res; items.value = props.pagination.reversed ? [...res].reverse() : res;
if (props.externalItemArray) {
props.externalItemArray.value = items.value;
}
more.value = false; more.value = false;
} }
if(props.externalItemArray) {
props.externalItemArray.value = items.value;
}
offset.value = res.length; offset.value = res.length;
error.value = false; error.value = false;
fetching.value = false; fetching.value = false;
@ -126,7 +134,7 @@ const init = async (): Promise<void> => {
const reload = (): void => { const reload = (): void => {
items.value = []; items.value = [];
if(props.externalItemArray) { if (props.externalItemArray) {
props.externalItemArray.value = []; props.externalItemArray.value = [];
} }
init(); init();
@ -188,14 +196,17 @@ const fetchMore = async (): Promise<void> => {
if (res.length > SECOND_FETCH_LIMIT) { if (res.length > SECOND_FETCH_LIMIT) {
res.pop(); res.pop();
items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res); items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res);
if (props.externalItemArray) {
props.externalItemArray.value = items.value;
}
more.value = true; more.value = true;
} else { } else {
items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res); items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res);
if (props.externalItemArray) {
props.externalItemArray.value = items.value;
}
more.value = false; more.value = false;
} }
if(props.externalItemArray) {
props.externalItemArray.value = items.value;
}
offset.value += res.length; offset.value += res.length;
moreFetching.value = false; moreFetching.value = false;
}, err => { }, err => {
@ -221,14 +232,17 @@ const fetchMoreAhead = async (): Promise<void> => {
if (res.length > SECOND_FETCH_LIMIT) { if (res.length > SECOND_FETCH_LIMIT) {
res.pop(); res.pop();
items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res); items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res);
if (props.externalItemArray) {
props.externalItemArray.value = items.value;
}
more.value = true; more.value = true;
} else { } else {
items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res); items.value = props.pagination.reversed ? [...res].reverse().concat(items.value) : items.value.concat(res);
if (props.externalItemArray) {
props.externalItemArray.value = items.value;
}
more.value = false; more.value = false;
} }
if(props.externalItemArray) {
props.externalItemArray.value = items.value;
}
offset.value += res.length; offset.value += res.length;
moreFetching.value = false; moreFetching.value = false;
}, err => { }, err => {
@ -254,7 +268,7 @@ const prepend = (item: Item): void => {
//items.value = items.value.slice(-props.displayLimit); //items.value = items.value.slice(-props.displayLimit);
while (items.value.length >= props.displayLimit) { while (items.value.length >= props.displayLimit) {
items.value.shift(); items.value.shift();
if(props.externalItemArray) props.externalItemArray.value.shift(); if (props.externalItemArray) props.externalItemArray.value.shift();
} }
more.value = true; more.value = true;
} }
@ -262,13 +276,13 @@ const prepend = (item: Item): void => {
} }
} }
items.value.push(item); items.value.push(item);
if(props.externalItemArray) props.externalItemArray.value.push(item); if (props.externalItemArray) props.externalItemArray.value.push(item);
// TODO // TODO
} else { } else {
// unshiftOK // unshiftOK
if (!rootEl.value) { if (!rootEl.value) {
items.value.unshift(item); items.value.unshift(item);
if(props.externalItemArray) props.externalItemArray.value.unshift(item); if (props.externalItemArray) props.externalItemArray.value.unshift(item);
return; return;
} }
@ -277,18 +291,7 @@ const prepend = (item: Item): void => {
if (isTop) { if (isTop) {
// Prepend the item // Prepend the item
items.value.unshift(item); items.value.unshift(item);
if(props.externalItemArray) props.externalItemArray.value.unshift(item); if (props.externalItemArray) props.externalItemArray.value.unshift(item);
//
if (items.value.length >= props.displayLimit) {
// Vue 3.2
//this.items = items.value.slice(0, props.displayLimit);
while (items.value.length >= props.displayLimit) {
items.value.pop();
if(props.externalItemArray) props.externalItemArray.value.pop();
}
more.value = true;
}
} else { } else {
queue.value.push(item); queue.value.push(item);
onScrollTop(rootEl.value, () => { onScrollTop(rootEl.value, () => {
@ -303,28 +306,30 @@ const prepend = (item: Item): void => {
const append = (item: Item): void => { const append = (item: Item): void => {
items.value.push(item); items.value.push(item);
if(props.externalItemArray) props.externalItemArray.value.push(item); if (props.externalItemArray) props.externalItemArray.value.push(item);
}; };
const removeItem = (finder: (item: Item) => boolean): boolean => { const removeItem = (finder: (item: Item) => boolean): boolean => {
const i = items.value.findIndex(finder); const i = items.value.findIndex(finder);
if (i === -1) { const j = props.externalItemArray?.findIndex(finder);
if (i === -1 && j === -1) {
return false; return false;
} }
items.value.splice(i, 1); items.value.splice(i, 1);
if(props.externalItemArray) props.externalItemArray.value.splice(i, 1); if (props.externalItemArray) props.externalItemArray.value.splice(i, 1);
return true; return true;
}; };
const updateItem = (id: Item['id'], replacer: (old: Item) => Item): boolean => { const updateItem = (id: Item['id'], replacer: (old: Item) => Item): boolean => {
const i = items.value.findIndex(item => item.id === id); const i = items.value.findIndex(item => item.id === id);
if (i === -1) { const j = props.externalItemArray?.findIndex(item => item.id === id);
if (i === -1 && j === -1) {
return false; return false;
} }
items.value[i] = replacer(items.value[i]); items.value[i] = replacer(items.value[i]);
if(props.externalItemArray) props.externalItemArray.value[i] = items.value[i]; if (props.externalItemArray) props.externalItemArray.value[i] = items.value[i];
return true; return true;
}; };
@ -341,10 +346,14 @@ init();
onActivated(() => { onActivated(() => {
isBackTop.value = false; isBackTop.value = false;
if(alreadyInitedOnce.value && (!props.disableReload || !props.disableReload.value)) {
reload();
}
}); });
onDeactivated(() => { onDeactivated(() => {
isBackTop.value = window.scrollY === 0; isBackTop.value = window.scrollY === 0;
alreadyInitedOnce.value = true;
}); });
defineExpose({ defineExpose({
@ -365,13 +374,14 @@ defineExpose({
.fade-leave-active { .fade-leave-active {
transition: opacity 0.15s ease; transition: opacity 0.15s ease;
} }
.fade-enter-from, .fade-enter-from,
.fade-leave-to { .fade-leave-to {
opacity: 0; opacity: 0;
} }
.cxiknjgy { .cxiknjgy {
> .button { >.button {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }

View File

@ -2,28 +2,28 @@
<XModalWindow <XModalWindow
ref="dialog" ref="dialog"
:width="400" :width="400"
:height="450" :height="600"
:with-ok-button="true" :with-ok-button="true"
:ok-button-disabled="false" :ok-button-disabled="false"
:can-close="false" :can-close="false"
@close="dialog.close()" @close="dialog.close()"
@closed="$emit('closed')" @closed="$emit('closed')"
@ok="ok()" @ok="ok()"
style="padding: 12px"
> >
<template #header>{{ title || i18n.ts.generateAccessToken }}</template> <template #header>{{ title || i18n.ts.generateAccessToken }}</template>
<div v-if="information" class="_section"> <div v-if="information" class="_section">
<MkInfo warn>{{ information }}</MkInfo> <MkInfo warn>{{ information }}</MkInfo>
</div> </div>
<div class="_section"> <div class="_section">
<MkInput v-model="name"> <div style="margin-bottom: 16px;"><b>{{ i18n.ts.name }}</b></div>
<template #label>{{ i18n.ts.name }}</template> <MkInput style="margin-bottom: 16px;" v-model="name"/>
</MkInput>
</div> </div>
<div class="_section"> <div class="_section">
<div style="margin-bottom: 16px;"><b>{{ i18n.ts.permission }}</b></div> <div style="margin-bottom: 16px;"><b>{{ i18n.ts.permission }}</b></div>
<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> <MkButton style="margin-bottom: 12px;" inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
<MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch> <MkSwitch style="margin-bottom: 6px;" v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch>
</div> </div>
</XModalWindow> </XModalWindow>
</template> </template>

View File

@ -12,14 +12,14 @@
<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> <span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span>
</div> </div>
<div class="_formBlock" style="text-align: center;"> <div class="_formBlock" style="text-align: center;">
{{ i18n.ts._aboutMisskey.about }}<br><a href="https://lavaforge.org/calckey/calckey" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a> {{ i18n.ts._aboutMisskey.about }}<br><a href="https://codeberg.org/calckey/calckey" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a>
</div> </div>
<div class="_formBlock" style="text-align: center;"> <div class="_formBlock" style="text-align: center;">
<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Calckey</MkButton> <MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Calckey</MkButton>
</div> </div>
<FormSection> <FormSection>
<div class="_formLinks"> <div class="_formLinks">
<FormLink to="https://lavaforge.org/calckey/calckey" external> <FormLink to="https://codeberg.org/calckey/calckey" external>
<template #icon><i class="ph-code-bold ph-lg"></i></template> <template #icon><i class="ph-code-bold ph-lg"></i></template>
{{ i18n.ts._aboutMisskey.source }} {{ i18n.ts._aboutMisskey.source }}
<template #suffix>Codeberg</template> <template #suffix>Codeberg</template>
@ -44,7 +44,7 @@
<FormLink to="/@syuilo@misskey.io"><Mfm :text="'@syuilo@misskey.io (Original Misskey developer)'"/></FormLink> <FormLink to="/@syuilo@misskey.io"><Mfm :text="'@syuilo@misskey.io (Original Misskey developer)'"/></FormLink>
<FormLink to="https://www.youtube.com/c/Henkiwashere" external>Henki (error images artist)</FormLink> <FormLink to="https://www.youtube.com/c/Henkiwashere" external>Henki (error images artist)</FormLink>
</div> </div>
<template #caption><MkLink url="https://lavaforge.org/calckey/calckey/activity">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> <template #caption><MkLink url="https://codeberg.org/calckey/calckey/activity">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template>
</FormSection> </FormSection>
<FormSection> <FormSection>
<template #label><Mfm text="$[jelly ❤]"/> {{ i18n.ts._aboutMisskey.patrons }}</template> <template #label><Mfm text="$[jelly ❤]"/> {{ i18n.ts._aboutMisskey.patrons }}</template>

View File

@ -11,7 +11,7 @@
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> <MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> <MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> <MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
<MkInfo v-if="updateAvailable" warn class="info">{{ i18n.ts.updateAvailable }} <a href="https://lavaforge.org/calckey/calckey/releases" target="_bank" class="_link">{{ i18n.ts.check }}</a></MkInfo> <MkInfo v-if="updateAvailable" warn class="info">{{ i18n.ts.updateAvailable }} <a href="https://codeberg.org/calckey/calckey/releases" target="_bank" class="_link">{{ i18n.ts.check }}</a></MkInfo>
<MkSuperMenu :def="menuDef" :grid="currentPage?.route.name == null"></MkSuperMenu> <MkSuperMenu :def="menuDef" :grid="currentPage?.route.name == null"></MkSuperMenu>
</div> </div>

View File

@ -1,43 +1,47 @@
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header>
<div> <MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" />
<MkSpacer :content-max="800"> </template>
<swiper <div>
:modules="[Virtual]" <MkSpacer :content-max="800">
:space-between="20" <swiper :modules="[Virtual]" :space-between="20" :virtual="true"
:virtual="true" :allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" @swiper="setSwiperRef"
:allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" @slide-change="onSlideChange">
@swiper="setSwiperRef" <swiper-slide>
@slide-change="onSlideChange" <div class="_content yweeujhr dms">
> <MkButton primary class="start" @click="startUser"><i class="ph-plus-bold ph-lg"></i> {{
<swiper-slide> i18n.ts.startMessaging
<div class="_content yweeujhr dms"> }}</MkButton>
<MkButton primary class="start" @click="startUser"><i class="ph-plus-bold ph-lg"></i> {{ i18n.ts.startMessaging }}</MkButton> <MkPagination v-slot="{ items }" :pagination="dmsPagination">
<MkPagination v-slot="{}" :externalItemArray="messages" :pagination="dmsPagination"> <MkChatPreview v-for="message in items" :key="message.id" class="yweeujhr message _block"
<MkChatPreview v-for="message in messages" :key="message.id" class="yweeujhr message _block" :message="message"/> :message="message" />
</MkPagination> </MkPagination>
</div>
</swiper-slide>
<swiper-slide>
<div class="_content yweeujhr groups">
<div class="groupsbuttons">
<MkButton primary class="start" :link="true" to="/my/groups"><i class="ph-user-circle-gear-bold ph-lg"></i> {{ i18n.ts.manageGroups }}</MkButton>
<MkButton primary class="start" @click="startGroup"><i class="ph-plus-bold ph-lg"></i> {{ i18n.ts.startMessaging }}</MkButton>
</div> </div>
<MkPagination v-slot="{}" :externalItemArray="groupMessages" :pagination="groupsPagination"> </swiper-slide>
<MkChatPreview v-for="message in groupMessages" :key="message.id" class="yweeujhr message _block" :message="message"/> <swiper-slide>
</MkPagination> <div class="_content yweeujhr groups">
</div> <div class="groupsbuttons">
</swiper-slide> <MkButton primary class="start" :link="true" to="/my/groups"><i
</swiper> class="ph-user-circle-gear-bold ph-lg"></i> {{ i18n.ts.manageGroups }}</MkButton>
</MkSpacer> <MkButton primary class="start" @click="startGroup"><i class="ph-plus-bold ph-lg"></i> {{
</div> i18n.ts.startMessaging
</MkStickyContainer> }}</MkButton>
</div>
<MkPagination v-slot="{ items }" :pagination="groupsPagination">
<MkChatPreview v-for="message in items" :key="message.id" class="yweeujhr message _block"
:message="message" />
</MkPagination>
</div>
</swiper-slide>
</swiper>
</MkSpacer>
</div>
</MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw, onMounted, onUnmounted, watch } from 'vue'; import { markRaw, onMounted, onUnmounted, watch, computed } from 'vue';
import * as Acct from 'calckey-js/built/acct'; import * as Acct from 'calckey-js/built/acct';
import { Virtual } from 'swiper'; import { Virtual } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/vue'; import { Swiper, SwiperSlide } from 'swiper/vue';
@ -58,8 +62,9 @@ import 'swiper/scss/virtual';
const router = useRouter(); const router = useRouter();
let messages = $ref([]); let messages = $ref([]);
let groupMessages = $ref([]);
let connection = $ref(null); let connection = $ref(null);
let paginationComponentUser = $ref<InstanceType<typeof MkPagination>>();
let paginationComponentGroup = $ref<InstanceType<typeof MkPagination>>();
const tabs = ['dms', 'groups']; const tabs = ['dms', 'groups'];
let tab = $ref(tabs[0]); let tab = $ref(tabs[0]);
@ -89,30 +94,33 @@ definePageMetadata({
const dmsPagination = { const dmsPagination = {
endpoint: 'messaging/history' as const, endpoint: 'messaging/history' as const,
limit: 15, limit: 20,
params: { params: computed(() => ({
group: false, group: false,
}, })),
offsetMode: true,
}; };
const groupsPagination = { const groupsPagination = {
endpoint: 'messaging/history' as const, endpoint: 'messaging/history' as const,
limit: 5, limit: 10,
params: { params: computed(() => ({
group: true, group: true,
}, })),
offsetMode: true,
}; };
function onMessage(message): void { function onMessage(message): void {
if (message.recipientId) { if (message.recipientId) {
messages = messages.filter(m => !( messages = messages.filter(m => !(
(m.recipientId === message.recipientId && m.userId === message.userId) || (m.recipientId === message.recipientId && m.userId === message.userId) ||
(m.recipientId === message.userId && m.userId === message.recipientId))); (m.recipientId === message.userId && m.userId === message.recipientId)));
messages.unshift(message); messages.unshift(message);
} else if (message.groupId) { } else if (message.groupId) {
groupMessages = groupMessages.filter(m => m.groupId !== message.groupId); messages = messages.filter(m => m.groupId !== message.groupId);
groupMessages.unshift(message); messages.unshift(message);
} }
forceRerender();
} }
function onRead(ids): void { function onRead(ids): void {
@ -206,17 +214,17 @@ onUnmounted(() => {
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.yweeujhr { .yweeujhr {
> .start { >.start {
margin: 0 auto var(--margin) auto; margin: 0 auto var(--margin) auto;
}
> .groupsbuttons {
max-width: 100%;
display: flex;
justify-content: center;
margin-bottom: 1rem;
}
} }
</style>
>.groupsbuttons {
max-width: 100%;
display: flex;
justify-content: center;
margin-bottom: 1rem;
}
}
</style>

View File

@ -460,7 +460,7 @@ export const routes = [
{ {
path: "/ads", path: "/ads",
name: "ads", name: "ads",
component: page(() => import("./pages/admin/ads.vue")), component: page(() => import("./pages/admin/promotions.vue")),
}, },
{ {
path: "/database", path: "/database",

View File

@ -28,7 +28,7 @@
</main> </main>
<div class="powered-by"> <div class="powered-by">
<b><MkA to="/">{{ host }}</MkA></b> <b><MkA to="/">{{ host }}</MkA></b>
<small>Powered by <a href="https://lavaforge.org/calckey/calckey" target="_blank">Calckey</a></small> <small>Powered by <a href="https://codeberg.org/calckey/calckey" target="_blank">Calckey</a></small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -14,7 +14,7 @@
</main> </main>
<div v-if="!root" class="powered-by"> <div v-if="!root" class="powered-by">
<b><MkA to="/">{{ host }}</MkA></b> <b><MkA to="/">{{ host }}</MkA></b>
<small>Powered by <a href="https://lavaforge.org/calckey/calckey" target="_blank">Calckey</a></small> <small>Powered by <a href="https://codeberg.org/calckey/calckey" target="_blank">Calckey</a></small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -28,7 +28,7 @@
</div> </div>
<div v-if="poweredBy" class="powered-by"> <div v-if="poweredBy" class="powered-by">
<b><MkA to="/">{{ host }}</MkA></b> <b><MkA to="/">{{ host }}</MkA></b>
<small>Powered by <a href="https://lavaforge.org/calckey/calckey" target="_blank">Calckey</a></small> <small>Powered by <a href="https://codeberg.org/calckey/calckey" target="_blank">Calckey</a></small>
</div> </div>
</template> </template>
</div> </div>