Skip to content

Commit aa18e9f

Browse files
wallpaper optimized for wallpaper and thumbnails
1 parent dbcbfc1 commit aa18e9f

12 files changed

Lines changed: 126 additions & 56 deletions

src/main/utils/images.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { access, readdir, stat } from "node:fs/promises";
22
import { constants } from "node:fs";
33
import path from "node:path";
44
import { pathToFileURL } from "node:url";
5+
import { nativeImage } from "electron";
6+
import { BackgroundImagesInterface } from "@shared/types/setting.types";
57

68
const imageExtensions = new Set<string>([
79
".png",
@@ -19,23 +21,49 @@ export const getImageFilesFromFolder = async ({
1921
}: {
2022
folderPath: string;
2123
limit?: number;
22-
}) => {
24+
}): Promise<BackgroundImagesInterface | null> => {
2325
try {
2426
await access(folderPath, constants.R_OK);
2527

26-
const files =
27-
(await readdir(folderPath, { withFileTypes: true }))
28+
const imageEntries =
29+
(
30+
await readdir(folderPath, {
31+
withFileTypes: true,
32+
})
33+
)
2834
?.filter(
2935
entry =>
3036
entry.isFile() &&
3137
imageExtensions.has(path.extname(entry.name).toLowerCase()),
3238
)
33-
?.slice(0, limit)
34-
?.map(file => getApiBoltFileProtocolBasedPath(folderPath, file.name)) ??
35-
[];
39+
?.slice(0, limit) ?? [];
40+
41+
const thumbnails = (
42+
await Promise.allSettled(
43+
imageEntries.map(entry => {
44+
const fullPath = path.join(folderPath, entry.name);
45+
return nativeImage.createThumbnailFromPath(fullPath, {
46+
width: 300,
47+
height: 300,
48+
});
49+
}),
50+
)
51+
)
52+
.filter(res => res.status === "fulfilled")
53+
.map(res => res.value.toDataURL());
54+
55+
const images =
56+
imageEntries?.map(file =>
57+
getApiBoltFileProtocolBasedPath(folderPath, file.name),
58+
) ?? [];
3659

3760
const folderUrl = path.resolve(folderPath);
38-
return [folderUrl, ...files];
61+
62+
return {
63+
folderUrl,
64+
thumbnails,
65+
images,
66+
};
3967
} catch (error) {
4068
console.error("getImageFilesFromFolder error:", error);
4169
return null;

src/renderer/src/components/app/setting/content/background/images/SettingBackgroundImagePreview.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,44 @@ import { useSettingBackground } from "@/context/setting/background/SettingBackgr
1111
import ImageWithFallback from "@/components/ui/image-with-fallback";
1212

1313
interface Props {
14-
backgroundList: Array<string>;
15-
selectedIndex: number | null;
14+
selectedBackground: string | null;
1615
className?: string;
1716
}
1817

1918
const SettingBackgroundImagePreview = memo(
20-
({ backgroundList, selectedIndex, className = "" }: Props) => {
19+
({ selectedBackground, className = "" }: Props) => {
2120
const {
2221
handleChangeSelectedBackgroundImageIndex,
2322
handleNavigateSelectedBackgroundImageIndex,
2423
} = useSettingBackground();
2524

2625
return (
2726
<AnimatePresence mode="wait">
28-
{selectedIndex !== null && (
27+
{selectedBackground !== null && (
2928
<motion.div
3029
className={cn(
3130
"bg-accent/50 col-span-1 h-90 origin-center overflow-hidden rounded-lg relative flex flex-col p-3",
3231
className,
3332
)}
34-
initial={{ scale: 0.6, opacity: 0, filter: "blur(10px)" }}
35-
animate={{ scale: 1, opacity: 1, filter: "blur(0)" }}
36-
exit={{ scale: 0.8, opacity: 0, filter: "blur(10px)" }}
37-
transition={{ duration: 0.4, ease: "easeInOut" }}
33+
initial={{
34+
scale: 0.6,
35+
opacity: 0,
36+
filter: "blur(10px)",
37+
}}
38+
animate={{
39+
scale: 1,
40+
opacity: 1,
41+
filter: "blur(0)",
42+
}}
43+
exit={{
44+
scale: 0.8,
45+
opacity: 0,
46+
filter: "blur(10px)",
47+
}}
48+
transition={{
49+
duration: 0.4,
50+
ease: "easeInOut",
51+
}}
3852
>
3953
{/* Close Button */}
4054
<Button
@@ -57,8 +71,8 @@ const SettingBackgroundImagePreview = memo(
5771
<AnimatePresence mode="wait">
5872
<ImageWithFallback
5973
isAnimated
60-
key={selectedIndex}
61-
src={backgroundList[selectedIndex]}
74+
key={selectedBackground}
75+
src={selectedBackground}
6276
alt="background-preview"
6377
className="max-w-full max-h-full w-full h-full object-contain rounded-lg shadow-xl blur-sm"
6478
style={{

src/renderer/src/components/app/setting/content/background/images/SettingBackgroundImages.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const SettingBackgroundContent = memo(() => {
2424
settingType,
2525
senitizedValue,
2626
folderPath,
27+
thumbnails,
2728
isHideMoreData,
2829
handleChange,
2930
handleChangeSettingType,
@@ -52,7 +53,7 @@ const SettingBackgroundContent = memo(() => {
5253
}
5354
activeTab={activeTab}
5455
folderPath={folderPath}
55-
senitizedValue={senitizedValue as Array<string>}
56+
thumbnails={thumbnails}
5657
settingType={settingType}
5758
/>
5859
{settingType === "custom" && (

src/renderer/src/components/app/setting/content/background/images/SettingBackgroundImagesContent.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ const SettingBackgroundImagesContent = memo(
4242
/>
4343
{/* RIGHT PREVIEW */}
4444
<SettingBackgroundImagePreview
45-
selectedIndex={selectedBackgroundImageIndex}
46-
backgroundList={backgroundList}
45+
selectedBackground={
46+
typeof selectedBackgroundImageIndex === "number"
47+
? backgroundList[selectedBackgroundImageIndex]
48+
: null
49+
}
4750
className={cn({
4851
"h-80": height === "short",
4952
})}

src/renderer/src/components/app/setting/content/background/images/SettingBackgroundImagesDetails.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import { AnimatePresence, motion } from "motion/react";
77

88
interface Props {
99
folderPath: string | null;
10-
senitizedValue: Array<string>;
10+
thumbnails: Array<string>;
1111
activeTab: TSettingTab;
1212
settingType: SettingType;
1313
isOpen: boolean;
1414
}
1515

1616
const SettingBackgroundImagesDetails = ({
1717
folderPath,
18-
senitizedValue,
18+
thumbnails,
1919
activeTab,
2020
settingType,
2121
isOpen,
@@ -39,7 +39,7 @@ const SettingBackgroundImagesDetails = ({
3939
<SettingItemHorizontalLayout className="flex-col justify-center items-center gap-4">
4040
<SettingBackgroundImagesFolderPath path={folderPath} />
4141
<SettingBackgroundImagesContent
42-
backgroundList={senitizedValue}
42+
backgroundList={thumbnails}
4343
/* if images showing from project and global then short height */
4444
height={
4545
activeTab === "project" && settingType === "global"

src/renderer/src/context/setting/background/SettingBackgroundProvider.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface SettingBackgroundContext {
1414
) => void;
1515
settingType: SettingType;
1616
senitizedValue: string | Array<string> | undefined;
17+
thumbnails: Array<string>;
1718
folderPath: string | null;
1819
handleChange: (
1920
method?: UpdateBackgroundImagePayloadMethodType | undefined,
@@ -49,6 +50,7 @@ const SettingBackgroundProvider = ({
4950
const {
5051
settingType,
5152
senitizedValue,
53+
thumbnails,
5254
folderPath,
5355
handleChange,
5456
isHideMoreData,
@@ -69,11 +71,11 @@ const SettingBackgroundProvider = ({
6971
? 0
7072
: Math.abs(
7173
direction === "left"
72-
? (senitizedValue?.length ?? 0) + prev - 1
74+
? (thumbnails?.length ?? 0) + prev - 1
7375
: prev + 1,
74-
) % (senitizedValue?.length ?? 0),
76+
) % (thumbnails?.length ?? 0),
7577
),
76-
[senitizedValue?.length],
78+
[thumbnails?.length],
7779
);
7880

7981
return (
@@ -84,6 +86,7 @@ const SettingBackgroundProvider = ({
8486
handleNavigateSelectedBackgroundImageIndex,
8587
settingType,
8688
senitizedValue,
89+
thumbnails,
8790
folderPath,
8891
handleChange,
8992
isHideMoreData,

src/renderer/src/hooks/setting/use-check-applying-background.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { selectSettingBackground } from "@/context/redux/setting/selectors/setti
55

66
const useCheckApplyingBackground = (): {
77
backgroundImages: Array<string> | string;
8+
backgroundThumbnails: Array<string> | null;
89
backgroundOpacity: number;
910
backgroundBlur: number;
1011
slideInterval: number;
@@ -44,10 +45,13 @@ const useCheckApplyingBackground = (): {
4445
: global.slideInterval) ?? DEFAULT_SETTINGS.slideInterval!;
4546
if (slideInterval < 0) slideInterval = DEFAULT_SETTINGS.slideInterval!;
4647

47-
if (!backgroundImages?.length) return null;
48+
if (typeof backgroundImages !== "object" || !backgroundImages.images?.length)
49+
return null;
4850

4951
return {
50-
backgroundImages: backgroundImages.slice(1, maxNumberOfImages),
52+
backgroundImages: backgroundImages?.images.slice(1, maxNumberOfImages),
53+
backgroundThumbnails:
54+
backgroundImages?.thumbnails?.slice(1, maxNumberOfImages) ?? null,
5155
backgroundOpacity,
5256
backgroundBlur,
5357
maxNumberOfImages,

src/renderer/src/hooks/setting/use-check-applying-setting-scope-background-images.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@ import {
44
selectBackgroundImagesLocal,
55
} from "@/context/redux/setting/selectors/setting";
66
import { useSetting } from "@/context/setting/SettingProvider";
7+
import { SettingsInterface } from "@shared/types/setting.types";
78

89
const useCheckApplyingSettingScopeBackgroundImages = () => {
910
const { activeTab } = useSetting();
1011
const backgroundImagesGlobal = useAppSelector(selectBackgroundImagesGlobal);
1112
const backgroundImagesLocal = useAppSelector(selectBackgroundImagesLocal);
1213

13-
let backgroundImages = (
14+
const backgroundImages = (
1415
activeTab === "project"
1516
? (backgroundImagesLocal ?? backgroundImagesGlobal)
1617
: backgroundImagesGlobal
17-
) as Array<string> | string;
18+
) as SettingsInterface["backgroundImages"];
1819

19-
if (backgroundImages === "default" || !backgroundImages)
20-
backgroundImages = [];
20+
if (
21+
backgroundImages &&
22+
typeof backgroundImages === "object" &&
23+
"thumbnails" in backgroundImages
24+
)
25+
return backgroundImages["thumbnails"];
26+
27+
if (backgroundImages === "default" || !backgroundImages) return [];
2128

2229
return backgroundImages;
2330
};

src/renderer/src/hooks/setting/use-check-background-setting-images.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {
66
import { useSetting } from "@/context/setting/SettingProvider";
77
import { DEFAULT_SETTINGS } from "@/constant/settings.constant";
88
import { senitizeValue } from "@/utils/settings.utils";
9-
import type { SettingBackgroundImagesValueType } from "@/hooks/setting/use-global-local-bg-images";
109
import useCheckApplyingSettingScopeBackgroundImages from "@/hooks/setting/use-check-applying-setting-scope-background-images";
1110
import useGlobalLocalBgImages from "@/hooks/setting/use-global-local-bg-images";
11+
import { SettingsInterface } from "@shared/types/setting.types";
1212

1313
const useCheckBackgroundSettingImages = () => {
1414
const { activeTab } = useSetting();
@@ -23,24 +23,32 @@ const useCheckBackgroundSettingImages = () => {
2323
activeTab,
2424
});
2525

26-
const senitizedValue = senitizeValue(
27-
value,
28-
DEFAULT_SETTINGS.backgroundImages,
29-
) as SettingBackgroundImagesValueType;
26+
const senitizedValue = senitizeValue<
27+
Exclude<SettingsInterface["backgroundImages"], "default">
28+
>(value, DEFAULT_SETTINGS.backgroundImages);
29+
30+
const folderPath =
31+
(typeof senitizedValue === "object" ? senitizedValue?.folderUrl : null) ??
32+
null;
33+
34+
const images =
35+
(typeof senitizedValue === "object" ? senitizedValue?.images : []) ?? [];
3036

31-
const folderPath = Array.isArray(senitizedValue) ? senitizedValue[0] : null;
37+
const thumbnails =
38+
(typeof senitizedValue === "object" ? senitizedValue?.thumbnails : []) ??
39+
[];
3240

3341
const isHideMoreData =
3442
!Array.isArray(backgroundImages) || !backgroundImages.length;
3543

3644
return {
3745
value,
46+
images,
47+
thumbnails,
3848
handleChange,
3949
handleChangeSettingType,
4050
settingType,
41-
senitizedValue: Array.isArray(senitizedValue)
42-
? senitizedValue?.slice(1)
43-
: [],
51+
senitizedValue: Array.isArray(senitizedValue) ? senitizedValue : [],
4452
folderPath,
4553
activeTab,
4654
isHideMoreData,

src/renderer/src/hooks/setting/use-global-local-bg-images.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useCallback, useEffect, useMemo, useState } from "react";
22
import type {
3+
BackgroundImagesInterface,
4+
SettingsInterface,
35
SettingType,
46
UpdateBackgroundImagePayloadMethodType,
57
} from "@shared/types/setting.types";
@@ -12,23 +14,17 @@ const checkIsDefaultType = (value: unknown) =>
1214
[-1, "default"].includes(value as number | string);
1315

1416
interface UseGlobalLocalBgImagesProps {
15-
globalSetting: SettingBackgroundImagesValueType;
16-
localSetting: SettingBackgroundImagesValueType;
17+
globalSetting: SettingsInterface["backgroundImages"];
18+
localSetting: SettingsInterface["backgroundImages"];
1719
activeTab: TSettingTab;
1820
}
1921

20-
export type SettingBackgroundImagesValueType =
21-
| Array<string>
22-
| string
23-
| null
24-
| undefined;
25-
2622
const useGlobalLocalBgImages = ({
2723
globalSetting,
2824
localSetting,
2925
activeTab,
3026
}: UseGlobalLocalBgImagesProps): {
31-
value: SettingBackgroundImagesValueType;
27+
value: SettingsInterface["backgroundImages"];
3228
handleChange: (method?: UpdateBackgroundImagePayloadMethodType) => void;
3329
handleChangeSettingType: (value: SettingType) => void;
3430
settingType: SettingType;
@@ -67,7 +63,7 @@ const useGlobalLocalBgImages = ({
6763
if (settingType === "default")
6864
return localSetting ?? globalSetting ?? DEFAULT_SETTINGS.backgroundImages;
6965

70-
return localSetting ?? [];
66+
return localSetting;
7167
}, [activeTab, globalSetting, localSetting, settingType]);
7268

7369
const handleChange = useCallback(

0 commit comments

Comments
 (0)