mirror of
https://github.com/kokonect-link/cherrypick
synced 2024-11-23 22:56:53 +09:00
feat(client): ページの更新が必要なときに静かに通知する機能を追加
This commit is contained in:
parent
2cc10aa02d
commit
d56b50a5f1
@ -65,6 +65,7 @@
|
|||||||
- 노트 사이를 띄우는 옵션 활성화 시 알림 페이지의 노트도 띄우도록
|
- 노트 사이를 띄우는 옵션 활성화 시 알림 페이지의 노트도 띄우도록
|
||||||
- 안테나, 그룹, 리스트, 클립 페이지의 생성 버튼을 헤더로 이동
|
- 안테나, 그룹, 리스트, 클립 페이지의 생성 버튼을 헤더로 이동
|
||||||
- 채팅 디자인 일부 개선
|
- 채팅 디자인 일부 개선
|
||||||
|
- 페이지 새로 고침 팝업을 조용히 알리는 기능 추가
|
||||||
- Fix: (Friendly) 위젯 영역에 safe-area-inset-bottom이 적용되지 않음
|
- Fix: (Friendly) 위젯 영역에 safe-area-inset-bottom이 적용되지 않음
|
||||||
- Fix: (Friendly) 플로팅 메뉴를 길게 눌렀을 때 프로필 이미지를 드래그 할 수 있는 문제
|
- Fix: (Friendly) 플로팅 메뉴를 길게 눌렀을 때 프로필 이미지를 드래그 할 수 있는 문제
|
||||||
- Fix: 위젯 편집 시 헤더 이외의 영역을 눌렀을 때 위젯 설정이 뜨는 문제
|
- Fix: 위젯 편집 시 헤더 이외의 영역을 눌렀을 때 위젯 설정이 뜨는 문제
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
_lang_: "English"
|
_lang_: "English"
|
||||||
|
requireRefresh: "When the page needs to refresh"
|
||||||
performanceWarning: "High resource usage can result in higher device temperatures and faster battery consumption"
|
performanceWarning: "High resource usage can result in higher device temperatures and faster battery consumption"
|
||||||
photosensitiveSeizuresWarning: "Can cause photosensitive seizures"
|
photosensitiveSeizuresWarning: "Can cause photosensitive seizures"
|
||||||
friendlyEnableNotification: "Enable/Disable the notification area"
|
friendlyEnableNotification: "Enable/Disable the notification area"
|
||||||
@ -1099,6 +1100,9 @@ later: "Later"
|
|||||||
goToMisskey: "To CherryPick"
|
goToMisskey: "To CherryPick"
|
||||||
additionalEmojiDictionary: "Additional emoji dictionaries"
|
additionalEmojiDictionary: "Additional emoji dictionaries"
|
||||||
installed: "Installed"
|
installed: "Installed"
|
||||||
|
_requireRefreshBehavior:
|
||||||
|
dialog: "Show warning dialog"
|
||||||
|
quiet: "Show unobtrusive alert"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "Your account was successfully created!"
|
accountCreated: "Your account was successfully created!"
|
||||||
letsStartAccountSetup: "For starters, let's set up your profile."
|
letsStartAccountSetup: "For starters, let's set up your profile."
|
||||||
|
11
locales/index.d.ts
vendored
11
locales/index.d.ts
vendored
@ -3,6 +3,10 @@
|
|||||||
// Do not edit this file directly.
|
// Do not edit this file directly.
|
||||||
export interface Locale {
|
export interface Locale {
|
||||||
"_lang_": string;
|
"_lang_": string;
|
||||||
|
"requireRefresh": string;
|
||||||
|
"performanceWarning": string;
|
||||||
|
"photosensitiveSeizuresWarning": string;
|
||||||
|
"friendlyEnableNotification": string;
|
||||||
"useBoldFont": string;
|
"useBoldFont": string;
|
||||||
"newNoteReceivedNotification": string;
|
"newNoteReceivedNotification": string;
|
||||||
"disableRightClick": string;
|
"disableRightClick": string;
|
||||||
@ -1099,9 +1103,10 @@ export interface Locale {
|
|||||||
"goToMisskey": string;
|
"goToMisskey": string;
|
||||||
"additionalEmojiDictionary": string;
|
"additionalEmojiDictionary": string;
|
||||||
"installed": string;
|
"installed": string;
|
||||||
"performanceWarning": string;
|
"_requireRefreshBehavior": {
|
||||||
"photosensitiveSeizuresWarning": string;
|
"dialog": string;
|
||||||
"friendlyEnableNotification": string;
|
"quiet": string;
|
||||||
|
};
|
||||||
"_initialAccountSetting": {
|
"_initialAccountSetting": {
|
||||||
"accountCreated": string;
|
"accountCreated": string;
|
||||||
"letsStartAccountSetup": string;
|
"letsStartAccountSetup": string;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
_lang_: "日本語"
|
_lang_: "日本語"
|
||||||
|
|
||||||
|
requireRefresh: "페이지 새로 고침이 필요할 때"
|
||||||
performanceWarning: "リソースを多く使用するため、デバイスの温度が高くなり、バッテリーの消耗が速くなる可能性があります"
|
performanceWarning: "リソースを多く使用するため、デバイスの温度が高くなり、バッテリーの消耗が速くなる可能性があります"
|
||||||
photosensitiveSeizuresWarning: "光敏感性発作を起こす可能性があります"
|
photosensitiveSeizuresWarning: "光敏感性発作を起こす可能性があります"
|
||||||
friendlyEnableNotification: "通知領域を有効化/無効化"
|
friendlyEnableNotification: "通知領域を有効化/無効化"
|
||||||
@ -1100,6 +1101,10 @@ goToMisskey: "CherryPickへ"
|
|||||||
additionalEmojiDictionary: "絵文字の追加辞書"
|
additionalEmojiDictionary: "絵文字の追加辞書"
|
||||||
installed: "インストール済み"
|
installed: "インストール済み"
|
||||||
|
|
||||||
|
_requireRefreshBehavior:
|
||||||
|
dialog: "ダイアログで通知"
|
||||||
|
quiet: "控えめに通知"
|
||||||
|
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "アカウントの作成が完了しました!"
|
accountCreated: "アカウントの作成が完了しました!"
|
||||||
letsStartAccountSetup: "アカウントの初期設定を行いましょう。"
|
letsStartAccountSetup: "アカウントの初期設定を行いましょう。"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
_lang_: "한국어"
|
_lang_: "한국어"
|
||||||
|
requireRefresh: "페이지 새로 고침이 필요할 때"
|
||||||
performanceWarning: "리소스를 많이 사용하므로, 디바이스의 온도가 높아지고 배터리의 소모가 빨라질 수 있어요"
|
performanceWarning: "리소스를 많이 사용하므로, 디바이스의 온도가 높아지고 배터리의 소모가 빨라질 수 있어요"
|
||||||
photosensitiveSeizuresWarning: "광과민성 발작을 일으킬 수 있어요"
|
photosensitiveSeizuresWarning: "광과민성 발작을 일으킬 수 있어요"
|
||||||
friendlyEnableNotification: "알림 영역 활성화/비활성화"
|
friendlyEnableNotification: "알림 영역 활성화/비활성화"
|
||||||
@ -1100,6 +1101,9 @@ later: "나중에"
|
|||||||
goToMisskey: "CherryPick으로"
|
goToMisskey: "CherryPick으로"
|
||||||
additionalEmojiDictionary: "이모지 추가 사전"
|
additionalEmojiDictionary: "이모지 추가 사전"
|
||||||
installed: "설치됨"
|
installed: "설치됨"
|
||||||
|
_requireRefreshBehavior:
|
||||||
|
dialog: "알림창 표시"
|
||||||
|
quiet: "조용히 알림"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "계정 생성이 완료되었어요!"
|
accountCreated: "계정 생성이 완료되었어요!"
|
||||||
letsStartAccountSetup: "계정의 초기 설정을 진행해 볼까요?"
|
letsStartAccountSetup: "계정의 초기 설정을 진행해 볼까요?"
|
||||||
|
@ -71,18 +71,21 @@ import { $i } from '@/account';
|
|||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
import { miLocalStorage } from '@/local-storage';
|
import { miLocalStorage } from '@/local-storage';
|
||||||
import { unisonReload } from '@/scripts/unison-reload';
|
import { unisonReload } from '@/scripts/unison-reload';
|
||||||
|
import { eventBus } from '@/scripts/cherrypick/eventBus';
|
||||||
|
|
||||||
const fontSizeBefore = ref(miLocalStorage.getItem('fontSize'));
|
const fontSizeBefore = ref(miLocalStorage.getItem('fontSize'));
|
||||||
const useBoldFont = ref(miLocalStorage.getItem('useBoldFont'));
|
const useBoldFont = ref(miLocalStorage.getItem('useBoldFont'));
|
||||||
|
|
||||||
async function reloadAsk() {
|
async function reloadAsk() {
|
||||||
const { canceled } = await os.confirm({
|
if (defaultStore.state.requireRefreshBehavior === 'dialog') {
|
||||||
type: 'info',
|
const { canceled } = await os.confirm({
|
||||||
text: i18n.ts.reloadToApplySetting,
|
type: 'info',
|
||||||
});
|
text: i18n.ts.reloadToApplySetting,
|
||||||
if (canceled) return;
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
unisonReload();
|
unisonReload();
|
||||||
|
} else eventBus.emit('hasRequireRefresh', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fontSize = computed(defaultStore.makeGetterSetter('fontSize'));
|
const fontSize = computed(defaultStore.makeGetterSetter('fontSize'));
|
||||||
|
@ -215,6 +215,11 @@
|
|||||||
<option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
|
<option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
|
||||||
<option value="none">{{ i18n.ts._serverDisconnectedBehavior.none }}</option>
|
<option value="none">{{ i18n.ts._serverDisconnectedBehavior.none }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
<MkSelect v-model="requireRefreshBehavior">
|
||||||
|
<template #label>{{ i18n.ts.requireRefresh }}</template>
|
||||||
|
<option value="dialog">{{ i18n.ts._requireRefreshBehavior.dialog }}</option>
|
||||||
|
<option value="quiet">{{ i18n.ts._requireRefreshBehavior.quiet }}</option>
|
||||||
|
</MkSelect>
|
||||||
<MkSelect v-model="newNoteReceivedNotificationBehavior">
|
<MkSelect v-model="newNoteReceivedNotificationBehavior">
|
||||||
<template #label>{{ i18n.ts.newNoteReceivedNotification }}</template>
|
<template #label>{{ i18n.ts.newNoteReceivedNotification }}</template>
|
||||||
<option value="default">{{ i18n.ts._newNoteReceivedNotificationBehavior.default }}</option>
|
<option value="default">{{ i18n.ts._newNoteReceivedNotificationBehavior.default }}</option>
|
||||||
@ -264,6 +269,7 @@ import { unisonReload } from '@/scripts/unison-reload';
|
|||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { miLocalStorage } from '@/local-storage';
|
import { miLocalStorage } from '@/local-storage';
|
||||||
|
import { eventBus } from '@/scripts/cherrypick/eventBus';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
|
||||||
const lang = ref(miLocalStorage.getItem('lang'));
|
const lang = ref(miLocalStorage.getItem('lang'));
|
||||||
@ -274,13 +280,15 @@ const fontSizeBefore = ref(miLocalStorage.getItem('fontSize'));
|
|||||||
const useBoldFont = ref(miLocalStorage.getItem('useBoldFont'));
|
const useBoldFont = ref(miLocalStorage.getItem('useBoldFont'));
|
||||||
|
|
||||||
async function reloadAsk() {
|
async function reloadAsk() {
|
||||||
const { canceled } = await os.confirm({
|
if (requireRefreshBehavior.value === 'dialog') {
|
||||||
type: 'info',
|
const { canceled } = await os.confirm({
|
||||||
text: i18n.ts.reloadToApplySetting,
|
type: 'info',
|
||||||
});
|
text: i18n.ts.reloadToApplySetting,
|
||||||
if (canceled) return;
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
unisonReload();
|
unisonReload();
|
||||||
|
} else eventBus.emit('hasRequireRefresh', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
|
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
|
||||||
@ -319,6 +327,7 @@ const postFormVisibilityHotkey = computed(defaultStore.makeGetterSetter('postFor
|
|||||||
const newNoteReceivedNotificationBehavior = computed(defaultStore.makeGetterSetter('newNoteReceivedNotificationBehavior'));
|
const newNoteReceivedNotificationBehavior = computed(defaultStore.makeGetterSetter('newNoteReceivedNotificationBehavior'));
|
||||||
const fontSize = computed(defaultStore.makeGetterSetter('fontSize'));
|
const fontSize = computed(defaultStore.makeGetterSetter('fontSize'));
|
||||||
const collapseDefault = computed(defaultStore.makeGetterSetter('collapseDefault'));
|
const collapseDefault = computed(defaultStore.makeGetterSetter('collapseDefault'));
|
||||||
|
const requireRefreshBehavior = computed(defaultStore.makeGetterSetter('requireRefreshBehavior'));
|
||||||
|
|
||||||
watch(lang, () => {
|
watch(lang, () => {
|
||||||
miLocalStorage.setItem('lang', lang.value as string);
|
miLocalStorage.setItem('lang', lang.value as string);
|
||||||
|
@ -52,6 +52,7 @@ import { defaultStore } from '@/store';
|
|||||||
import { unisonReload } from '@/scripts/unison-reload';
|
import { unisonReload } from '@/scripts/unison-reload';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
import { eventBus } from '@/scripts/cherrypick/eventBus';
|
||||||
import { deepClone } from '@/scripts/clone';
|
import { deepClone } from '@/scripts/clone';
|
||||||
|
|
||||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||||
@ -64,13 +65,15 @@ const items = ref(defaultStore.state.menu.map(x => ({
|
|||||||
const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
|
const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
|
||||||
|
|
||||||
async function reloadAsk() {
|
async function reloadAsk() {
|
||||||
const { canceled } = await os.confirm({
|
if (defaultStore.state.requireRefreshBehavior === 'dialog') {
|
||||||
type: 'info',
|
const { canceled } = await os.confirm({
|
||||||
text: i18n.ts.reloadToApplySetting,
|
type: 'info',
|
||||||
});
|
text: i18n.ts.reloadToApplySetting,
|
||||||
if (canceled) return;
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
unisonReload();
|
unisonReload();
|
||||||
|
} else eventBus.emit('hasRequireRefresh', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addItem() {
|
async function addItem() {
|
||||||
|
@ -87,6 +87,7 @@ import { signout, $i } from '@/account';
|
|||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { unisonReload } from '@/scripts/unison-reload';
|
import { unisonReload } from '@/scripts/unison-reload';
|
||||||
|
import { eventBus } from '@/scripts/cherrypick/eventBus';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
|
|
||||||
const reportError = computed(defaultStore.makeGetterSetter('reportError'));
|
const reportError = computed(defaultStore.makeGetterSetter('reportError'));
|
||||||
@ -128,13 +129,15 @@ async function deleteAccount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function reloadAsk() {
|
async function reloadAsk() {
|
||||||
const { canceled } = await os.confirm({
|
if (defaultStore.state.requireRefreshBehavior === 'dialog') {
|
||||||
type: 'info',
|
const { canceled } = await os.confirm({
|
||||||
text: i18n.ts.reloadToApplySetting,
|
type: 'info',
|
||||||
});
|
text: i18n.ts.reloadToApplySetting,
|
||||||
if (canceled) return;
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
unisonReload();
|
unisonReload();
|
||||||
|
} else eventBus.emit('hasRequireRefresh', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch([
|
watch([
|
||||||
|
@ -91,6 +91,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
|||||||
'postFormVisibilityHotkey',
|
'postFormVisibilityHotkey',
|
||||||
'newNoteReceivedNotificationBehavior',
|
'newNoteReceivedNotificationBehavior',
|
||||||
'collapseDefault',
|
'collapseDefault',
|
||||||
|
'requireRefreshBehavior',
|
||||||
];
|
];
|
||||||
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
||||||
'lightTheme',
|
'lightTheme',
|
||||||
|
@ -156,13 +156,15 @@ function focus(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function reloadAsk() {
|
async function reloadAsk() {
|
||||||
const { canceled } = await os.confirm({
|
if (defaultStore.state.requireRefreshBehavior === 'dialog') {
|
||||||
type: 'info',
|
const { canceled } = await os.confirm({
|
||||||
text: i18n.ts.reloadToApplySetting,
|
type: 'info',
|
||||||
});
|
text: i18n.ts.reloadToApplySetting,
|
||||||
if (canceled) return;
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
unisonReload();
|
unisonReload();
|
||||||
|
} else eventBus.emit('hasRequireRefresh', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = $computed(() => [{
|
||||||
|
@ -367,6 +367,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||||||
where: 'account',
|
where: 'account',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
requireRefreshBehavior: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'dialog' as 'quiet' | 'dialog',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div v-if="hasRequireRefresh && defaultStore.state.requireRefreshBehavior === 'quiet'" :class="$style.root" class="_panel _shadow" @click="resetRequireRefresh">
|
||||||
|
<div><i class="ti ti-alert-circle"></i> {{ i18n.ts.reloadToApplySetting2 }}</div>
|
||||||
|
<div :class="$style.command" class="_buttons">
|
||||||
|
<MkButton small primary @click="reload">{{ i18n.ts.reload }}</MkButton>
|
||||||
|
<MkButton small>{{ i18n.ts.doNothing }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="hasDisconnected && defaultStore.state.serverDisconnectedBehavior === 'quiet'" :class="$style.root" class="_panel _shadow" @click="resetDisconnected">
|
<div v-if="hasDisconnected && defaultStore.state.serverDisconnectedBehavior === 'quiet'" :class="$style.root" class="_panel _shadow" @click="resetDisconnected">
|
||||||
<div><i class="ti ti-alert-triangle"></i> {{ i18n.ts.disconnectedFromServer }}</div>
|
<div><i class="ti ti-alert-triangle"></i> {{ i18n.ts.disconnectedFromServer }}</div>
|
||||||
<div :class="$style.command" class="_buttons">
|
<div :class="$style.command" class="_buttons">
|
||||||
@ -9,15 +16,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted } from 'vue';
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
import { useStream } from '@/stream';
|
import { useStream } from '@/stream';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
import { eventBus } from '@/scripts/cherrypick/eventBus';
|
||||||
|
|
||||||
const zIndex = os.claimZIndex('high');
|
const zIndex = os.claimZIndex('high');
|
||||||
|
|
||||||
|
let hasRequireRefresh = $ref(false);
|
||||||
let hasDisconnected = $ref(false);
|
let hasDisconnected = $ref(false);
|
||||||
|
|
||||||
function onDisconnected() {
|
function onDisconnected() {
|
||||||
@ -28,12 +37,22 @@ function resetDisconnected() {
|
|||||||
hasDisconnected = false;
|
hasDisconnected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetRequireRefresh() {
|
||||||
|
hasRequireRefresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
useStream().on('_disconnected_', onDisconnected);
|
useStream().on('_disconnected_', onDisconnected);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
eventBus.on('hasRequireRefresh', (hasRequireRefresh_receive) => {
|
||||||
|
hasRequireRefresh = hasRequireRefresh_receive;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
useStream().off('_disconnected_', onDisconnected);
|
useStream().off('_disconnected_', onDisconnected);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user