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

feat(frontend): スワイプやボタンでタイムラインを再読込する機能 #12113

Merged
merged 29 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cb4cf50
pc reloading
slofp Oct 21, 2023
e9d0e5b
add: disable TL websocket option
slofp Oct 22, 2023
97d5434
fix: stream disconnect when reload
slofp Oct 22, 2023
9e63971
add: pull to refresh
slofp Oct 22, 2023
3d9ca5d
Merge remote-tracking branch 'upstream/develop' into pr-push-refresh
slofp Oct 22, 2023
3558b33
fix: pull to refresh
slofp Oct 22, 2023
47e994b
add changelog
slofp Oct 22, 2023
aab714b
fact: change to disableStreamingTimeline
slofp Oct 22, 2023
8435a4b
lint
slofp Oct 22, 2023
7354c55
remove: en-US text
slofp Oct 22, 2023
546a994
Merge branch 'develop' into pr/12113
syuilo Oct 23, 2023
b078944
Merge branch 'develop' into pr/12113
syuilo Oct 23, 2023
c79b685
Merge branch 'develop' into pr/12113
syuilo Oct 23, 2023
60b8fc7
Merge branch 'develop' into pr/12113
syuilo Oct 23, 2023
3f4234d
refactor
syuilo Oct 23, 2023
e3c3702
refactor
syuilo Oct 23, 2023
3a5e183
add license identifier
syuilo Oct 23, 2023
28efb71
tweak
syuilo Oct 23, 2023
1105741
Update MkPullToRefresh.vue
syuilo Oct 23, 2023
9705a7c
Update MkPullToRefresh.vue
syuilo Oct 23, 2023
c6ce446
Merge remote-tracking branch 'upstream/develop' into pr-push-refresh-1
slofp Oct 27, 2023
cfe3671
Merge branch 'develop' into pr-push-refresh
syuilo Oct 29, 2023
161608f
Merge branch 'develop' into pr/12113
syuilo Oct 29, 2023
06a630a
Merge branch 'pr-push-refresh' of https://github.com/slofp/misskey in…
syuilo Oct 29, 2023
45e1604
change name timeoutHeartBeat
slofp Oct 29, 2023
47ca54c
Merge branch 'develop' into pr/12113
syuilo Oct 29, 2023
552a74a
Merge branch 'pr-push-refresh' of https://github.com/slofp/misskey in…
syuilo Oct 29, 2023
cf16aec
tweak
syuilo Oct 30, 2023
943e3a4
:art:
syuilo Oct 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
- Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました
- 外部サイトでの実装が必要です。詳細は Misskey Hub をご覧ください
https://misskey-hub.net/docs/advanced/publish-on-your-website.html
- Feat: スワイプしてタイムラインを再読込できるように
- PCの場合は右上のボタンからでも再読込できます
- Feat: タイムラインの自動更新を無効にできるように
- Fix: 投稿フォームでのユーザー変更がプレビューに反映されない問題を修正

