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

FormSelector.vueに検索機能を追加 #4243

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9d55813
templateとscriptを編集し、キャッシュ削除ボタンを"削除する"1つに統一
reiroop Dec 2, 2023
7b8b5ac
見た目をデザインの通りに変更
reiroop Dec 2, 2023
27c9778
CacheManager.vueのキャッシュ項目をKBのみに変更
reiroop Dec 24, 2023
d6a2567
CacheClearモーダルの中に入れるロジックを仮作成(動作未確認)
reiroop Dec 24, 2023
5af2ebb
CacheClearModalのプロトタイプができた
reiroop Dec 24, 2023
fd53bb8
モーダルのボタンの見た目を調整
reiroop Jan 1, 2024
db4e645
Merge remote-tracking branch 'origin/master' into fix/setting_browser…
reiroop Jan 1, 2024
d2d0dd7
formCheckboxを利用しない形に変更
reiroop Jan 2, 2024
9e4624e
checkboxのロジックが完成
reiroop Jan 3, 2024
c7e07fe
見た目を調整
reiroop Jan 3, 2024
bf1648c
デバッグ用のselectedCachesを下に移動
reiroop Jan 3, 2024
449a588
見た目を調整
reiroop Jan 3, 2024
2a9da5a
モーダルのsubtitleがアイコンがないときにtitleの位置からずれていた問題を修正
reiroop Jan 4, 2024
8d68d23
モーダルのbodyの余白を調整
reiroop Jan 4, 2024
8a81fc5
FormCheckboxInnerを使ってcheckboxをいい感じに
reiroop Jan 4, 2024
5f6b6e6
checkbox非選択時にグレーアウトするように変更
reiroop Jan 4, 2024
d6acd10
何も選択されていない時に「削除する」がdisabledになるように
reiroop Jan 4, 2024
2b91a39
checkboxのフォーカス時にenterキーで選択状態を変えられるように
reiroop Jan 4, 2024
79a83df
clearCacheを整理
reiroop Jan 4, 2024
845962b
修正忘れを修正
reiroop Jan 5, 2024
f8968db
checkboxをコンポーネントに分割
reiroop Jan 5, 2024
7aef582
「削除する」でモーダルが閉じるよう変更
reiroop Jan 5, 2024
a8dcb45
スマホでlabelのレイアウトが崩れていたのを修正
reiroop Jan 5, 2024
b43355f
FormCheckboxWithLabelSlotを削除し、FormCheckboxを書き換え
reiroop Jan 6, 2024
df07042
aria-checkedの削除
reiroop Jan 10, 2024
85cb661
FormCheckboxを利用していたコンポーネントの修正(未完)
reiroop Jan 10, 2024
d846bb8
type CacheNameに関する変更
reiroop Jan 10, 2024
e491a06
promisesの修正
reiroop Jan 13, 2024
525fdc9
不要なreturnを削除
reiroop Feb 24, 2024
c519812
cacheNameToIsSelectedに変更
reiroop Feb 24, 2024
368c1bf
コメントの追加
reiroop Feb 29, 2024
a1c9699
navigator.storage.estimate()の使用をやめ、cache.matchAll()を使用
reiroop Feb 29, 2024
86b00cf
updateCacheSizeを作成、データ使用量が0になる
reiroop Feb 29, 2024
d46f9c2
キャッシュサイズの表示が常に0になっているバグがある
reiroop Mar 7, 2024
f46306b
updateCacheSize周りのバグ確認
reiroop Mar 12, 2024
df31d5a
CacheName, CacheNamesの定義を変更
reiroop Mar 12, 2024
fe9f8a5
FormSelectorに検索機能を追加
reiroop Mar 15, 2024
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
5 changes: 3 additions & 2 deletions src/components/Modal/Common/ModalFrame.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { useModalStore } from '/@/store/ui/modal'
withDefaults(
defineProps<{
iconMdi?: boolean
iconName: string
iconName?: string
title: string
subtitle?: string
returnButton?: boolean
Expand Down Expand Up @@ -64,11 +64,12 @@ const { clearModal } = useModalStore()
}
.body {
width: 100%;
padding: 16px 24px;
padding: 16px;
overflow: {
x: hidden;
y: auto;
}
scrollbar-gutter: stable;
}
</style>
<!-- TODO: 依存しているコンポーネントが未修正 -->
19 changes: 14 additions & 5 deletions src/components/Modal/Common/ModalHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
<modal-return-button v-if="returnButton" :class="$style.returnButton" />
<div :class="$style.content">
<h1 :class="$style.title">
<a-icon :class="$style.icon" :name="iconName" :mdi="iconMdi" />{{
title
}}
<a-icon
v-if="iconName"
:class="$style.icon"
:name="iconName"
:mdi="iconMdi"
/>
{{ title }}
</h1>
<h2 :class="$style.subtitle"><slot name="subtitle" /></h2>
<h2 :class="$style.subtitle" :data-has-icon="$boolAttr(!iconName)">
<slot name="subtitle" />
</h2>
</div>
</div>
</template>
Expand All @@ -19,7 +25,7 @@ import ModalReturnButton from './ModalReturnButton.vue'
withDefaults(
defineProps<{
iconMdi?: boolean
iconName: string
iconName?: string
title: string
subtitle?: string
returnButton?: boolean
Expand Down Expand Up @@ -69,6 +75,9 @@ withDefaults(
weight: 500;
size: 0.875rem;
}
&[data-has-icon] {
padding-left: 0;
}
}
.icon {
margin-right: 16px;
Expand Down
5 changes: 3 additions & 2 deletions src/components/Modal/GroupCreateModal/GroupCreateModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
<form-checkbox
v-model="addMember"
:class="[$style.item, $style.memberCheckbox]"
label="自分自身をメンバーに追加する"
/>
>
自分自身をメンバーに追加する
</form-checkbox>
<div :class="$style.createButtonWrapper">
<form-button label="作成" @click="create" />
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/components/Modal/ModalContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import GroupCreateModal from './GroupCreateModal/GroupCreateModal.vue'
import GroupMemberEditModal from './GroupMemberEditModal/GroupMemberEditModal.vue'
import GroupAdminAddModal from './GroupAdminAddModal/GroupAdminAddModal.vue'
import GroupMemberAddModal from './GroupMemberAddModal/GroupMemberAddModal.vue'
import SettingsCacheClearModal from './SettingsCacheClearModal/SettingsCacheClearModal.vue'

const { shouldShowModal, currentState } = useModalStore()

Expand Down Expand Up @@ -96,6 +97,8 @@ const component = computed(() => {
return GroupAdminAddModal
case 'group-member-add':
return GroupMemberAddModal
case 'settings-cache-clear':
return SettingsCacheClearModal
}
// eslint-disable-next-line no-console
console.error('Unexpected modal type:', currentState.value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<template>
<modal-frame
title="キャッシュの削除"
subtitle="キャッシュを削除する項目を選んで下さい。"
>
<form-button label="update" type="secondary" @click="updateCacheSize" />
<div :class="$style.content">
<form-checkbox
v-for="name in cacheNames"
:key="name"
v-model="cacheNameToIsSelected[name]"
:class="$style.checkbox"
>
<div :class="$style.label">
{{ cacheLabel(name) }}
<span>{{ cacheSize[name] }}</span>
</div>
</form-checkbox>
<div :class="$style.buttonContainer">
<form-button label="キャンセル" type="tertiary" @click="clearModal" />
<form-button
label="削除する"
type="secondary"
:disabled="!anyCacheSelected"
is-danger
@click="clearCache"
/>
</div>
</div>
</modal-frame>
</template>

<script lang="ts" setup>
import { onMounted, ref, computed } from 'vue'
import { useToastStore } from '/@/store/ui/toast'
import { wait } from '/@/lib/basic/timer'
import ModalFrame from '../Common/ModalFrame.vue'
import FormButton from '/@/components/UI/FormButton.vue'
import { useModalStore } from '/@/store/ui/modal'
import FormCheckbox from '/@/components/UI/FormCheckbox.vue'
import { prettifyFileSize } from '/@/lib/basic/file'

declare global {
interface StorageEstimate {
usageDetails: Record<CacheName, number>
}
}

const confirmClear = () => window.confirm('本当に削除しますか?')

/* CacheStorageのnameはsw.jsを参照 */
const clearCacheStorage = (cacheName: string) => window.caches.delete(cacheName)

const { addSuccessToast } = useToastStore()
const showToast = (extraMessage?: string) => {
addSuccessToast(
`削除に成功しました${extraMessage ? `: ${extraMessage}` : ''}`
)
}

const cacheNames = [
'traQ_S-precache',
'files-cache',
'thumbnail-cache'
] as const
type CacheName = (typeof cacheNames)[number]

const cacheNameToIsSelected = ref<Record<CacheName, boolean>>({
'traQ_S-precache': false,
'files-cache': false,
'thumbnail-cache': false
})
const anyCacheSelected = computed(() => {
return Object.values(cacheNameToIsSelected).includes(true)
})

const cacheSize = ref<Record<CacheName, number>>({
'traQ_S-precache': -1,
'files-cache': -1,
'thumbnail-cache': -1
})

onMounted(() => {
updateCacheSize()
})

const updateCacheSize = async () => {
await Promise.all(
cacheNames.map(async name => {
cacheSize.value[name] = await calculateCacheSize(name)
})
)
}

const calculateCacheSize = async (cacheName: CacheName) => {
const cache = await caches.open(cacheName)
const keys = await cache.keys()
let size = 0
Promise.all(
keys.map(async key => {
const response = await cache.match(key)
if (!response) return
const blob = await response.blob()
size += blob.size
})
)
return size
}

const cacheLabel = (cacheName: CacheName) => {
switch (cacheName) {
case 'traQ_S-precache':
return 'traQ本体'
case 'files-cache':
return 'ファイルの本体一覧'
case 'thumbnail-cache':
return 'ファイルのサムネイル一覧'
default:
throw new Error(`Unknown cache name: ${cacheName satisfies CacheName}`)
}
}

const { clearModal } = useModalStore()

const isClearingCache = ref<boolean>(false)

// TODO: 名前の改善
const clearMainCache = async () => {
const names = await window.caches.keys()
return names
.filter(name => name.startsWith('traQ_S-precache'))
.map(name => clearCacheStorage(name))
}

// TODO: 処理を綺麗にできると嬉しい
// TODO: promise周りの処理順の確認
// TODO: キャッシュの対応関係の確認
const clearCache = async () => {
if (isClearingCache.value || !confirmClear()) return
isClearingCache.value = true
const promises = []
if (cacheNameToIsSelected.value['traQ_S-precache']) {
promises.push(clearMainCache())
}
if (cacheNameToIsSelected.value['files-cache']) {
promises.push(clearCacheStorage('files-cache'))
}
if (cacheNameToIsSelected.value['thumbnail-cache']) {
promises.push(clearCacheStorage('thumbnail-cache'))
}
await Promise.all(promises.flat())
if (!cacheNameToIsSelected.value['traQ_S-precache']) {
isClearingCache.value = false
clearModal()
showToast()
return
}
const registration = await navigator.serviceWorker?.getRegistration()
if (!registration) {
isClearingCache.value = false
clearModal()
showToast()
return
}
registration.unregister()
isClearingCache.value = false
clearModal()
showToast('1秒後にリロードします')
await wait(1000)
window.location.reload()
}
</script>

<style lang="scss" module>
.content {
display: flex;
flex-direction: column;
gap: 32px;
}
.checkboxContainer {
display: flex;
flex-direction: column;
gap: 8px;
}
.checkbox {
@include color-ui-secondary;
&:has(:checked) {
@include color-ui-primary;
}
display: flex;
justify-content: space-between;
gap: 4px;
padding: 8px;
align-items: center;
cursor: pointer;
border: solid 2px transparent;
border-radius: 4px;
&:focus-within {
border-color: $theme-accent-focus-default;
}
}
.label {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 8px 16px;
}
.buttonContainer {
display: flex;
justify-content: flex-end;
gap: 16px;
}
</style>
Loading