storage.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { useState, useEffect } from 'react';
  2. // Safe localStorage hook that handles quota errors
  3. export function useLocalStorage<T>(
  4. key: string,
  5. initialValue: T,
  6. options?: {
  7. excludeLargeData?: boolean;
  8. maxSize?: number; // in bytes
  9. }
  10. ) {
  11. const { excludeLargeData = true, maxSize = 1024 * 1024 } = options || {}; // 1MB default
  12. // Get stored value
  13. const getStoredValue = (): T => {
  14. if (typeof window === 'undefined') {
  15. return initialValue;
  16. }
  17. try {
  18. const item = window.localStorage.getItem(key);
  19. if (item === null) {
  20. return initialValue;
  21. }
  22. return JSON.parse(item);
  23. } catch (error) {
  24. console.warn(`Error reading localStorage key "${key}":`, error);
  25. // If there's an error, clear the key and return initial value
  26. try {
  27. window.localStorage.removeItem(key);
  28. } catch (clearError) {
  29. console.warn(`Error clearing localStorage key "${key}":`, clearError);
  30. }
  31. return initialValue;
  32. }
  33. };
  34. const [storedValue, setStoredValue] = useState<T>(getStoredValue);
  35. // Set value to localStorage
  36. const setValue = (value: T | ((val: T) => T)) => {
  37. try {
  38. const valueToStore = value instanceof Function ? value(storedValue) : value;
  39. // Check if we should exclude large data
  40. if (excludeLargeData) {
  41. const serialized = JSON.stringify(valueToStore);
  42. // Check for large base64 images or data URLs
  43. if (serialized.includes('data:image/') || serialized.length > maxSize) {
  44. console.warn(`Skipping localStorage save for "${key}" - data too large or contains images`);
  45. setStoredValue(valueToStore);
  46. return;
  47. }
  48. }
  49. setStoredValue(valueToStore);
  50. if (typeof window !== 'undefined') {
  51. window.localStorage.setItem(key, JSON.stringify(valueToStore));
  52. }
  53. } catch (error) {
  54. console.warn(`Error saving localStorage key "${key}":`, error);
  55. // Still update the state even if localStorage fails
  56. const valueToStore = value instanceof Function ? value(storedValue) : value;
  57. setStoredValue(valueToStore);
  58. // Try to clear some space if it's a quota error
  59. if (error instanceof Error && error.name === 'QuotaExceededError') {
  60. try {
  61. // Clear some non-essential keys
  62. const keysToClear = ['img2img-form-data', 'text2img-form-data', 'inpainting-form-data'];
  63. keysToClear.forEach(k => {
  64. if (k !== key) {
  65. window.localStorage.removeItem(k);
  66. }
  67. });
  68. } catch (clearError) {
  69. console.warn('Error clearing localStorage space:', clearError);
  70. }
  71. }
  72. }
  73. };
  74. return [storedValue, setValue] as const;
  75. }
  76. // Session storage alternative for large data
  77. export function useSessionStorage<T>(key: string, initialValue: T) {
  78. const [storedValue, setStoredValue] = useState<T>(() => {
  79. if (typeof window === 'undefined') {
  80. return initialValue;
  81. }
  82. try {
  83. const item = window.sessionStorage.getItem(key);
  84. return item ? JSON.parse(item) : initialValue;
  85. } catch (error) {
  86. console.warn(`Error reading sessionStorage key "${key}":`, error);
  87. return initialValue;
  88. }
  89. });
  90. const setValue = (value: T | ((val: T) => T)) => {
  91. try {
  92. const valueToStore = value instanceof Function ? value(storedValue) : value;
  93. setStoredValue(valueToStore);
  94. if (typeof window !== 'undefined') {
  95. window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
  96. }
  97. } catch (error) {
  98. console.warn(`Error saving sessionStorage key "${key}":`, error);
  99. // Still update the state even if sessionStorage fails
  100. const valueToStore = value instanceof Function ? value(storedValue) : value;
  101. setStoredValue(valueToStore);
  102. }
  103. };
  104. return [storedValue, setValue] as const;
  105. }
  106. // Memory-only storage for large data
  107. export function useMemoryStorage<T>(initialValue: T) {
  108. const [storedValue, setStoredValue] = useState<T>(initialValue);
  109. const setValue = (value: T | ((val: T) => T)) => {
  110. const valueToStore = value instanceof Function ? value(storedValue) : value;
  111. setStoredValue(valueToStore);
  112. };
  113. return [storedValue, setValue] as const;
  114. }