-
Notifications
You must be signed in to change notification settings - Fork 860
Fix theme persistence in exports and page reloads #235
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
{ | ||
"cSpell.words": [ | ||
"drawnix" | ||
] | ||
} | ||
"cSpell.words": ["drawnix"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,17 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { initializeData } from './initialize-data'; | ||
import { Drawnix } from '@drawnix/drawnix'; | ||
import { PlaitBoard, PlaitElement, PlaitTheme, Viewport } from '@plait/core'; | ||
import { | ||
PlaitBoard, | ||
PlaitElement, | ||
PlaitTheme, | ||
Viewport, | ||
ThemeColorMode, | ||
BoardTransforms, | ||
} from '@plait/core'; | ||
import { BoardChangeData } from '@plait-board/react-board'; | ||
import localforage from 'localforage'; | ||
import { loadThemeFromStorage, saveThemeToStorage } from '@drawnix/drawnix'; | ||
|
||
// 1个月后移出删除兼容 | ||
const OLD_DRAWNIX_LOCAL_DATA_KEY = 'drawnix-local-data'; | ||
|
@@ -23,20 +32,42 @@ export function App() { | |
|
||
useEffect(() => { | ||
const loadData = async () => { | ||
// Load saved theme from localStorage | ||
const savedTheme = loadThemeFromStorage(); | ||
|
||
const storedData = await localforage.getItem(MAIN_BOARD_CONTENT_KEY); | ||
if (storedData) { | ||
setValue(storedData as any); | ||
const data = storedData as any; | ||
// Always use theme from localStorage, not from stored data | ||
setValue({ | ||
children: data.children, | ||
viewport: data.viewport, | ||
theme: { themeColorMode: savedTheme || ThemeColorMode.default }, | ||
}); | ||
return; | ||
} | ||
const localData = localStorage.getItem(OLD_DRAWNIX_LOCAL_DATA_KEY); | ||
if (localData) { | ||
const parsedData = JSON.parse(localData); | ||
setValue(parsedData); | ||
await localforage.setItem(MAIN_BOARD_CONTENT_KEY, parsedData); | ||
// Use theme from localStorage, not from old data | ||
setValue({ | ||
children: parsedData.children, | ||
viewport: parsedData.viewport, | ||
theme: { themeColorMode: savedTheme || ThemeColorMode.default }, | ||
}); | ||
// Save only children and viewport to new storage | ||
await localforage.setItem(MAIN_BOARD_CONTENT_KEY, { | ||
children: parsedData.children, | ||
viewport: parsedData.viewport, | ||
}); | ||
localStorage.removeItem(OLD_DRAWNIX_LOCAL_DATA_KEY); | ||
return; | ||
} | ||
setValue({ children: initializeData }); | ||
// For new users, use saved theme or default | ||
setValue({ | ||
children: initializeData, | ||
theme: { themeColorMode: savedTheme || ThemeColorMode.default }, | ||
}); | ||
}; | ||
|
||
loadData(); | ||
|
@@ -46,11 +77,37 @@ export function App() { | |
value={value.children} | ||
viewport={value.viewport} | ||
theme={value.theme} | ||
onChange={(value) => { | ||
localforage.setItem(MAIN_BOARD_CONTENT_KEY, value); | ||
//@ts-ignore | ||
onChange={(boardData: BoardChangeData) => { | ||
// Save theme to localStorage when it changes | ||
if (boardData.theme?.themeColorMode) { | ||
saveThemeToStorage(boardData.theme.themeColorMode); | ||
} | ||
|
||
// Don't save theme to localforage - only save content and viewport | ||
const newValue = { | ||
children: boardData.children, | ||
viewport: boardData.viewport, | ||
theme: boardData.theme, // Keep for state but don't persist | ||
}; | ||
setValue(newValue); | ||
|
||
// Only save children and viewport to localforage, not theme | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am curious that why not save theme to localforage and save it to localStorage. I think is fine that save viewport, children, and theme to one place. |
||
localforage.setItem(MAIN_BOARD_CONTENT_KEY, { | ||
children: boardData.children, | ||
viewport: boardData.viewport, | ||
}); | ||
}} | ||
afterInit={(board) => { | ||
console.log('board initialized'); | ||
|
||
// Ensure the saved theme is applied after board initialization | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the theme could be initialized correctly, whether the logic maybe is unnecessary. |
||
const savedTheme = loadThemeFromStorage(); | ||
if (savedTheme && board.theme.themeColorMode !== savedTheme) { | ||
console.log('Applying saved theme after board init:', savedTheme); | ||
BoardTransforms.updateThemeColor(board, savedTheme); | ||
} | ||
|
||
console.log( | ||
`add __drawnix__web__debug_log to window, so you can call add log anywhere, like: window.__drawnix__web__console('some thing')` | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
import { PlaitElement, Viewport } from '@plait/core'; | ||
import { PlaitElement, PlaitTheme, Viewport } from '@plait/core'; | ||
|
||
export interface DrawnixExportedData { | ||
type: DrawnixExportedType.drawnix; | ||
version: number; | ||
source: 'web'; | ||
elements: PlaitElement[]; | ||
viewport: Viewport; | ||
theme?: PlaitTheme; | ||
} | ||
|
||
export enum DrawnixExportedType { | ||
drawnix = 'drawnix' | ||
} | ||
drawnix = 'drawnix', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,79 @@ | ||
import { getSelectedElements, PlaitBoard } from '@plait/core'; | ||
import { getSelectedElements, PlaitBoard, ThemeColorMode } from '@plait/core'; | ||
import { base64ToBlob, boardToImage, download } from './common'; | ||
import { fileOpen } from '../data/filesystem'; | ||
import { IMAGE_MIME_TYPES } from '../constants'; | ||
import { insertImage } from '../data/image'; | ||
|
||
// Get the actual background color from the board container | ||
const getThemeBackgroundColor = (board: PlaitBoard): string => { | ||
try { | ||
const boardContainer = PlaitBoard.getBoardContainer(board); | ||
|
||
// Check multiple elements in the hierarchy for background color | ||
const elementsToCheck = [ | ||
boardContainer, | ||
boardContainer.parentElement, | ||
boardContainer.closest('.drawnix'), | ||
document.body, | ||
].filter(Boolean); | ||
|
||
for (const element of elementsToCheck) { | ||
if (element) { | ||
const computedStyle = window.getComputedStyle(element as Element); | ||
const backgroundColor = computedStyle.backgroundColor; | ||
|
||
// If we find a non-transparent background color, use it | ||
if ( | ||
backgroundColor && | ||
backgroundColor !== 'transparent' && | ||
backgroundColor !== 'rgba(0, 0, 0, 0)' && | ||
backgroundColor !== 'initial' && | ||
backgroundColor !== 'inherit' | ||
) { | ||
return backgroundColor; | ||
} | ||
} | ||
} | ||
|
||
// If no background color found, use fallback | ||
return getThemeBackgroundColorFallback(board.theme.themeColorMode); | ||
} catch (error) { | ||
console.warn( | ||
'Could not get background color from DOM, using fallback', | ||
error | ||
); | ||
return getThemeBackgroundColorFallback(board.theme.themeColorMode); | ||
} | ||
}; | ||
|
||
// Fallback theme background colors mapping | ||
const getThemeBackgroundColorFallback = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can get the active themeColors data through You can encapsulate e method exactly to get the background color of the drawing board in this way. |
||
themeColorMode: ThemeColorMode | ||
): string => { | ||
switch (themeColorMode) { | ||
case ThemeColorMode.dark: | ||
return '#121212'; // Dark theme background (darker) | ||
case ThemeColorMode.starry: | ||
return '#0a0a1a'; // Starry theme background (very dark blue/purple) | ||
case ThemeColorMode.retro: | ||
return '#f4f1e8'; // Retro theme background (warm cream) | ||
case ThemeColorMode.colorful: | ||
return '#ffffff'; // Colorful theme background (pure white) | ||
case ThemeColorMode.soft: | ||
return '#f8f9fa'; // Soft theme background (very light gray) | ||
case ThemeColorMode.default: | ||
default: | ||
return '#ffffff'; // Default theme background (white) | ||
} | ||
}; | ||
|
||
export const saveAsImage = (board: PlaitBoard, isTransparent: boolean) => { | ||
const selectedElements = getSelectedElements(board); | ||
const themeBackgroundColor = getThemeBackgroundColor(board); | ||
|
||
boardToImage(board, { | ||
elements: selectedElements.length > 0 ? selectedElements : undefined, | ||
fillStyle: isTransparent ? 'transparent' : 'white', | ||
fillStyle: isTransparent ? 'transparent' : themeBackgroundColor, | ||
}).then((image) => { | ||
if (image) { | ||
const ext = isTransparent ? 'png' : 'jpg'; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { ThemeColorMode } from '@plait/core'; | ||
|
||
const THEME_STORAGE_KEY = 'drawnix-theme'; | ||
|
||
export const saveThemeToStorage = (theme: ThemeColorMode): void => { | ||
try { | ||
localStorage.setItem(THEME_STORAGE_KEY, theme); | ||
} catch (error) { | ||
console.warn('Failed to save theme to localStorage:', error); | ||
} | ||
}; | ||
|
||
export const loadThemeFromStorage = (): ThemeColorMode | null => { | ||
try { | ||
const savedTheme = localStorage.getItem(THEME_STORAGE_KEY); | ||
if ( | ||
savedTheme && | ||
Object.values(ThemeColorMode).includes(savedTheme as ThemeColorMode) | ||
) { | ||
return savedTheme as ThemeColorMode; | ||
} | ||
} catch (error) { | ||
console.warn('Failed to load theme from localStorage:', error); | ||
} | ||
return null; | ||
}; | ||
|
||
export const clearThemeFromStorage = (): void => { | ||
try { | ||
localStorage.removeItem(THEME_STORAGE_KEY); | ||
} catch (error) { | ||
console.warn('Failed to clear theme from localStorage:', error); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to remove the feedback logic later, so you can ignore it.