### Server
Expand Down
4 changes: 4 additions & 0 deletions locales/en-US.yml
slofp marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,10 @@ impressumDescription: "In some countries, like germany, the inclusion of operato
privacyPolicy: "Privacy Policy"
privacyPolicyUrl: "Privacy Policy URL"
tosAndPrivacyPolicy: "Terms of Service and Privacy Policy"
releaseToRefresh: "Release to reload"
refreshing: "Reloading"
pullDownToRefresh: "Pull down to reload"
disableWebSocket: "Disable realtime update on timeline"
_announcement:
forExistingUsers: "Existing users only"
forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it."
Expand Down
4 changes: 4 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,10 @@ export interface Locale {
"angle": string;
"flip": string;
"showAvatarDecorations": string;
"releaseToRefresh": string;
"refreshing": string;
"pullDownToRefresh": string;
"disableWebSocket": string;
"_announcement": {
"forExistingUsers": string;
"forExistingUsersDescription": string;
Expand Down
4 changes: 4 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,10 @@ detach: "外す"
angle: "角度"
flip: "反転"
showAvatarDecorations: "アイコンのデコレーションを表示"
releaseToRefresh: "離してリロード"
refreshing: "リロード中"
pullDownToRefresh: "引っ張ってリロード"
disableWebSocket: "タイムラインのリアルタイム更新を無効にする"

_announcement:
forExistingUsers: "既存ユーザーのみ"
Expand Down
3 changes: 2 additions & 1 deletion packages/frontend/src/boot/main-boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { common } from './common.js';
import { version, ui, lang, updateLocale } from '@/config.js';
import { i18n, updateI18n } from '@/i18n.js';
import { confirm, alert, post, popup, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import { useStream, isReloading } from '@/stream.js';
import * as sound from '@/scripts/sound.js';
import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
import { defaultStore, ColdDeviceStorage } from '@/store.js';
Expand Down Expand Up @@ -39,6 +39,7 @@ export async function mainBoot() {

let reloadDialogShowing = false;
stream.on('_disconnected_', async () => {
if (isReloading) return;
if (defaultStore.state.serverDisconnectedBehavior === 'reload') {
location.reload();
} else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') {
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/src/components/MkPageWindow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ defineExpose({

<style lang="scss" module>
.root {
overscroll-behavior: none;

min-height: 100%;
background: var(--bg);

Expand Down
6 changes: 6 additions & 0 deletions packages/frontend/src/components/MkPagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const props = withDefaults(defineProps<{

const emit = defineEmits<{
(ev: 'queue', count: number): void;
(ev: 'status', error: boolean): void;
}>();

let rootEl = $shallowRef<HTMLElement>();
Expand Down Expand Up @@ -192,6 +193,11 @@ watch(queue, (a, b) => {
emit('queue', queue.value.size);
}, { deep: true });

watch(error, (n, o) => {
if (n === o) return;
emit('status', n);
});

async function init(): Promise<void> {
items.value = new Map();
queue.value = new Map();
Expand Down
246 changes: 246 additions & 0 deletions packages/frontend/src/components/MkPullToRefresh.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<template>
<div ref="rootEl">
<div v-if="isPullStart" :class="$style.frame" :style="`--frame-min-height: ${currentHeight}px;`">
<div :class="$style.contents">
<i v-if="!isRefreshing" :class="['ti', 'ti-arrow-bar-to-down', { [$style.refresh]: isPullEnd }]"></i>
<MkLoading v-else :em="true"/>
<p v-if="isPullEnd">{{ i18n.ts.releaseToRefresh }}</p>
<p v-else-if="isRefreshing">{{ i18n.ts.refreshing }}</p>
<p v-else>{{ i18n.ts.pullDownToRefresh }}</p>
</div>
</div>
<div :class="{ [$style.slotClip]: isPullStart }">
<slot />
</div>
</div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue';
import { deviceKind } from '@/scripts/device-kind.js';
import { i18n } from '@/i18n.js';

let isPullStart = $ref(false);
let isPullEnd = $ref(false);
let isRefreshing = $ref(false);

const scrollStop = 10;
const maxFrameSize = 75;
const refreshFrameSize = 65;
const fixTimeMs = 200;
let currentHeight = $ref(0);

let supportPointerDesktop = false;
let startScreenY: number | null = null;

const rootEl = $shallowRef<HTMLDivElement>();
let scrollEl: HTMLElement | null = null;

let disabled = false;

const emits = defineEmits<{
(e: 'refresh'): void;

Check failure on line 42 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Identifier 'e' is restricted
}>();

const getScrollableParentElement = (node) => {
if (node == null) {
return null;
}

if (node.scrollHeight > node.clientHeight) {
return node;
}

Check failure on line 52 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Closing curly brace does not appear on the same line as the subsequent block
else {
return getScrollableParentElement(node.parentNode);
}
};

const getScreenY = (event) => {
if (supportPointerDesktop) {
return event.screenY;
}
return event.touches[0].screenY;
};

const moveStart = (e) => {

Check failure on line 65 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Identifier 'e' is restricted
if (!isPullStart && !isRefreshing && !disabled) {
isPullStart = true;
startScreenY = getScreenY(e);
currentHeight = 0;
}
};

const moveBySystem = (to: number): Promise<void> => new Promise(r => {
const startHeight = currentHeight;
const overHeight = currentHeight - to;
if (overHeight < 1) {
r();
return;
}
const startTime = Date.now();
let intervalId = setInterval(() => {
const time = Date.now() - startTime;
if (time > fixTimeMs) {
currentHeight = to;
clearInterval(intervalId);
r();
return;
}
const nextHeight = startHeight - (overHeight / fixTimeMs) * time;
if (currentHeight < nextHeight) return;
currentHeight = nextHeight;
}, 1);
});

const fixOverContent = async () => {
if (currentHeight > refreshFrameSize) {
await moveBySystem(refreshFrameSize);
}
};

const closeContent = async () => {
if (currentHeight > 0) {
await moveBySystem(0);
}
};

const moveEnd = () => {
if (isPullStart && !isRefreshing) {
startScreenY = null;
if (isPullEnd) {
isPullEnd = false;
isRefreshing = true;
fixOverContent().then(() => emits('refresh'));
}

Check failure on line 114 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Closing curly brace does not appear on the same line as the subsequent block
else {
closeContent().then(() => isPullStart = false);
}
}
};

const moving = (e) => {

Check failure on line 121 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Identifier 'e' is restricted
if (!isPullStart || isRefreshing || disabled) return;

if (!scrollEl) {
scrollEl = getScrollableParentElement(rootEl);
}
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? scrollStop : scrollStop + currentHeight)) {
currentHeight = 0;
isPullEnd = false;
moveEnd();
return;
}

if (startScreenY === null) {
startScreenY = getScreenY(e);
}
const moveScreenY = getScreenY(e);

const moveHeight = moveScreenY - startScreenY!;
if (moveHeight < 0) {
currentHeight = 0;
}

Check failure on line 142 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Closing curly brace does not appear on the same line as the subsequent block
else if (moveHeight >= maxFrameSize) {
currentHeight = maxFrameSize;
}

Check failure on line 145 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Closing curly brace does not appear on the same line as the subsequent block
else {
currentHeight = moveHeight;
}

isPullEnd = currentHeight >= refreshFrameSize;
};

onMounted(() => {
supportPointerDesktop = !!window.PointerEvent && deviceKind === 'desktop';

if (supportPointerDesktop) {
rootEl.addEventListener('pointerdown', moveStart);
// ポインターの場合、ポップアップ系の動作をするとdownだけ発火されてupが発火されないため
window.addEventListener('pointerup', moveEnd);
rootEl.addEventListener('pointermove', moving, {passive: true});

Check failure on line 160 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

A space is required after '{'

Check failure on line 160 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

A space is required before '}'
}

Check failure on line 161 in packages/frontend/src/components/MkPullToRefresh.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Closing curly brace does not appear on the same line as the subsequent block
else {
rootEl.addEventListener('touchstart', moveStart);
rootEl.addEventListener('touchend', moveEnd);
rootEl.addEventListener('touchmove', moving, {passive: true});
}
});

onUnmounted(() => {
if (supportPointerDesktop) window.removeEventListener('pointerup', moveEnd);
});

/**
* emit(refresh)が完了したことを知らせる関数
*
* タイムアウトがないのでこれを最終的に実行しないと出たままになる
*/
const refreshFinished = () => {
closeContent().then(() => {
isPullStart = false;
isRefreshing = false;
});
};

const setDisabled = (value) => {
disabled = value;
};

defineExpose({
refreshFinished,
setDisabled,
});

</script>

<style lang="scss" module>
.frame {
position: relative;
overflow: clip;

width: 100%;
min-height: var(--frame-min-height, 0px);

box-shadow: inset 0px -7px 10px -10px rgba(0,0,0,.1);
mask-image: linear-gradient(90deg, #000 0%, #000 80%, transparent);
-webkit-mask-image: -webkit-linear-gradient(90deg, #000 0%, #000 80%, transparent);

pointer-events: none;

> .contents {
position: absolute;

bottom: 0;
width: 100%;

margin: 5px 0;

display: flex;
flex-direction: column;
align-items: center;

margin: 5px 0;

font-size: 14px;

> i, > div {
margin: 6px 0;
}
> i {
transition: transform .25s;

&.refresh {
transform: rotate(180deg);
}
}

> p {
margin: 5px 0;
}
}
}

.slotClip {
overflow-y: clip;
}
</style>
Loading
Loading