Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: v2024.3.1-kakurega.1.35.0 #143

Merged
merged 8 commits into from
Apr 14, 2024
25 changes: 25 additions & 0 deletions CHANGELOG_KAKUREGA.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## 1.35.0
Release: 2024/04/14
Base: 2024.3.1

### 新機能
- ノート内にある絵文字を絵文字ピッカーに追加できる機能を実装
![image](https://media.kakurega.app/static/misskey/cc9f3350-915a-4d96-bffd-df93054881ef.png)
- ノート内にある絵文字をクリック→「絵文字ピッカーに追加」で追加できます
- 既に絵文字ピッカーに追加されている場合は追加できません
- リアクションした絵文字を絵文字ピッカーに追加できる機能を実装
![image](https://media.kakurega.app/static/misskey/f03f1852-2093-4cd3-91ea-37585b017a12.png)
- リアクション一覧から絵文字を右クリック (スマホの場合は長押し) →「絵文字ピッカーに追加」で追加できます
- 既に絵文字ピッカーに追加されている場合は追加できません
- リアクション数の非表示機能を実装
![image](https://media.kakurega.app/static/misskey/f5c550a1-ed02-4baa-bcac-972fe1af910d.png)
- ノート下部のリアクション数と、詳細ページのリアクション数を非表示にできます
- 誰がリアクションをしたのかを非表示にできる機能を実装
![image](https://media.kakurega.app/static/misskey/95edc174-826d-44da-8673-06968f8b60f0.png)
- リアクションをホバーした際のユーザー一覧と、詳細ページのリアクションタブにあるリアクションをしたユーザー一覧を非表示にできます

「リアクション数の非表示機能」「誰がリアクションをしたのかを非表示にできる機能」は、設定→全般から設定できます

### 変更
- 時限ノートで1年以上先の日時を指定できないように変更

## 1.34.1
Release: 2024/04/11
Base: 2024.3.1
Expand Down
38 changes: 38 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5272,6 +5272,10 @@ export interface Locale extends ILocale {
* このノートは{time}に消滅します
*/
"noteDeletationAt": ParameterizedString<"time">;
/**
* 1年以上先の日時を指定することはできません
*/
"cannotScheduleLaterThanOneYear": string;
/**
* アクティビティを非公開にする
*/
Expand All @@ -5296,6 +5300,22 @@ export interface Locale extends ILocale {
* 投稿フォームをリセット
*/
"clearPost": string;
/**
* 絵文字ピッカーに追加
*/
"addToEmojiPicker": string;
/**
* リアクション数の非表示
*/
"hideReactionCount": string;
/**
* 誰がリアクションをしたのかを非表示にする
*/
"hideReactionUsers": string;
/**
* リアクションをホバーした際のユーザー一覧と、ノート詳細ページのリアクションタブにあるリアクションをしたユーザー一覧を非表示にします
*/
"hideReactionUsersDescription": string;
"_bubbleGame": {
/**
* 遊び方
Expand Down Expand Up @@ -10272,6 +10292,24 @@ export interface Locale extends ILocale {
*/
"header": string;
};
"_hideReactionCount": {
/**
* 非表示にしない
*/
"none": string;
/**
* 自分のノートのみ
*/
"self": string;
/**
* 自分以外のノートのみ
*/
"others": string;
/**
* 全てのノート
*/
"all": string;
};
}
declare const locales: {
[lang: string]: Locale;
Expand Down
10 changes: 10 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1314,12 +1314,17 @@ surrender: "やめる"
gameRetry: "リトライ"
scheduledNoteDelete: "ノートの自己消滅"
noteDeletationAt: "このノートは{time}に消滅します"
cannotScheduleLaterThanOneYear: "1年以上先の日時を指定することはできません"
hideActivity: "アクティビティを非公開にする"
hideActivityDescription: "自分のプロフィールのアクティビティ (概要/アクティビティタブ) を他人が見れないようにします。このオプションを有効にしても、自分であればプロフィールのアクティビティタブから引き続き閲覧できます。"
channelAnnouncementDescription: "このお知らせはチャンネルのタイムライン上部に表示されます。最初の1行がタイトルとして表示され、2行目以降はお知らせをタップすることで表示されるようになります。"
postForm: "投稿フォーム"
postFormBottomSettingsDescription: "投稿フォームの下部に表示される項目の並び替えが出来ます。項目をクリックすると削除できます。"
clearPost: "投稿フォームをリセット"
addToEmojiPicker: "絵文字ピッカーに追加"
hideReactionCount: "リアクション数の非表示"
hideReactionUsers: 誰がリアクションをしたのかを非表示にする
hideReactionUsersDescription: "リアクションをホバーした際のユーザー一覧と、ノート詳細ページのリアクションタブにあるリアクションをしたユーザー一覧を非表示にします"

_bubbleGame:
howToPlay: "遊び方"
Expand Down Expand Up @@ -2737,3 +2742,8 @@ _offlineScreen:
title: "オフライン - サーバーに接続できません"
header: "サーバーに接続できません"

_hideReactionCount:
none: "非表示にしない"
self: "自分のノートのみ"
others: "自分以外のノートのみ"
all: "全てのノート"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2024.3.1-kakurega.1.34.1",
"version": "2024.3.1-kakurega.1.35.0",
"codename": "nasubi",
"repository": {
"type": "git",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class FanboxManagementService implements OnApplicationShutdown {
});
}

this.logger.info(`Found ${Object.keys(usersList).length} fanbox supporter(s)`);
this.logger.info(`Found ${usersList.size} fanbox supporter(s)`);
this.logger.info('Cache updated.');
} catch (err: any) {
this.logger.error('Failed to update fanbox supporter cache');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export class PatreonManagementService implements OnApplicationShutdown {
});
}

this.logger.info(`Found ${Object.keys(usersList).length} patreon(s)`);
this.logger.info(`Found ${usersList.size} patreon(s)`);
} catch (err: any) {
this.logger.error('Failed to fetch patreon users');
this.logger.error(err);
Expand Down
10 changes: 10 additions & 0 deletions packages/backend/src/server/api/endpoints/notes/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ export const meta = {
id: '05655b05-5a09-47c3-af56-de0c0900791a',
},

cannotScheduleDeleteLaterThanOneYear: {
message: 'Scheduled delete time is later than one year.',
code: 'CANNOT_SCHEDULE_DELETE_LATER_THAN_ONE_YEAR',
id: 'b02b5edb-2741-4841-b692-d9893f1e6515',
},

noSuchChannel: {
message: 'No such channel.',
code: 'NO_SUCH_CHANNEL',
Expand Down Expand Up @@ -375,6 +381,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} else if (typeof ps.scheduledDelete.deleteAfter === 'number') {
ps.scheduledDelete.deleteAt = Date.now() + ps.scheduledDelete.deleteAfter;
}

if (ps.scheduledDelete.deleteAt && ps.scheduledDelete.deleteAt > Date.now() + ms('1year')) {
throw new ApiError(meta.errors.cannotScheduleDeleteLaterThanOneYear);
}
}

let channel: MiChannel | null = null;
Expand Down
79 changes: 49 additions & 30 deletions packages/frontend/src/components/MkDeleteScheduleEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<div class="zmdxowus">
<div :class="$style.root">
<span>{{ i18n.ts.scheduledNoteDelete }}</span>
<MkInfo v-if="!isValid" warn>{{ i18n.ts.cannotScheduleLaterThanOneYear }}</MkInfo>
<section>
<div>
<MkSelect v-model="expiration" small>
Expand Down Expand Up @@ -41,13 +42,15 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, watch } from 'vue';
import MkInput from './MkInput.vue';
import MkSelect from './MkSelect.vue';
import MkInfo from './MkInfo.vue';
import { formatDateTimeString } from '@/scripts/format-time-string.js';
import { addTime } from '@/scripts/time.js';
import { i18n } from '@/i18n.js';

export type DeleteScheduleEditorModelValue = {
deleteAt: number | null;
deleteAfter: number | null;
isValid: boolean;
};

const props = defineProps<{
Expand All @@ -57,11 +60,12 @@ const emit = defineEmits<{
(ev: 'update:modelValue', v: DeleteScheduleEditorModelValue): void;
}>();

const expiration = ref('at');
const expiration = ref<'at' | 'after'>('at');
const atDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'));
const atTime = ref('00:00');
const after = ref(0);
const unit = ref('second');
const isValid = ref(true);

if (props.modelValue.deleteAt) {
expiration.value = 'at';
Expand All @@ -73,39 +77,56 @@ if (props.modelValue.deleteAt) {
after.value = props.modelValue.deleteAfter / 1000;
}

function get(): DeleteScheduleEditorModelValue {
const calcAt = () => {
return new Date(`${atDate.value} ${atTime.value}`).getTime();
};

const calcAfter = () => {
let base = parseInt(after.value.toString());
switch (unit.value) {
// @ts-expect-error fallthrough
case 'day': base *= 24;
// @ts-expect-error fallthrough
case 'hour': base *= 60;
// @ts-expect-error fallthrough
case 'minute': base *= 60;
// eslint-disable-next-line no-fallthrough
case 'second': return base *= 1000;
default: return null;
}
};
const calcAt = () => {
return new Date(`${atDate.value} ${atTime.value}`).getTime();
};

return {
const calcAfter = () => {
let base = parseInt(after.value.toString());
switch (unit.value) {
// @ts-expect-error fallthrough
case 'day': base *= 24;
// @ts-expect-error fallthrough
case 'hour': base *= 60;
// @ts-expect-error fallthrough
case 'minute': base *= 60;
// eslint-disable-next-line no-fallthrough
case 'second': return base *= 1000;
default: return null;
}
};

const isValidTime = () => {
if (expiration.value === 'at') {
return calcAt() < Date.now() + (1000 * 60 * 60 * 24 * 365);
} else {
const afterMs = calcAfter();
if (afterMs === null) return false;
return afterMs < 1000 * 60 * 60 * 24 * 365;
}
};

isValid.value = isValidTime();

watch([expiration, atDate, atTime, after, unit, isValid], () => {
const isValidTimeValue = isValidTime();
isValid.value = isValidTimeValue;

emit('update:modelValue', {
deleteAt: expiration.value === 'at' ? calcAt() : null,
deleteAfter: expiration.value === 'after' ? calcAfter() : null,
};
}

watch([expiration, atDate, atTime, after, unit], () => emit('update:modelValue', get()), {
isValid: isValidTimeValue,
});
}, {
deep: true,
});
</script>

<style lang="scss" scoped>
.zmdxowus {
<style lang="scss" module>
.root {
display: flex;
flex-direction: column;
gap: 16px;
padding: 8px 16px;

>span {
Expand Down Expand Up @@ -136,8 +157,6 @@ watch([expiration, atDate, atTime, after, unit], () => emit('update:modelValue',
}

>section {
margin: 16px 0 0 0;

>div {
margin: 0 8px;
display: flex;
Expand Down
13 changes: 11 additions & 2 deletions packages/frontend/src/components/MkNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else-if="tab === 'reactions'" :class="$style.tab_reactions">
<div :class="$style.reactionTabs">
<button v-for="reaction in Object.keys(appearNote.reactions)" :key="reaction" :class="[$style.reactionTab, { [$style.reactionTabActive]: reactionTabType === reaction }]" class="_button" @click="reactionTabType = reaction">
<button v-for="reaction in Object.keys(appearNote.reactions)" :key="reaction" :class="[$style.reactionTab, { [$style.reactionTabActive]: reactionTabType === reaction }]" class="_button" @click="reactionTabType = defaultStore.state.hideReactionUsers ? null : reaction">
<MkReactionIcon :reaction="reaction"/>
<span style="margin-left: 4px;">{{ appearNote.reactions[reaction] }}</span>
<span v-if="!hideReactionCount" style="margin-left: 4px;">{{ appearNote.reactions[reaction] }}</span>
</button>
</div>
<MkPagination v-if="reactionTabType" :key="reactionTabType" :pagination="reactionsPagination" :disableAutoLoad="true">
Expand Down Expand Up @@ -283,6 +283,15 @@ const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultS
const conversation = ref<Misskey.entities.Note[]>([]);
const replies = ref<Misskey.entities.Note[]>([]);
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id);
const hideReactionCount = computed(() => {
switch (defaultStore.state.hideReactionCount) {
case 'none': return false;
case 'all': return true;
case 'self': return props.note.userId === $i?.id;
case 'others': return props.note.userId !== $i?.id;
default: return false;
}
});

const keymap = {
'r': () => reply(true),
Expand Down
3 changes: 3 additions & 0 deletions packages/frontend/src/components/MkPostForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ const maxTextLength = computed((): number => {

const canPost = computed((): boolean => {
return !props.mock && !posting.value && !posted.value &&
(scheduledNoteDelete.value ? scheduledNoteDelete.value.isValid : true) &&
(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
(textLength.value <= maxTextLength.value) &&
(!poll.value || poll.value.choices.length >= 2);
Expand Down Expand Up @@ -451,6 +452,7 @@ function toggleScheduledNoteDelete() {
scheduledNoteDelete.value = {
deleteAt: null,
deleteAfter: null,
isValid: true,
};
}
}
Expand Down Expand Up @@ -1039,6 +1041,7 @@ onMounted(() => {
scheduledNoteDelete.value = {
deleteAt: init.deleteAt ? (new Date(init.deleteAt)).getTime() : null,
deleteAfter: null,
isValid: true,
};
}
visibility.value = init.visibility;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkReactionIcon :reaction="reaction" :class="$style.reactionIcon" :noStyle="true"/>
<div :class="$style.reactionName">{{ getReactionName(reaction) }}</div>
</div>
<div :class="$style.users">
<div v-if="users.length" :class="$style.users">
<div v-for="u in users" :key="u.id" :class="$style.user">
<MkAvatar :class="$style.avatar" :user="u"/>
<MkUserName :user="u" :nowrap="true"/>
Expand Down Expand Up @@ -55,9 +55,7 @@ function getReactionName(reaction: string): string {

.reaction {
max-width: 100px;
padding-right: 10px;
text-align: center;
border-right: solid 0.5px var(--divider);
}

.reactionIcon {
Expand All @@ -78,6 +76,8 @@ function getReactionName(reaction: string): string {
margin: -4px 14px 0 10px;
font-size: 0.95em;
text-align: left;
padding-left: 10px;
border-left: solid 0.5px var(--divider);
}

.user {
Expand Down
Loading
Loading