sanity-plugin-shopify-assets 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.esm.js +2654 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2671 -0
- package/dist/index.js.map +1 -0
- package/package.json +101 -0
- package/sanity.json +8 -0
- package/src/components/AssetDiff.tsx +58 -0
- package/src/components/AssetPreview.tsx +82 -0
- package/src/components/DialogHeader.tsx +45 -0
- package/src/components/File.styled.tsx +66 -0
- package/src/components/File.tsx +56 -0
- package/src/components/ShopifyAssetInput.styled.tsx +13 -0
- package/src/components/ShopifyAssetInput.tsx +84 -0
- package/src/components/ShopifyAssetPicker.tsx +202 -0
- package/src/components/ShopifyIcon.tsx +22 -0
- package/src/components/VideoPlayer.tsx +50 -0
- package/src/datastores/shopify.ts +66 -0
- package/src/index.ts +31 -0
- package/src/schema/shopifyAssetSchema.ts +118 -0
- package/src/types.ts +54 -0
- package/src/utils/helpers.ts +1 -0
- package/v2-incompatible.js +11 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {Asset, PageInfo, ShopifyAPIResponse, ShopifyFile} from '../types'
|
|
2
|
+
import {BehaviorSubject, Subscription} from 'rxjs'
|
|
3
|
+
import {Card, Dialog, Flex, Inline, Spinner, Stack, Text, TextInput} from '@sanity/ui'
|
|
4
|
+
import {PatchEvent, set, useProjectId, ObjectInputProps} from 'sanity'
|
|
5
|
+
import React, {useCallback, useEffect, useMemo, useState} from 'react'
|
|
6
|
+
|
|
7
|
+
import DialogHeader from './DialogHeader'
|
|
8
|
+
import {ErrorOutlineIcon} from '@sanity/icons'
|
|
9
|
+
import File from './File'
|
|
10
|
+
import InfiniteScroll from 'react-infinite-scroll-component'
|
|
11
|
+
import PhotoAlbum from 'react-photo-album'
|
|
12
|
+
import {Search} from './ShopifyAssetInput.styled'
|
|
13
|
+
import {search} from '../datastores/shopify'
|
|
14
|
+
|
|
15
|
+
const RESULTS_PER_PAGE = 42
|
|
16
|
+
const PHOTO_SPACING = 2
|
|
17
|
+
const PHOTO_PADDING = 1
|
|
18
|
+
|
|
19
|
+
export interface AssetPickerProps extends ObjectInputProps<Asset> {
|
|
20
|
+
shopifyDomain: string
|
|
21
|
+
isOpen: boolean
|
|
22
|
+
onClose: () => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function ShopifyAssetPicker(props: AssetPickerProps) {
|
|
26
|
+
const {isOpen, onClose, shopifyDomain, onChange, schemaType, value} = props
|
|
27
|
+
const projectId = useProjectId()
|
|
28
|
+
|
|
29
|
+
const [error, setError] = useState('')
|
|
30
|
+
const [query, setQuery] = useState('')
|
|
31
|
+
const [searchResults, setSearchResults] = useState<any[]>([])
|
|
32
|
+
const [pageInfo, setPageInfo] = useState<PageInfo>()
|
|
33
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
34
|
+
|
|
35
|
+
const searchSubject$ = useMemo(() => new BehaviorSubject(''), [])
|
|
36
|
+
const cursorSubject$ = useMemo(() => new BehaviorSubject(''), [])
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!shopifyDomain) setError('Please configure your Shopify domain in the plugin config')
|
|
40
|
+
}, [shopifyDomain])
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const searchSubscription: Subscription = search(
|
|
44
|
+
projectId,
|
|
45
|
+
shopifyDomain,
|
|
46
|
+
searchSubject$,
|
|
47
|
+
cursorSubject$,
|
|
48
|
+
RESULTS_PER_PAGE
|
|
49
|
+
).subscribe({
|
|
50
|
+
next: (results: ShopifyAPIResponse) => {
|
|
51
|
+
setSearchResults((prevResults) => [...prevResults, ...results.assets])
|
|
52
|
+
setPageInfo(results.pageInfo)
|
|
53
|
+
setIsLoading(false)
|
|
54
|
+
},
|
|
55
|
+
error: (err) => {
|
|
56
|
+
setError(
|
|
57
|
+
`${
|
|
58
|
+
err.response.data.message || err.message || 'An error occurred'
|
|
59
|
+
} - check plugin configuration`
|
|
60
|
+
)
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return () => searchSubscription.unsubscribe()
|
|
65
|
+
}, [searchSubject$, cursorSubject$, shopifyDomain, projectId])
|
|
66
|
+
|
|
67
|
+
const handleSearchTermChanged = useCallback(
|
|
68
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
69
|
+
const newQuery = event.currentTarget.value
|
|
70
|
+
setQuery(newQuery)
|
|
71
|
+
setSearchResults([])
|
|
72
|
+
setPageInfo(undefined)
|
|
73
|
+
setIsLoading(true)
|
|
74
|
+
|
|
75
|
+
cursorSubject$.next('')
|
|
76
|
+
searchSubject$.next(newQuery)
|
|
77
|
+
},
|
|
78
|
+
[cursorSubject$, searchSubject$]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
const handleScollerLoadMore = useCallback(() => {
|
|
82
|
+
setIsLoading(true)
|
|
83
|
+
if (pageInfo) cursorSubject$.next(pageInfo.cursor)
|
|
84
|
+
searchSubject$.next(query)
|
|
85
|
+
}, [cursorSubject$, pageInfo, searchSubject$, query])
|
|
86
|
+
|
|
87
|
+
const handleSelect = useCallback(
|
|
88
|
+
(file: Asset) => {
|
|
89
|
+
file._key = value?._key
|
|
90
|
+
file._type = schemaType.name
|
|
91
|
+
onChange(PatchEvent.from([set(file)]))
|
|
92
|
+
onClose()
|
|
93
|
+
},
|
|
94
|
+
[onChange, onClose, schemaType.name, value?._key]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const renderFile = useCallback(
|
|
98
|
+
(fileProps: any) => {
|
|
99
|
+
const {photo, layout} = fileProps
|
|
100
|
+
return (
|
|
101
|
+
<File
|
|
102
|
+
onClick={handleSelect}
|
|
103
|
+
data={photo.data}
|
|
104
|
+
width={layout.width}
|
|
105
|
+
height={layout.height}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
},
|
|
109
|
+
[handleSelect]
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
const handleWidth = useCallback((width: number) => {
|
|
113
|
+
if (width < 300) return 150
|
|
114
|
+
else if (width < 600) return 200
|
|
115
|
+
return 300
|
|
116
|
+
}, [])
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<Dialog
|
|
120
|
+
id="shopify-asset-source"
|
|
121
|
+
header={<DialogHeader title="Shopify Assets" shopifyDomain={shopifyDomain} />}
|
|
122
|
+
onClose={onClose}
|
|
123
|
+
open={isOpen}
|
|
124
|
+
width={4}
|
|
125
|
+
>
|
|
126
|
+
<Stack space={3} padding={4}>
|
|
127
|
+
{error ? (
|
|
128
|
+
<Card overflow="hidden" padding={4} radius={2} shadow={1} tone="critical">
|
|
129
|
+
<Flex align="center" gap={3}>
|
|
130
|
+
<Text size={2}>
|
|
131
|
+
<ErrorOutlineIcon />
|
|
132
|
+
</Text>
|
|
133
|
+
<Inline space={2}>
|
|
134
|
+
<Text size={1}>{error}</Text>
|
|
135
|
+
</Inline>
|
|
136
|
+
</Flex>
|
|
137
|
+
</Card>
|
|
138
|
+
) : (
|
|
139
|
+
<>
|
|
140
|
+
<Card>
|
|
141
|
+
<Search space={3}>
|
|
142
|
+
<Text size={1} weight="semibold">
|
|
143
|
+
Search Shopify for assets
|
|
144
|
+
</Text>
|
|
145
|
+
<TextInput
|
|
146
|
+
label="Search Images"
|
|
147
|
+
placeholder="filename.jpg"
|
|
148
|
+
value={query}
|
|
149
|
+
onChange={handleSearchTermChanged}
|
|
150
|
+
/>
|
|
151
|
+
</Search>
|
|
152
|
+
</Card>
|
|
153
|
+
{!isLoading && searchResults.length === 0 && (
|
|
154
|
+
<Text size={1} muted>
|
|
155
|
+
No results found
|
|
156
|
+
</Text>
|
|
157
|
+
)}
|
|
158
|
+
<InfiniteScroll
|
|
159
|
+
dataLength={searchResults.length} // This is important field to render the next data
|
|
160
|
+
next={handleScollerLoadMore}
|
|
161
|
+
hasMore={pageInfo ? pageInfo?.hasNextPage : true}
|
|
162
|
+
scrollThreshold={0.99}
|
|
163
|
+
height="60vh"
|
|
164
|
+
loader={
|
|
165
|
+
<Flex align="center" justify="center" padding={3}>
|
|
166
|
+
<Spinner muted />
|
|
167
|
+
</Flex>
|
|
168
|
+
}
|
|
169
|
+
endMessage={
|
|
170
|
+
<Flex align="center" justify="center" padding={3}>
|
|
171
|
+
<Text size={1} muted>
|
|
172
|
+
No more results
|
|
173
|
+
</Text>
|
|
174
|
+
</Flex>
|
|
175
|
+
}
|
|
176
|
+
>
|
|
177
|
+
{searchResults && (
|
|
178
|
+
<PhotoAlbum
|
|
179
|
+
layout="rows"
|
|
180
|
+
spacing={PHOTO_SPACING}
|
|
181
|
+
padding={PHOTO_PADDING}
|
|
182
|
+
targetRowHeight={handleWidth}
|
|
183
|
+
photos={searchResults.map((file: ShopifyFile) => ({
|
|
184
|
+
src: file?.preview?.url,
|
|
185
|
+
width: file?.preview?.width || 2048,
|
|
186
|
+
height: file?.preview?.height || 2048,
|
|
187
|
+
key: file.id,
|
|
188
|
+
data: file,
|
|
189
|
+
}))}
|
|
190
|
+
renderPhoto={renderFile}
|
|
191
|
+
componentsProps={{
|
|
192
|
+
containerProps: {style: {marginBottom: `${PHOTO_SPACING}px`}},
|
|
193
|
+
}}
|
|
194
|
+
/>
|
|
195
|
+
)}
|
|
196
|
+
</InfiniteScroll>
|
|
197
|
+
</>
|
|
198
|
+
)}
|
|
199
|
+
</Stack>
|
|
200
|
+
</Dialog>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const ShopifyIcon = () => {
|
|
4
|
+
return (
|
|
5
|
+
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
6
|
+
<path
|
|
7
|
+
d="M15.3269 3.85113C15.3132 3.75015 15.2258 3.69411 15.1531 3.688C15.081 3.6819 13.6693 3.66026 13.6693 3.66026C13.6693 3.66026 12.4887 2.50392 12.3722 2.38628C12.2555 2.26865 12.0277 2.30417 11.9392 2.3308C11.9381 2.33135 11.7175 2.40016 11.3461 2.51612C11.2839 2.31304 11.1927 2.06335 11.0622 1.81255C10.6419 1.00356 10.0263 0.575752 9.2825 0.574642C9.28142 0.574642 9.28092 0.574642 9.27975 0.574642C9.22808 0.574642 9.17692 0.579636 9.12517 0.584074C9.10317 0.557441 9.08117 0.531362 9.05808 0.505838C8.73408 0.156272 8.31869 -0.0140727 7.82082 0.000908712C6.86027 0.0286521 5.90357 0.72834 5.12787 1.97124C4.58212 2.84572 4.16677 3.94435 4.04904 4.79497C2.94601 5.13953 2.17471 5.38035 2.15766 5.3859C1.60091 5.56235 1.58331 5.57955 1.51069 6.10889C1.45677 6.50895 0 17.8704 0 17.8704L12.2082 20L17.4994 18.6733C17.4994 18.6733 15.3407 3.95212 15.3269 3.85113ZM10.7349 2.707C10.4537 2.79467 10.1342 2.89454 9.78758 3.00274C9.78042 2.51224 9.72267 1.82975 9.496 1.23992C10.2249 1.3792 10.5836 2.21095 10.7349 2.707ZM9.14883 3.20249C8.509 3.40225 7.81091 3.62031 7.11058 3.83892C7.30753 3.0782 7.68107 2.32081 8.13989 1.8242C8.31044 1.63943 8.54917 1.43358 8.832 1.31594C9.09767 1.87525 9.15542 2.66705 9.14883 3.20249ZM7.84007 0.645665C8.06562 0.640671 8.25542 0.690609 8.41775 0.798253C8.15805 0.9342 7.90718 1.12951 7.67172 1.38419C7.06162 2.04448 6.594 3.06932 6.4075 4.0581C5.826 4.23954 5.25715 4.41766 4.73342 4.58078C5.06405 3.02438 6.35743 0.688944 7.84007 0.645665Z"
|
|
8
|
+
fill="#95BF47"
|
|
9
|
+
/>
|
|
10
|
+
<path
|
|
11
|
+
d="M9.276 6.43238L8.66142 8.75117C8.66142 8.75117 7.97598 8.43658 7.16342 8.48817C5.97181 8.56417 5.95916 9.32217 5.97126 9.51242C6.03618 10.5495 8.74125 10.7759 8.89308 13.2051C9.01242 15.1161 7.88796 16.4233 6.26779 16.5265C4.32303 16.6502 3.25246 15.4933 3.25246 15.4933L3.66452 13.7256C3.66452 13.7256 4.74224 14.5457 5.60487 14.4907C6.16821 14.4547 6.36957 13.9924 6.34921 13.6657C6.26448 12.3128 4.06172 12.3927 3.92253 10.17C3.80536 8.29951 5.02337 6.40408 7.71081 6.23318C8.74617 6.16604 9.276 6.43238 9.276 6.43238Z"
|
|
12
|
+
fill="white"
|
|
13
|
+
/>
|
|
14
|
+
<path
|
|
15
|
+
d="M15.1536 3.68853C15.0815 3.68243 13.6698 3.66078 13.6698 3.66078C13.6698 3.66078 12.4893 2.50444 12.3726 2.38681C12.3292 2.34298 12.2703 2.32023 12.2087 2.31079L12.2093 19.9994L17.4999 18.6733C17.4999 18.6733 15.3412 3.95264 15.3274 3.85166C15.3137 3.75068 15.2257 3.69463 15.1536 3.68853Z"
|
|
16
|
+
fill="#5E8E3E"
|
|
17
|
+
/>
|
|
18
|
+
</svg>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default ShopifyIcon
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, {CSSProperties, useCallback, useEffect, MouseEvent} from 'react'
|
|
2
|
+
import videojs, {VideoJsPlayer} from 'video.js'
|
|
3
|
+
|
|
4
|
+
type PlayerKind = 'player' | 'diff'
|
|
5
|
+
|
|
6
|
+
interface VideoProps {
|
|
7
|
+
src: string
|
|
8
|
+
kind: PlayerKind
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const VideoPlayer = ({src, kind}: VideoProps) => {
|
|
12
|
+
const videoNode = React.useRef<HTMLVideoElement>(null)
|
|
13
|
+
const player = React.useRef<VideoJsPlayer>()
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
player.current = videojs(videoNode.current ?? '', {
|
|
17
|
+
sources: [{src}],
|
|
18
|
+
controls: true,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
player.current.src({src})
|
|
22
|
+
}, [src])
|
|
23
|
+
|
|
24
|
+
const stopPropagation = useCallback((event: MouseEvent) => {
|
|
25
|
+
event.stopPropagation()
|
|
26
|
+
}, [])
|
|
27
|
+
|
|
28
|
+
const className: Record<PlayerKind, string> = {
|
|
29
|
+
player: 'video-js vjs-16-9 vjs-big-play-centered',
|
|
30
|
+
diff: 'video-js vjs-layout-tiny vjs-fluid',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const style: CSSProperties = {position: 'relative'}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div>
|
|
37
|
+
<link href="https://vjs.zencdn.net/7.8.4/video-js.css" rel="stylesheet" />
|
|
38
|
+
<div data-vjs-player>
|
|
39
|
+
<video
|
|
40
|
+
onClick={stopPropagation}
|
|
41
|
+
style={kind === 'diff' ? style : {}}
|
|
42
|
+
className={className[kind]}
|
|
43
|
+
ref={videoNode}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default VideoPlayer
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {BehaviorSubject, Observable, concat, defer} from 'rxjs'
|
|
2
|
+
import {debounceTime, distinctUntilChanged, map, switchMap, withLatestFrom} from 'rxjs/operators'
|
|
3
|
+
|
|
4
|
+
import axios from 'axios'
|
|
5
|
+
|
|
6
|
+
type SearchSubject = BehaviorSubject<string>
|
|
7
|
+
type CursorSubject = BehaviorSubject<any>
|
|
8
|
+
|
|
9
|
+
const fetchSearch = (
|
|
10
|
+
projectId: string,
|
|
11
|
+
shop: string,
|
|
12
|
+
query: string,
|
|
13
|
+
cursor: string,
|
|
14
|
+
perPage: number
|
|
15
|
+
): Observable<any> =>
|
|
16
|
+
defer(() =>
|
|
17
|
+
axios.get(
|
|
18
|
+
`https://${projectId}.api.sanity.io/v1/shopify/assets/production?shop=${shop}&query=${encodeURIComponent(
|
|
19
|
+
query
|
|
20
|
+
)}${cursor && `&cursor=${cursor}`}&limit=${perPage}`,
|
|
21
|
+
{
|
|
22
|
+
withCredentials: true,
|
|
23
|
+
method: 'GET',
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
).pipe(map((result) => result.data))
|
|
27
|
+
|
|
28
|
+
const fetchList = (
|
|
29
|
+
projectId: string,
|
|
30
|
+
shop: string,
|
|
31
|
+
cursor: string,
|
|
32
|
+
perPage: number
|
|
33
|
+
): Observable<any> =>
|
|
34
|
+
defer(() =>
|
|
35
|
+
axios.get(
|
|
36
|
+
`https://${projectId}.api.sanity.io/v1/shopify/assets/production?shop=${shop}${
|
|
37
|
+
cursor && `&cursor=${cursor}`
|
|
38
|
+
}&limit=${perPage}`,
|
|
39
|
+
{
|
|
40
|
+
withCredentials: true,
|
|
41
|
+
method: 'GET',
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
).pipe(map((result) => result.data))
|
|
45
|
+
|
|
46
|
+
export const search = (
|
|
47
|
+
projectId: string,
|
|
48
|
+
shop: string,
|
|
49
|
+
query: SearchSubject,
|
|
50
|
+
cursor: CursorSubject,
|
|
51
|
+
resultsPerPage: number
|
|
52
|
+
): Observable<any> => {
|
|
53
|
+
return concat(
|
|
54
|
+
query.pipe(
|
|
55
|
+
withLatestFrom(cursor),
|
|
56
|
+
debounceTime(500),
|
|
57
|
+
distinctUntilChanged(),
|
|
58
|
+
switchMap(([q, c]) => {
|
|
59
|
+
if (q) {
|
|
60
|
+
return fetchSearch(projectId, shop, q, c, resultsPerPage).pipe(distinctUntilChanged())
|
|
61
|
+
}
|
|
62
|
+
return fetchList(projectId, shop, c, resultsPerPage)
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {shopifyAssetSchema} from './schema/shopifyAssetSchema'
|
|
2
|
+
import {definePlugin, ObjectDefinition} from 'sanity'
|
|
3
|
+
import {PluginConfig} from './types'
|
|
4
|
+
|
|
5
|
+
export * from './types'
|
|
6
|
+
|
|
7
|
+
// enables autocompletion and validation of document options
|
|
8
|
+
declare module 'sanity' {
|
|
9
|
+
export namespace Schema {
|
|
10
|
+
// here we type up our custom schema definition
|
|
11
|
+
export type ShopifyAssetTypeDef = Omit<ObjectDefinition, 'type' | 'fields'> & {
|
|
12
|
+
type: 'shopify.asset'
|
|
13
|
+
options: {
|
|
14
|
+
shopifyDomain: string
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Adds 'extension-type' as an intrinsic type
|
|
18
|
+
export interface IntrinsicTypeDefinition {
|
|
19
|
+
'shopify.asset': ShopifyAssetTypeDef
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const shopifyAssets = definePlugin<PluginConfig>((config) => {
|
|
25
|
+
return {
|
|
26
|
+
name: 'shopify-asset-schema',
|
|
27
|
+
schema: {
|
|
28
|
+
types: [shopifyAssetSchema(config)],
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
})
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import ShopifyAssetInput from '../components/ShopifyAssetInput'
|
|
3
|
+
import AssetDiff from '../components/AssetDiff'
|
|
4
|
+
import AssetPreview from '../components/AssetPreview'
|
|
5
|
+
import {defineField, defineType} from 'sanity'
|
|
6
|
+
|
|
7
|
+
interface ObjectConfig {
|
|
8
|
+
shopifyDomain: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module 'sanity' {
|
|
12
|
+
interface ObjectOptions {
|
|
13
|
+
shopifyDomain?: string
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const shopifyAssetSchema = (config: ObjectConfig) => {
|
|
18
|
+
const {shopifyDomain} = config
|
|
19
|
+
|
|
20
|
+
return defineType({
|
|
21
|
+
type: 'object',
|
|
22
|
+
name: 'shopify.asset',
|
|
23
|
+
title: 'Shopify Asset',
|
|
24
|
+
options: {
|
|
25
|
+
shopifyDomain,
|
|
26
|
+
},
|
|
27
|
+
fields: [
|
|
28
|
+
defineField({
|
|
29
|
+
type: 'string',
|
|
30
|
+
name: 'filename',
|
|
31
|
+
}),
|
|
32
|
+
defineField({
|
|
33
|
+
type: 'string',
|
|
34
|
+
name: 'id',
|
|
35
|
+
}),
|
|
36
|
+
defineField({
|
|
37
|
+
type: 'object',
|
|
38
|
+
name: 'meta',
|
|
39
|
+
fields: [
|
|
40
|
+
defineField({
|
|
41
|
+
type: 'string',
|
|
42
|
+
name: 'alt',
|
|
43
|
+
}),
|
|
44
|
+
defineField({
|
|
45
|
+
type: 'number',
|
|
46
|
+
name: 'duration',
|
|
47
|
+
}),
|
|
48
|
+
defineField({
|
|
49
|
+
type: 'number',
|
|
50
|
+
name: 'fileSize',
|
|
51
|
+
}),
|
|
52
|
+
defineField({
|
|
53
|
+
type: 'number',
|
|
54
|
+
name: 'height',
|
|
55
|
+
}),
|
|
56
|
+
defineField({
|
|
57
|
+
type: 'number',
|
|
58
|
+
name: 'width',
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
defineField({
|
|
63
|
+
type: 'object',
|
|
64
|
+
name: 'preview',
|
|
65
|
+
fields: [
|
|
66
|
+
defineField({
|
|
67
|
+
type: 'number',
|
|
68
|
+
name: 'height',
|
|
69
|
+
}),
|
|
70
|
+
defineField({
|
|
71
|
+
type: 'number',
|
|
72
|
+
name: 'width',
|
|
73
|
+
}),
|
|
74
|
+
defineField({
|
|
75
|
+
type: 'url',
|
|
76
|
+
name: 'url',
|
|
77
|
+
}),
|
|
78
|
+
],
|
|
79
|
+
}),
|
|
80
|
+
defineField({
|
|
81
|
+
type: 'string',
|
|
82
|
+
name: 'type',
|
|
83
|
+
}),
|
|
84
|
+
defineField({
|
|
85
|
+
type: 'url',
|
|
86
|
+
name: 'url',
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
...({
|
|
90
|
+
components: {
|
|
91
|
+
input: ShopifyAssetInput,
|
|
92
|
+
diff: AssetDiff,
|
|
93
|
+
preview: AssetPreview,
|
|
94
|
+
},
|
|
95
|
+
} as {}),
|
|
96
|
+
preview: {
|
|
97
|
+
select: {
|
|
98
|
+
meta: 'meta',
|
|
99
|
+
preview: 'preview',
|
|
100
|
+
url: 'url',
|
|
101
|
+
filename: 'filename',
|
|
102
|
+
type: 'type',
|
|
103
|
+
},
|
|
104
|
+
prepare({url, meta, preview, filename, type}) {
|
|
105
|
+
return {
|
|
106
|
+
title: filename,
|
|
107
|
+
subtitle: type,
|
|
108
|
+
value: {
|
|
109
|
+
url,
|
|
110
|
+
meta,
|
|
111
|
+
preview,
|
|
112
|
+
filename,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {ObjectSchemaType} from 'sanity'
|
|
2
|
+
|
|
3
|
+
export interface PluginConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Your *.myshopify.com domain. Do not include https:// or any path.
|
|
6
|
+
*/
|
|
7
|
+
shopifyDomain: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ObjectSchemaWithOptions extends ObjectSchemaType {
|
|
11
|
+
options: {
|
|
12
|
+
shopifyDomain: string
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type PossibleFileTypes = 'file' | 'image' | 'video'
|
|
17
|
+
|
|
18
|
+
export interface AssetPreviewImage {
|
|
19
|
+
height: number
|
|
20
|
+
url: string
|
|
21
|
+
width: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AssetMeta {
|
|
25
|
+
alt?: string
|
|
26
|
+
duration?: number
|
|
27
|
+
fileSize?: number
|
|
28
|
+
height?: number
|
|
29
|
+
width?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ShopifyFile {
|
|
33
|
+
meta: AssetMeta
|
|
34
|
+
preview: AssetPreviewImage
|
|
35
|
+
id: string
|
|
36
|
+
type: PossibleFileTypes
|
|
37
|
+
url: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Asset extends ShopifyFile {
|
|
41
|
+
_type?: string
|
|
42
|
+
_key?: string
|
|
43
|
+
filename?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PageInfo {
|
|
47
|
+
hasNextPage: boolean
|
|
48
|
+
cursor: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ShopifyAPIResponse {
|
|
52
|
+
pageInfo: PageInfo
|
|
53
|
+
assets: ShopifyFile[]
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const extractName = (name: string): string => name?.split('/')?.pop()?.split('?')[0] ?? ''
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
|
|
2
|
+
const {name, version, sanityExchangeUrl} = require('./package.json')
|
|
3
|
+
|
|
4
|
+
export default showIncompatiblePluginDialog({
|
|
5
|
+
name: name,
|
|
6
|
+
versions: {
|
|
7
|
+
v3: version,
|
|
8
|
+
v2: undefined,
|
|
9
|
+
},
|
|
10
|
+
sanityExchangeUrl,
|
|
11
|
+
})
|