@@ -23,10 +23,27 @@ import { BsHeart, BsHeartFill } from "react-icons/bs";
23
23
import { HiDownload } from "react-icons/hi" ;
24
24
import { IoMdCheckmarkCircleOutline } from "react-icons/io" ;
25
25
import { MdOutlineModelTraining } from "react-icons/md" ;
26
+ import { Ri4KFill } from "react-icons/ri" ;
26
27
import { useMutation , useQuery } from "react-query" ;
27
28
import ShotImage from "./ShotImage" ;
28
29
import { TbFaceIdError } from "react-icons/tb" ;
29
30
31
+ const getHdLabel = ( shot : Shot , isHd : boolean ) => {
32
+ if ( shot . hdStatus === "NO" ) {
33
+ return "Generate in 4K" ;
34
+ }
35
+
36
+ if ( shot . hdStatus === "PENDING" ) {
37
+ return "4K in progress" ;
38
+ }
39
+
40
+ if ( shot . hdStatus === "PROCESSED" && isHd ) {
41
+ return "Show standard resolution" ;
42
+ }
43
+
44
+ return "Show 4K" ;
45
+ } ;
46
+
30
47
const ShotCard = ( {
31
48
shot : initialShot ,
32
49
handleSeed,
@@ -37,6 +54,7 @@ const ShotCard = ({
37
54
const { onCopy, hasCopied } = useClipboard ( initialShot . prompt ) ;
38
55
const { query } = useRouter ( ) ;
39
56
const [ shot , setShot ] = useState ( initialShot ) ;
57
+ const [ isHd , setIsHd ] = useState ( Boolean ( shot . hdOutputUrl ) ) ;
40
58
41
59
const { mutate : bookmark , isLoading } = useMutation (
42
60
`update-shot-${ initialShot . id } ` ,
@@ -54,6 +72,19 @@ const ShotCard = ({
54
72
}
55
73
) ;
56
74
75
+ const { mutate : createdHd , isLoading : isCreatingHd } = useMutation (
76
+ `create-hd-${ initialShot . id } ` ,
77
+ ( ) =>
78
+ axios . post < { shot : Shot } > (
79
+ `/api/projects/${ query . id } /predictions/${ initialShot . id } /hd`
80
+ ) ,
81
+ {
82
+ onSuccess : ( response ) => {
83
+ setShot ( response . data . shot ) ;
84
+ } ,
85
+ }
86
+ ) ;
87
+
57
88
useQuery (
58
89
`shot-${ initialShot . id } ` ,
59
90
( ) =>
@@ -65,10 +96,33 @@ const ShotCard = ({
65
96
{
66
97
refetchInterval : ( data ) => ( data ?. shot . outputUrl ? false : 5000 ) ,
67
98
refetchOnWindowFocus : false ,
68
- enabled : ! initialShot . outputUrl ,
99
+ enabled : ! initialShot . outputUrl && initialShot . status !== "failed" ,
100
+ initialData : { shot : initialShot } ,
101
+ onSuccess : ( response ) => {
102
+ setShot ( response . shot ) ;
103
+ } ,
104
+ }
105
+ ) ;
106
+
107
+ useQuery (
108
+ `shot-hd-${ initialShot . id } ` ,
109
+ ( ) =>
110
+ axios
111
+ . get < { shot : Shot } > (
112
+ `/api/projects/${ query . id } /predictions/${ initialShot . id } /hd`
113
+ )
114
+ . then ( ( res ) => res . data ) ,
115
+ {
116
+ refetchInterval : ( data ) =>
117
+ data ?. shot . hdStatus !== "PENDING" ? false : 5000 ,
118
+ refetchOnWindowFocus : false ,
119
+ enabled : shot . hdStatus === "PENDING" ,
69
120
initialData : { shot : initialShot } ,
70
121
onSuccess : ( response ) => {
71
122
setShot ( response . shot ) ;
123
+ if ( response . shot . hdOutputUrl ) {
124
+ setIsHd ( true ) ;
125
+ }
72
126
} ,
73
127
}
74
128
) ;
@@ -82,7 +136,7 @@ const ShotCard = ({
82
136
position = "relative"
83
137
>
84
138
{ shot . outputUrl ? (
85
- < ShotImage shot = { shot } />
139
+ < ShotImage isHd = { isHd } shot = { shot } />
86
140
) : (
87
141
< Box >
88
142
< AspectRatio ratio = { 1 } >
@@ -104,10 +158,7 @@ const ShotCard = ({
104
158
</ Box >
105
159
) }
106
160
< Flex position = "relative" p = { 3 } flexDirection = "column" >
107
- < Flex alignItems = "center" justifyContent = "space-between" >
108
- < Text color = "blackAlpha.700" fontSize = "xs" >
109
- { formatRelative ( new Date ( shot . createdAt ) , new Date ( ) ) }
110
- </ Text >
161
+ < Flex alignItems = "center" justifyContent = "flex-end" >
111
162
< Box >
112
163
{ shot . seed && shot . outputUrl && (
113
164
< Tooltip hasArrow label = "Re-use style" >
@@ -129,17 +180,41 @@ const ShotCard = ({
129
180
</ Tooltip >
130
181
) }
131
182
{ shot . outputUrl && (
132
- < IconButton
133
- size = "sm"
134
- as = { Link }
135
- href = { shot . outputUrl }
136
- target = "_blank"
137
- variant = "ghost"
138
- aria-label = "Download"
139
- fontSize = "md"
140
- icon = { < HiDownload /> }
141
- />
183
+ < >
184
+ < IconButton
185
+ size = "sm"
186
+ as = { Link }
187
+ href = { isHd ? shot . hdOutputUrl : shot . outputUrl }
188
+ target = "_blank"
189
+ variant = "ghost"
190
+ aria-label = "Download"
191
+ fontSize = "md"
192
+ icon = { < HiDownload /> }
193
+ />
194
+ < Tooltip hasArrow label = { getHdLabel ( shot , isHd ) } >
195
+ < IconButton
196
+ icon = { < Ri4KFill /> }
197
+ color = { isHd ? "red.400" : "gray.600" }
198
+ isLoading = { shot . hdStatus === "PENDING" || isCreatingHd }
199
+ onClick = { ( ) => {
200
+ if ( shot . hdStatus === "NO" ) {
201
+ createdHd ( ) ;
202
+ } else if (
203
+ shot . hdStatus === "PROCESSED" &&
204
+ shot . hdOutputUrl
205
+ ) {
206
+ setIsHd ( ! isHd ) ;
207
+ }
208
+ } }
209
+ size = "sm"
210
+ variant = "ghost"
211
+ aria-label = "Make 4K"
212
+ fontSize = "lg"
213
+ />
214
+ </ Tooltip >
215
+ </ >
142
216
) }
217
+
143
218
< Tooltip
144
219
hasArrow
145
220
label = { `${ shot . bookmarked ? "Remove" : "Add" } to your gallery` }
@@ -168,7 +243,10 @@ const ShotCard = ({
168
243
{ shot . prompt }
169
244
</ Text >
170
245
171
- < HStack mt = { 4 } >
246
+ < HStack justifyContent = "space-between" mt = { 4 } >
247
+ < Text color = "beige.400" fontSize = "xs" >
248
+ { formatRelative ( new Date ( shot . createdAt ) , new Date ( ) ) }
249
+ </ Text >
172
250
< Button
173
251
rightIcon = { hasCopied ? < IoMdCheckmarkCircleOutline /> : undefined }
174
252
colorScheme = "beige"
0 commit comments