@@ -2,35 +2,125 @@ import { useMemo } from "react";
2
2
import { computed } from "nanostores" ;
3
3
import { useStore } from "@nanostores/react" ;
4
4
import warnOnce from "warn-once" ;
5
- import type { Asset } from "@webstudio-is/sdk" ;
5
+ import invariant from "tiny-invariant" ;
6
+ import type { Asset , Page } from "@webstudio-is/sdk" ;
6
7
import type { AssetType } from "@webstudio-is/asset-uploader" ;
7
8
import { Box , toast , css , theme } from "@webstudio-is/design-system" ;
8
9
import { sanitizeS3Key } from "@webstudio-is/asset-uploader" ;
10
+ import { Image , wsImageLoader } from "@webstudio-is/image" ;
11
+ import type { ImageValue , StyleValue } from "@webstudio-is/css-engine" ;
9
12
import { restAssetsUploadPath , restAssetsPath } from "~/shared/router-utils" ;
10
- import type {
11
- AssetContainer ,
12
- UploadedAssetContainer ,
13
- UploadingAssetContainer ,
14
- } from "./types" ;
13
+ import { fetch } from "~/shared/fetch.client" ;
15
14
import type { ActionData } from "~/builder/shared/assets" ;
16
15
import {
17
16
$assets ,
18
17
$authToken ,
18
+ $pages ,
19
19
$project ,
20
+ $props ,
21
+ $styles ,
20
22
$uploadingFilesDataStore ,
21
23
type UploadingFileData ,
22
24
} from "~/shared/nano-states" ;
23
25
import { serverSyncStore } from "~/shared/sync" ;
26
+ import type {
27
+ AssetContainer ,
28
+ UploadedAssetContainer ,
29
+ UploadingAssetContainer ,
30
+ } from "./types" ;
24
31
import {
25
32
getFileName ,
26
33
getMimeType ,
27
34
getSha256Hash ,
28
35
getSha256HashOfFile ,
29
36
uploadingFileDataToAsset ,
30
37
} from "./asset-utils" ;
31
- import { Image , wsImageLoader } from "@webstudio-is/image" ;
32
- import invariant from "tiny-invariant" ;
33
- import { fetch } from "~/shared/fetch.client" ;
38
+ import { mapGetOrInsert } from "~/shared/shim" ;
39
+
40
+ export type AssetUsage =
41
+ | { type : "favicon" }
42
+ | { type : "socialImage" ; pageId : Page [ "id" ] }
43
+ | { type : "marketplaceThumbnail" ; pageId : Page [ "id" ] }
44
+ | { type : "prop" ; propId : string }
45
+ | { type : "style" ; styleDeclKey : string } ;
46
+
47
+ const traverseStyleValue = (
48
+ styleValue : StyleValue ,
49
+ callback : ( value : ImageValue ) => void
50
+ ) => {
51
+ if ( styleValue . type === "image" ) {
52
+ callback ( styleValue ) ;
53
+ }
54
+ if ( styleValue . type === "tuple" ) {
55
+ for ( const item of styleValue . value ) {
56
+ traverseStyleValue ( item , callback ) ;
57
+ }
58
+ }
59
+ if ( styleValue . type === "layers" ) {
60
+ for ( const item of styleValue . value ) {
61
+ traverseStyleValue ( item , callback ) ;
62
+ }
63
+ }
64
+ } ;
65
+
66
+ export const $usagesByAssetId = computed (
67
+ [ $pages , $props , $styles ] ,
68
+ ( pages , props , styles ) => {
69
+ const usagesByAsset = new Map < Asset [ "id" ] , AssetUsage [ ] > ( ) ;
70
+ if ( pages ?. meta ?. faviconAssetId ) {
71
+ const usages = mapGetOrInsert (
72
+ usagesByAsset ,
73
+ pages . meta . faviconAssetId ,
74
+ [ ]
75
+ ) ;
76
+ usages . push ( { type : "favicon" } ) ;
77
+ }
78
+ if ( pages ) {
79
+ for ( const page of [ pages . homePage , ...pages . pages ] ) {
80
+ if ( page . meta . socialImageAssetId ) {
81
+ const usages = mapGetOrInsert (
82
+ usagesByAsset ,
83
+ page . meta . socialImageAssetId ,
84
+ [ ]
85
+ ) ;
86
+ usages . push ( { type : "socialImage" , pageId : page . id } ) ;
87
+ }
88
+ if ( page . marketplace ?. thumbnailAssetId ) {
89
+ const usages = mapGetOrInsert (
90
+ usagesByAsset ,
91
+ page . marketplace . thumbnailAssetId ,
92
+ [ ]
93
+ ) ;
94
+ usages . push ( { type : "marketplaceThumbnail" , pageId : page . id } ) ;
95
+ }
96
+ }
97
+ }
98
+ for ( const prop of props . values ( ) ) {
99
+ if (
100
+ prop . type === "asset" &&
101
+ // ignore width and height properties which are specific to size
102
+ prop . name !== "width" &&
103
+ prop . name !== "height"
104
+ ) {
105
+ const usages = mapGetOrInsert ( usagesByAsset , prop . value , [ ] ) ;
106
+ usages . push ( { type : "prop" , propId : prop . id } ) ;
107
+ }
108
+ }
109
+ for ( const [ styleDeclKey , styleDecl ] of styles ) {
110
+ traverseStyleValue ( styleDecl . value , ( imageValue ) => {
111
+ if ( imageValue . value . type === "asset" ) {
112
+ const usages = mapGetOrInsert (
113
+ usagesByAsset ,
114
+ imageValue . value . value ,
115
+ [ ]
116
+ ) ;
117
+ usages . push ( { type : "style" , styleDeclKey } ) ;
118
+ }
119
+ } ) ;
120
+ }
121
+ return usagesByAsset ;
122
+ }
123
+ ) ;
34
124
35
125
export const deleteAssets = ( assetIds : Asset [ "id" ] [ ] ) => {
36
126
serverSyncStore . createTransaction ( [ $assets ] , ( assets ) => {
0 commit comments