diff --git a/src/hooks/useDatabase.ts b/src/hooks/useDatabase.ts index 5f738ec..639927b 100644 --- a/src/hooks/useDatabase.ts +++ b/src/hooks/useDatabase.ts @@ -20,8 +20,6 @@ export const useDatabase = () => { const { handleError } = useError(); - - // タイムスタンプをデータベースに保存する関数 --------------------------------------------------------------------------- const saveTimeStampDb = async (targetTree: UniqueIdentifier | null) => { if (!uid) { diff --git a/src/hooks/useFirebaseConnection.ts b/src/hooks/useFirebaseConnection.ts new file mode 100644 index 0000000..2253c8e --- /dev/null +++ b/src/hooks/useFirebaseConnection.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from 'react'; +import { getDatabase, ref, onValue } from 'firebase/database'; +import { useAppStateStore } from '../store/appStateStore'; + +export const useFirebaseConnection = () => { + const [isConnected, setIsConnected] = useState(false); + + const isLoading = useAppStateStore((state) => state.isLoading); + const setIsLoading = useAppStateStore((state) => state.setIsLoading); + + useEffect(() => { + const db = getDatabase(); + const connectedRef = ref(db, '.info/connected'); + const unsubscribe = onValue(connectedRef, (snap) => { + setIsConnected(snap.val() === true); + if (snap.val() === true && isLoading) { + setIsLoading(false); + } else if (snap.val() === false && !isLoading) { + setIsLoading(true); + } + }); + + return () => unsubscribe(); + }, [isLoading, setIsLoading]); + + return isConnected; +}; \ No newline at end of file diff --git a/src/hooks/useIndexedDb.ts b/src/hooks/useIndexedDb.ts index ccd9aa6..879d7a4 100644 --- a/src/hooks/useIndexedDb.ts +++ b/src/hooks/useIndexedDb.ts @@ -143,6 +143,9 @@ export const useIndexedDb = () => { // IndexedデータベースからAppの設定を読み込む ------------------------------------------------ const loadSettingsFromIdb = async () => { + if (!uid) { + return; + } try { const appState = await idb.appstate.get(1); if (appState) { @@ -159,6 +162,9 @@ export const useIndexedDb = () => { // Indexedデータベースからクイックメモを読み込む ------------------------------------------------ const loadQuickMemoFromIdb = async () => { + if (!uid) { + return; + } try { const appState = await idb.appstate.get(1); if (appState) { @@ -172,6 +178,9 @@ export const useIndexedDb = () => { // Indexedデータベースからツリーリストを読み込む ------------------------------------------------ const loadTreesListFromIdb = async () => { + if (!uid) { + return; + } try { const appState = await idb.appstate.get(1); if (appState) { @@ -184,6 +193,9 @@ export const useIndexedDb = () => { // IndexedデータベースからDarkMode設定を読み込む ------------------------------------------------ const loadDarkModeFromIdb = async () => { + if (!uid) { + return; + } try { const appState = await idb.appstate.get(1); if (appState) { @@ -196,6 +208,9 @@ export const useIndexedDb = () => { // Indexedデータベースから完了済みアイテムの非表示設定を読み込む ------------------------------------------------ const loadHideDoneItemsFromIdb = async () => { + if (!uid) { + return; + } try { const appState = await idb.appstate.get(1); if (appState) { @@ -208,6 +223,9 @@ export const useIndexedDb = () => { // Indexedデータベースから指定されたツリーのデータを読み込む ------------------------------------------------ const loadCurrentTreeDataFromIdb = async (targetTree: UniqueIdentifier) => { + if (!uid || !targetTree) { + return; + } try { const treeData = await idb.treestate.get(targetTree); if (treeData) { @@ -354,6 +372,9 @@ export const useIndexedDb = () => { // 指定されたツリーのデータをIndexedデータベースに保存 ------------------------------------------------ const copyTreeDataToIdbFromDb = async (targetTree: UniqueIdentifier) => { + if (!uid || !targetTree) { + return; + } const treeData: TreesListItemIncludingItems = { id: targetTree, name: '', diff --git a/src/hooks/useObserve.ts b/src/hooks/useObserve.ts index ba0f9c8..9a8a411 100644 --- a/src/hooks/useObserve.ts +++ b/src/hooks/useObserve.ts @@ -1,15 +1,16 @@ import { useEffect } from 'react'; import isEqual from 'lodash/isEqual'; import { get } from 'firebase/database'; -import { useTreeStateStore } from '../store/treeStateStore'; import { useTreeManagement } from './useTreeManagement'; import { useAppStateManagement } from './useAppStateManagement'; -import { useDialogStore } from '../store/dialogStore'; +import { useFirebaseConnection } from './useFirebaseConnection'; import { useError } from './useError'; import { useDatabase } from './useDatabase'; import { useIndexedDb } from './useIndexedDb'; import { getDatabase, ref, onValue, } from 'firebase/database'; import { useAppStateStore } from '../store/appStateStore'; +import { useTreeStateStore } from '../store/treeStateStore'; +import { useDialogStore } from '../store/dialogStore'; import { Preferences } from '@capacitor/preferences'; export const useObserve = () => { @@ -47,6 +48,7 @@ export const useObserve = () => { } = useIndexedDb(); const { loadQuickMemoFromDb, saveQuickMemoDb } = useAppStateManagement(); const { handleError } = useError(); + const isConnected = useFirebaseConnection(); // サーバのタイムスタンプを監視 ------------------------------------------------ const observeTimeStamp = async () => { @@ -133,7 +135,7 @@ export const useObserve = () => { // ローカルitemsの変更を監視し、データベースに保存 --------------------------------------------------------------------------- useEffect(() => { - if ((!uid && !isOffline) || !currentTree || isEqual(items, prevItems)) { + if ((!uid && !isOffline) || !currentTree || isEqual(items, prevItems) || !isConnected) { return; } @@ -189,7 +191,7 @@ export const useObserve = () => { // ローカルのクイックメモの変更を監視し、データベースに保存 --------------------------------------------------------------------------- useEffect(() => { - if (!uid) { + if (!uid || !isConnected || !quickMemoText) { return; } if (isLoadedMemoFromDb) { diff --git a/src/hooks/useTreeManagement.ts b/src/hooks/useTreeManagement.ts index 7ee47f8..4919e17 100644 --- a/src/hooks/useTreeManagement.ts +++ b/src/hooks/useTreeManagement.ts @@ -7,9 +7,9 @@ import { getFunctions, httpsCallable } from 'firebase/functions'; import { useAppStateStore } from '../store/appStateStore'; import { useTreeStateStore } from '../store/treeStateStore'; import { useAttachedFile } from './useAttachedFile'; -import { useError } from './useError'; import { useDatabase } from './useDatabase'; import { useIndexedDb } from './useIndexedDb'; +import { useFirebaseConnection } from './useFirebaseConnection'; import { useDialogStore, useInputDialogStore } from '../store/dialogStore'; import { Capacitor } from '@capacitor/core'; import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; @@ -42,7 +42,6 @@ export const useTreeManagement = () => { const showDialog = useDialogStore((state) => state.showDialog); const showInputDialog = useInputDialogStore((state) => state.showDialog); - const { handleError } = useError(); const { saveTimeStampDb, loadTreesListFromDb, loadAllTreesDataFromDb, @@ -58,16 +57,17 @@ export const useTreeManagement = () => { deleteTreeIdb } = useIndexedDb(); const { deleteFile } = useAttachedFile(); + const isConnected = useFirebaseConnection(); // ターゲットIDのitems、name、membersをDBからロードする --------------------------------------------------------------------------- const loadCurrentTreeData = async (targetTree: UniqueIdentifier) => { - if (!isLoading) setIsLoading(true); + if (!uid || !isConnected) { + return; + } try { - if (!uid) { - throw new Error('ユーザーがログインしていません。'); - } + if (!isLoading) setIsLoading(true); const treeData = await loadCurrentTreeDataFromIdb(targetTree); if (treeData && treeData.name && treeData.membersV2 && treeData.items) { setCurrentTreeName(treeData.name); @@ -82,15 +82,16 @@ export const useTreeManagement = () => { setIsLoading(false); } catch (error) { setIsLoading(false); - handleError('ツリーのデータの取得に失敗しました。\n\n' + error); + await showDialog('ツリーのデータの取得に失敗しました。\n\n' + error, 'Error'); } }; //ツリーを削除する関数 --------------------------------------------------------------------------- const deleteTree = async (targetTree: UniqueIdentifier) => { - if (!uid) { + if (!uid || !isConnected) { return; } + // ツリーを削除する前に現在のツリー内の子要素を含むすべてのattachedFileを再帰的に削除 const deleteAttachedFiles = async () => { const deleteAttachedFilesRecursively = async (items: TreeItem[]) => { @@ -109,8 +110,6 @@ export const useTreeManagement = () => { }; await deleteAttachedFiles(); - - try { await deleteTreeIdb(targetTree); await deleteTreeFromDb(targetTree); @@ -164,7 +163,7 @@ export const useTreeManagement = () => { const treeId: string = result.data as string; // result.dataをstring型としてキャスト return treeId; } catch (error) { - showDialog('メンバーのメールアドレスの取得に失敗しました。\n\n' + error, 'Error'); + await showDialog('ツリーの作成に失敗しました。\n\n' + error, 'Error'); return []; // エラーが発生した場合は空の配列を返す } }, @@ -176,7 +175,7 @@ export const useTreeManagement = () => { if (isOffline) { await showDialog('オフラインモードでは新しいツリーを作成できません。', 'Information'); } - if (!uid) { + if (!uid || !isConnected) { return Promise.reject(); } @@ -264,7 +263,7 @@ export const useTreeManagement = () => { // ファイルを読み込んでツリーの状態を復元する --------------------------------------------------------------------------- const handleFileUpload = async (file: File) => { - if (!uid) { + if (!uid || !isConnected) { return Promise.reject(); } if (!file) { @@ -308,7 +307,7 @@ export const useTreeManagement = () => { // 本編 const handleLoadedContent = async (data: string | null) => { - if (!uid) { + if (!uid || !isConnected) { return Promise.reject(); } const treesList = useTreeStateStore.getState().treesList; @@ -470,7 +469,7 @@ export const useTreeManagement = () => { }); return result.uri; } catch (e) { - console.error('Error saving file:', e); + await showDialog('ファイルの保存に失敗しました。\n\n' + e, 'Error'); return ''; } } else { @@ -484,7 +483,7 @@ export const useTreeManagement = () => { // すべてのツリーをJSONファイルとしてダウンロードする -------------------------------------------------------------------------- const handleDownloadAllTrees = async (isSilent: boolean = false) => { - if (!uid && !treesList) { + if (!uid || !isConnected || !treesList) { return Promise.reject(''); } try { @@ -540,6 +539,9 @@ export const useTreeManagement = () => { // ツリー名の変更 --------------------------------------------------------------------------- const handleTreeNameSubmit = async (editedTreeName: string) => { + if (!uid || !isConnected) { + return; + } if (editedTreeName !== null && editedTreeName !== '' && editedTreeName !== currentTreeName) { setCurrentTreeName(editedTreeName); if (isOffline) { @@ -564,7 +566,7 @@ export const useTreeManagement = () => { // メンバーの追加 --------------------------------------------------------------------------- const handleAddUserToTree = async () => { - if (!currentTree) return Promise.resolve(); + if (!currentTree || !uid || !isConnected) return Promise.resolve(); const email = await showInputDialog( '追加する編集メンバーのメールアドレスを入力してください。共有メンバーはこのアプリにユーザー登録されている必要があります。', 'Add Member', @@ -598,7 +600,7 @@ export const useTreeManagement = () => { // メンバーの削除 --------------------------------------------------------------------------- const handleDeleteUserFromTree = async (rescievedUid: string, rescievedEmail: string) => { - if (!currentTree) return Promise.resolve(); + if (!currentTree || !uid || !isConnected) return Promise.resolve(); let result; if (currentTreeMembers && currentTreeMembers.length === 1) { await showDialog('最後のメンバーを削除することはできません。', 'Information');