Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Bw-bestperf
SAM2
Commits
17d316f3
Commit
17d316f3
authored
Feb 04, 2026
by
suily
Browse files
Initial commit
parents
Pipeline
#3368
failed with stages
in 0 seconds
Changes
959
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1980 additions
and
0 deletions
+1980
-0
demo/frontend/src/common/components/effects/BackgroundEffects.tsx
...ntend/src/common/components/effects/BackgroundEffects.tsx
+61
-0
demo/frontend/src/common/components/effects/EffectVariantBadge.tsx
...tend/src/common/components/effects/EffectVariantBadge.tsx
+41
-0
demo/frontend/src/common/components/effects/EffectsCarousel.tsx
...rontend/src/common/components/effects/EffectsCarousel.tsx
+93
-0
demo/frontend/src/common/components/effects/EffectsCarouselShadow.tsx
...d/src/common/components/effects/EffectsCarouselShadow.tsx
+46
-0
demo/frontend/src/common/components/effects/EffectsToolbar.tsx
...frontend/src/common/components/effects/EffectsToolbar.tsx
+48
-0
demo/frontend/src/common/components/effects/EffectsToolbarBottomActions.tsx
...common/components/effects/EffectsToolbarBottomActions.tsx
+46
-0
demo/frontend/src/common/components/effects/EffectsToolbarHeader.tsx
...nd/src/common/components/effects/EffectsToolbarHeader.tsx
+62
-0
demo/frontend/src/common/components/effects/EffectsUtils.ts
demo/frontend/src/common/components/effects/EffectsUtils.ts
+76
-0
demo/frontend/src/common/components/effects/HighlightEffects.tsx
...ontend/src/common/components/effects/HighlightEffects.tsx
+64
-0
demo/frontend/src/common/components/effects/MobileEffectsToolbar.tsx
...nd/src/common/components/effects/MobileEffectsToolbar.tsx
+115
-0
demo/frontend/src/common/components/effects/MoreFunEffects.tsx
...frontend/src/common/components/effects/MoreFunEffects.tsx
+54
-0
demo/frontend/src/common/components/gallery/ChangeVideoModal.tsx
...ontend/src/common/components/gallery/ChangeVideoModal.tsx
+83
-0
demo/frontend/src/common/components/gallery/DefaultVideoGalleryModalTrigger.tsx
...on/components/gallery/DefaultVideoGalleryModalTrigger.tsx
+32
-0
demo/frontend/src/common/components/gallery/DemoVideoGallery.tsx
...ontend/src/common/components/gallery/DemoVideoGallery.tsx
+209
-0
demo/frontend/src/common/components/gallery/DemoVideoGalleryModal.tsx
...d/src/common/components/gallery/DemoVideoGalleryModal.tsx
+148
-0
demo/frontend/src/common/components/gallery/VideoGalleryUploadPhoto.tsx
...src/common/components/gallery/VideoGalleryUploadPhoto.tsx
+102
-0
demo/frontend/src/common/components/gallery/VideoPhoto.tsx
demo/frontend/src/common/components/gallery/VideoPhoto.tsx
+112
-0
demo/frontend/src/common/components/gallery/__generated__/DemoVideoGalleryModalQuery.graphql.ts
...llery/__generated__/DemoVideoGalleryModalQuery.graphql.ts
+303
-0
demo/frontend/src/common/components/gallery/__generated__/DemoVideoGalleryQuery.graphql.ts
...ts/gallery/__generated__/DemoVideoGalleryQuery.graphql.ts
+148
-0
demo/frontend/src/common/components/gallery/__generated__/useUploadVideoMutation.graphql.ts
...s/gallery/__generated__/useUploadVideoMutation.graphql.ts
+137
-0
No files found.
demo/frontend/src/common/components/effects/BackgroundEffects.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
backgroundEffects
}
from
'
@/common/components/effects/EffectsUtils
'
;
import
EffectVariantBadge
from
'
@/common/components/effects/EffectVariantBadge
'
;
import
ToolbarActionIcon
from
'
@/common/components/toolbar/ToolbarActionIcon
'
;
import
ToolbarSection
from
'
@/common/components/toolbar/ToolbarSection
'
;
import
useVideoEffect
from
'
@/common/components/video/editor/useVideoEffect
'
;
import
{
EffectIndex
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
activeBackgroundEffectAtom
}
from
'
@/demo/atoms
'
;
import
{
useAtomValue
}
from
'
jotai
'
;
export
default
function
BackgroundEffects
()
{
const
setEffect
=
useVideoEffect
();
const
activeEffect
=
useAtomValue
(
activeBackgroundEffectAtom
);
return
(
<
ToolbarSection
title
=
"Background"
borderBottom
=
{
false
}
>
{
backgroundEffects
.
map
(
backgroundEffect
=>
{
return
(
<
ToolbarActionIcon
variant
=
"toggle"
key
=
{
backgroundEffect
.
title
}
icon
=
{
backgroundEffect
.
Icon
}
title
=
{
backgroundEffect
.
title
}
isActive
=
{
activeEffect
.
name
===
backgroundEffect
.
effectName
}
badge
=
{
activeEffect
.
name
===
backgroundEffect
.
effectName
&&
(
<
EffectVariantBadge
label
=
{
`
${
activeEffect
.
variant
+
1
}
/
${
activeEffect
.
numVariants
}
`
}
/>
)
}
onClick
=
{
()
=>
{
if
(
activeEffect
.
name
===
backgroundEffect
.
effectName
)
{
setEffect
(
backgroundEffect
.
effectName
,
EffectIndex
.
BACKGROUND
,
{
variant
:
(
activeEffect
.
variant
+
1
)
%
activeEffect
.
numVariants
,
});
}
else
{
setEffect
(
backgroundEffect
.
effectName
,
EffectIndex
.
BACKGROUND
);
}
}
}
/>
);
})
}
</
ToolbarSection
>
);
}
demo/frontend/src/common/components/effects/EffectVariantBadge.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
right
,
top
}
from
'
@/theme/tokens.stylex
'
;
import
stylex
from
'
@stylexjs/stylex
'
;
const
styles
=
stylex
.
create
({
variantBadge
:
{
position
:
'
absolute
'
,
top
:
top
[
1
],
right
:
right
[
1
],
backgroundColor
:
'
#280578
'
,
color
:
'
#D2D2FF
'
,
fontVariantNumeric
:
'
tabular-nums
'
,
paddingHorizontal
:
4
,
paddingVertical
:
1
,
fontSize
:
9
,
borderRadius
:
6
,
fontWeight
:
'
bold
'
,
},
});
type
Props
=
{
label
:
string
;
};
export
default
function
VariantBadge
({
label
}:
Props
)
{
return
<
div
{
...
stylex
.
props
(
styles
.
variantBadge
)
}
>
{
label
}
</
div
>;
}
demo/frontend/src/common/components/effects/EffectsCarousel.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
CarouselContainerShadow
}
from
'
@/common/components/effects/EffectsCarouselShadow
'
;
import
{
DemoEffect
}
from
'
@/common/components/effects/EffectsUtils
'
;
import
useVideoEffect
from
'
@/common/components/video/editor/useVideoEffect
'
;
import
type
{
EffectIndex
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
Effects
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
color
,
fontSize
,
spacing
}
from
'
@/theme/tokens.stylex
'
;
import
stylex
from
'
@stylexjs/stylex
'
;
type
Props
=
{
label
:
string
;
effects
:
DemoEffect
[];
activeEffect
:
keyof
Effects
;
index
:
EffectIndex
;
};
const
styles
=
stylex
.
create
({
container
:
{
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
gap
:
spacing
[
2
],
width
:
'
100%
'
,
},
label
:
{
fontSize
:
fontSize
[
'
xs
'
],
color
:
'
#A6ACB2
'
,
textAlign
:
'
center
'
,
},
carouselContainer
:
{
position
:
'
relative
'
,
borderRadius
:
'
8px
'
,
overflow
:
'
hidden
'
,
width
:
'
100%
'
,
height
:
'
120px
'
,
backgroundColor
:
color
[
'
gray-700
'
],
},
});
export
default
function
EffectsCarousel
({
label
,
effects
,
activeEffect
,
index
:
effectIndex
,
}:
Props
)
{
const
setEffect
=
useVideoEffect
();
return
(
<
div
{
...
stylex
.
props
(
styles
.
container
)
}
>
<
div
{
...
stylex
.
props
(
styles
.
label
)
}
>
{
label
}
</
div
>
<
div
{
...
stylex
.
props
(
styles
.
carouselContainer
)
}
>
<
CarouselContainerShadow
isTop
=
{
true
}
/>
<
div
className
=
"carousel carousel-vertical w-full h-full text-white"
>
<
div
className
=
{
`carousel-item h-6`
}
/>
{
effects
.
map
(({
effectName
,
Icon
,
title
},
index
)
=>
{
const
isActive
=
activeEffect
===
effectName
;
return
(
<
div
key
=
{
index
}
className
=
{
`carousel-item flex items-center h-6 gap-2 px-4`
}
onClick
=
{
()
=>
setEffect
(
effectName
,
effectIndex
)
}
>
<
Icon
color
=
{
isActive
?
'
#FB73A5
'
:
undefined
}
size
=
{
18
}
fontWeight
=
{
10
}
/>
<
div
className
=
{
`text-sm
${
isActive
?
'
text-[#FB73A5] font-bold
'
:
'
font-medium
'
}
`
}
>
{
title
}
</
div
>
</
div
>
);
})
}
<
div
className
=
{
`carousel-item h-6`
}
/>
</
div
>
<
CarouselContainerShadow
isTop
=
{
false
}
/>
</
div
>
</
div
>
);
}
demo/frontend/src/common/components/effects/EffectsCarouselShadow.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
spacing
}
from
'
@/theme/tokens.stylex
'
;
import
stylex
from
'
@stylexjs/stylex
'
;
const
styles
=
stylex
.
create
({
container
:
{
position
:
'
absolute
'
,
width
:
'
100%
'
,
height
:
spacing
[
8
],
pointerEvents
:
'
none
'
,
},
});
type
CarouselContainerShadowProps
=
{
isTop
:
boolean
;
};
const
edgeColor
=
'
rgba(55, 62, 65, 1)
'
;
const
transitionColor
=
'
rgba(55, 62, 65, 0.2)
'
;
export
function
CarouselContainerShadow
({
isTop
}:
CarouselContainerShadowProps
)
{
return
(
<
div
{
...
stylex
.
props
(
styles
.
container
)
}
style
=
{
{
background
:
`linear-gradient(
${
isTop
?
`
${
edgeColor
}
,
${
transitionColor
}
`
:
`
${
transitionColor
}
,
${
edgeColor
}
`
}
)`
,
top
:
isTop
?
0
:
undefined
,
bottom
:
isTop
?
undefined
:
0
,
}
}
/>
);
}
demo/frontend/src/common/components/effects/EffectsToolbar.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
BackgroundEffects
from
'
@/common/components/effects/BackgroundEffects
'
;
import
EffectsToolbarBottomActions
from
'
@/common/components/effects/EffectsToolbarBottomActions
'
;
import
EffectsToolbarHeader
from
'
@/common/components/effects/EffectsToolbarHeader
'
;
import
HighlightEffects
from
'
@/common/components/effects/HighlightEffects
'
;
import
useMessagesSnackbar
from
'
@/common/components/snackbar/useDemoMessagesSnackbar
'
;
import
{
useEffect
,
useRef
}
from
'
react
'
;
type
Props
=
{
onTabChange
:
(
newIndex
:
number
)
=>
void
;
};
export
default
function
EffectsToolbar
({
onTabChange
}:
Props
)
{
const
isEffectsMessageShown
=
useRef
(
false
);
const
{
enqueueMessage
}
=
useMessagesSnackbar
();
useEffect
(()
=>
{
if
(
!
isEffectsMessageShown
.
current
)
{
isEffectsMessageShown
.
current
=
true
;
enqueueMessage
(
'
effectsMessage
'
);
}
},
[
enqueueMessage
]);
return
(
<
div
className
=
"flex flex-col h-full"
>
<
EffectsToolbarHeader
/>
<
div
className
=
"grow overflow-y-auto"
>
<
HighlightEffects
/>
<
BackgroundEffects
/>
</
div
>
<
EffectsToolbarBottomActions
onTabChange
=
{
onTabChange
}
/>
</
div
>
);
}
demo/frontend/src/common/components/effects/EffectsToolbarBottomActions.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
PrimaryCTAButton
from
'
@/common/components/button/PrimaryCTAButton
'
;
import
RestartSessionButton
from
'
@/common/components/session/RestartSessionButton
'
;
import
ToolbarBottomActionsWrapper
from
'
@/common/components/toolbar/ToolbarBottomActionsWrapper
'
;
import
{
MORE_OPTIONS_TOOLBAR_INDEX
,
OBJECT_TOOLBAR_INDEX
,
}
from
'
@/common/components/toolbar/ToolbarConfig
'
;
import
{
ChevronRight
}
from
'
@carbon/icons-react
'
;
type
Props
=
{
onTabChange
:
(
newIndex
:
number
)
=>
void
;
};
export
default
function
EffectsToolbarBottomActions
({
onTabChange
}:
Props
)
{
function
handleSwitchToMoreOptionsTab
()
{
onTabChange
(
MORE_OPTIONS_TOOLBAR_INDEX
);
}
return
(
<
ToolbarBottomActionsWrapper
>
<
RestartSessionButton
onRestartSession
=
{
()
=>
onTabChange
(
OBJECT_TOOLBAR_INDEX
)
}
/>
<
PrimaryCTAButton
onClick
=
{
handleSwitchToMoreOptionsTab
}
endIcon
=
{
<
ChevronRight
/>
}
>
Next
</
PrimaryCTAButton
>
</
ToolbarBottomActionsWrapper
>
);
}
demo/frontend/src/common/components/effects/EffectsToolbarHeader.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
ToolbarHeaderWrapper
from
'
@/common/components/toolbar/ToolbarHeaderWrapper
'
;
import
useVideoEffect
from
'
@/common/components/video/editor/useVideoEffect
'
;
import
{
EffectIndex
,
effectPresets
,
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
BLUE_PINK_FILL
}
from
'
@/theme/gradientStyle
'
;
import
{
MagicWandFilled
}
from
'
@carbon/icons-react
'
;
import
{
useCallback
,
useRef
}
from
'
react
'
;
import
{
Button
}
from
'
react-daisyui
'
;
export
default
function
EffectsToolbarHeader
()
{
const
preset
=
useRef
(
0
);
const
setEffect
=
useVideoEffect
();
const
handleTogglePreset
=
useCallback
(()
=>
{
preset
.
current
++
;
const
[
background
,
highlight
]
=
effectPresets
[
preset
.
current
%
effectPresets
.
length
];
setEffect
(
background
.
name
,
EffectIndex
.
BACKGROUND
,
{
variant
:
background
.
variant
,
});
setEffect
(
highlight
.
name
,
EffectIndex
.
HIGHLIGHT
,
{
variant
:
highlight
.
variant
,
});
},
[
setEffect
]);
return
(
<
ToolbarHeaderWrapper
title
=
"Add effects"
description
=
"Apply visual effects to your selected objects and the background. Keeping clicking the same effect for different variations."
bottomSection
=
{
<
div
className
=
"flex mt-1"
>
<
Button
color
=
"ghost"
size
=
"md"
className
=
{
`font-medium bg-black !rounded-full hover:!bg-gradient-to-br
${
BLUE_PINK_FILL
}
border-none`
}
endIcon
=
{
<
MagicWandFilled
size
=
{
20
}
className
=
"text-white "
/>
}
onClick
=
{
handleTogglePreset
}
>
Surprise Me
</
Button
>
</
div
>
}
className
=
"pb-4"
/>
);
}
demo/frontend/src/common/components/effects/EffectsUtils.ts
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
Effects
}
from
'
@/common/components/video/effects/Effects
'
;
import
type
{
CarbonIconType
}
from
'
@carbon/icons-react
'
;
import
{
AppleDash
,
Asterisk
,
Barcode
,
CenterCircle
,
ColorPalette
,
ColorSwitch
,
Development
,
Erase
,
FaceWink
,
Humidity
,
Image
,
Overlay
,
TextFont
,
}
from
'
@carbon/icons-react
'
;
export
type
DemoEffect
=
{
title
:
string
;
Icon
:
CarbonIconType
;
effectName
:
keyof
Effects
;
};
export
const
backgroundEffects
:
DemoEffect
[]
=
[
{
title
:
'
Original
'
,
Icon
:
Image
,
effectName
:
'
Original
'
},
{
title
:
'
Erase
'
,
Icon
:
Erase
,
effectName
:
'
EraseBackground
'
},
{
title
:
'
Gradient
'
,
Icon
:
ColorPalette
,
effectName
:
'
Gradient
'
,
},
{
title
:
'
Pixelate
'
,
Icon
:
Development
,
effectName
:
'
Pixelate
'
,
},
{
title
:
'
Desaturate
'
,
Icon
:
ColorSwitch
,
effectName
:
'
Desaturate
'
},
{
title
:
'
Text
'
,
Icon
:
TextFont
,
effectName
:
'
BackgroundText
'
},
{
title
:
'
Blur
'
,
Icon
:
Humidity
,
effectName
:
'
BackgroundBlur
'
},
{
title
:
'
Outline
'
,
Icon
:
AppleDash
,
effectName
:
'
Sobel
'
},
];
export
const
highlightEffects
:
DemoEffect
[]
=
[
{
title
:
'
Original
'
,
Icon
:
Image
,
effectName
:
'
Cutout
'
},
{
title
:
'
Erase
'
,
Icon
:
Erase
,
effectName
:
'
EraseForeground
'
},
{
title
:
'
Gradient
'
,
Icon
:
ColorPalette
,
effectName
:
'
VibrantMask
'
},
{
title
:
'
Pixelate
'
,
Icon
:
Development
,
effectName
:
'
PixelateMask
'
},
{
title
:
'
Overlay
'
,
Icon
:
Overlay
,
effectName
:
'
Overlay
'
,
},
{
title
:
'
Emoji
'
,
Icon
:
FaceWink
,
effectName
:
'
Replace
'
},
{
title
:
'
Burst
'
,
Icon
:
Asterisk
,
effectName
:
'
Burst
'
},
{
title
:
'
Spotlight
'
,
Icon
:
CenterCircle
,
effectName
:
'
Scope
'
},
];
export
const
moreEffects
:
DemoEffect
[]
=
[
{
title
:
'
Noisy
'
,
Icon
:
Barcode
,
effectName
:
'
NoisyMask
'
},
];
demo/frontend/src/common/components/effects/HighlightEffects.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
EffectVariantBadge
from
'
@/common/components/effects/EffectVariantBadge
'
;
import
ToolbarActionIcon
from
'
@/common/components/toolbar/ToolbarActionIcon
'
;
import
ToolbarSection
from
'
@/common/components/toolbar/ToolbarSection
'
;
import
useVideoEffect
from
'
@/common/components/video/editor/useVideoEffect
'
;
import
{
EffectIndex
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
activeHighlightEffectAtom
,
activeHighlightEffectGroupAtom
,
}
from
'
@/demo/atoms
'
;
import
{
useAtomValue
}
from
'
jotai
'
;
export
default
function
HighlightEffects
()
{
const
setEffect
=
useVideoEffect
();
const
activeEffect
=
useAtomValue
(
activeHighlightEffectAtom
);
const
activeEffectsGroup
=
useAtomValue
(
activeHighlightEffectGroupAtom
);
return
(
<
ToolbarSection
title
=
"Selected Objects"
borderBottom
=
{
true
}
>
{
activeEffectsGroup
.
map
(
highlightEffect
=>
{
return
(
<
ToolbarActionIcon
variant
=
"toggle"
key
=
{
highlightEffect
.
title
}
icon
=
{
highlightEffect
.
Icon
}
title
=
{
highlightEffect
.
title
}
isActive
=
{
activeEffect
.
name
===
highlightEffect
.
effectName
}
badge
=
{
activeEffect
.
name
===
highlightEffect
.
effectName
&&
(
<
EffectVariantBadge
label
=
{
`
${
activeEffect
.
variant
+
1
}
/
${
activeEffect
.
numVariants
}
`
}
/>
)
}
onClick
=
{
()
=>
{
if
(
activeEffect
.
name
===
highlightEffect
.
effectName
)
{
setEffect
(
highlightEffect
.
effectName
,
EffectIndex
.
HIGHLIGHT
,
{
variant
:
(
activeEffect
.
variant
+
1
)
%
activeEffect
.
numVariants
,
});
}
else
{
setEffect
(
highlightEffect
.
effectName
,
EffectIndex
.
HIGHLIGHT
);
}
}
}
/>
);
})
}
</
ToolbarSection
>
);
}
demo/frontend/src/common/components/effects/MobileEffectsToolbar.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
EffectsCarousel
from
'
@/common/components/effects/EffectsCarousel
'
;
import
{
backgroundEffects
}
from
'
@/common/components/effects/EffectsUtils
'
;
import
useVideoEffect
from
'
@/common/components/video/editor/useVideoEffect
'
;
import
{
EffectIndex
,
effectPresets
,
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
ListBoxes
,
MagicWand
,
MagicWandFilled
}
from
'
@carbon/icons-react
'
;
import
{
useCallback
,
useRef
,
useState
}
from
'
react
'
;
import
{
Button
}
from
'
react-daisyui
'
;
import
EffectsToolbarBottomActions
from
'
@/common/components/effects/EffectsToolbarBottomActions
'
;
import
ToolbarProgressChip
from
'
@/common/components/toolbar/ToolbarProgressChip
'
;
import
{
activeBackgroundEffectAtom
,
activeHighlightEffectAtom
,
activeHighlightEffectGroupAtom
,
}
from
'
@/demo/atoms
'
;
import
{
BLUE_PINK_FILL
}
from
'
@/theme/gradientStyle
'
;
import
{
useAtomValue
}
from
'
jotai
'
;
type
Props
=
{
onTabChange
:
(
newIndex
:
number
)
=>
void
;
};
export
default
function
MobileEffectsToolbar
({
onTabChange
}:
Props
)
{
const
preset
=
useRef
(
0
);
const
setEffect
=
useVideoEffect
();
const
[
showEffectsCarousels
,
setShowEffectsCarousels
]
=
useState
<
boolean
>
();
const
activeBackground
=
useAtomValue
(
activeBackgroundEffectAtom
);
const
activeHighlight
=
useAtomValue
(
activeHighlightEffectAtom
);
const
activeHighlightEffectsGroup
=
useAtomValue
(
activeHighlightEffectGroupAtom
,
);
const
handleTogglePreset
=
useCallback
(()
=>
{
preset
.
current
++
;
const
[
background
,
highlight
]
=
effectPresets
[
preset
.
current
%
effectPresets
.
length
];
setEffect
(
background
.
name
,
EffectIndex
.
BACKGROUND
,
{
variant
:
background
.
variant
,
});
setEffect
(
highlight
.
name
,
EffectIndex
.
HIGHLIGHT
,
{
variant
:
highlight
.
variant
,
});
},
[
setEffect
]);
return
(
<
div
className
=
"w-full"
>
{
showEffectsCarousels
?
(
<
div
className
=
"flex gap-2 px-2 py-4 items-center p-6"
>
<
Button
color
=
"ghost"
className
=
"mt-6 !px-2 !text-[#FB73A5]"
startIcon
=
{
<
MagicWand
size
=
{
20
}
/>
}
onClick
=
{
handleTogglePreset
}
/>
<
EffectsCarousel
label
=
"Highlights"
effects
=
{
activeHighlightEffectsGroup
}
activeEffect
=
{
activeHighlight
.
name
}
index
=
{
1
}
/>
<
EffectsCarousel
label
=
"Background"
effects
=
{
backgroundEffects
}
activeEffect
=
{
activeBackground
.
name
}
index
=
{
0
}
/>
</
div
>
)
:
(
<
div
className
=
"flex flex-col gap-6 p-6"
>
<
div
className
=
"text-sm text-white"
>
<
ToolbarProgressChip
/>
Apply visual effects to your selected objects and the background.
</
div
>
<
div
className
=
"grid grid-cols-2 gap-2"
>
<
Button
color
=
"ghost"
endIcon
=
{
<
MagicWandFilled
size
=
{
20
}
/>
}
className
=
{
`font-bold bg-black !rounded-full !bg-gradient-to-br
${
BLUE_PINK_FILL
}
border-none text-white`
}
onClick
=
{
handleTogglePreset
}
>
Surprise Me
</
Button
>
<
Button
color
=
"ghost"
className
=
{
`font-bold bg-black !rounded-full border-none text-white`
}
startIcon
=
{
<
ListBoxes
size
=
{
20
}
/>
}
onClick
=
{
()
=>
setShowEffectsCarousels
(
true
)
}
>
More effects
</
Button
>
</
div
>
</
div
>
)
}
<
EffectsToolbarBottomActions
onTabChange
=
{
onTabChange
}
/>
</
div
>
);
}
demo/frontend/src/common/components/effects/MoreFunEffects.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
moreEffects
}
from
'
@/common/components/effects/EffectsUtils
'
;
import
EffectVariantBadge
from
'
@/common/components/effects/EffectVariantBadge
'
;
import
ToolbarActionIcon
from
'
@/common/components/toolbar/ToolbarActionIcon
'
;
import
ToolbarSection
from
'
@/common/components/toolbar/ToolbarSection
'
;
import
useVideoEffect
from
'
@/common/components/video/editor/useVideoEffect
'
;
import
{
EffectIndex
}
from
'
@/common/components/video/effects/Effects
'
;
import
{
activeHighlightEffectAtom
}
from
'
@/demo/atoms
'
;
import
{
useAtomValue
}
from
'
jotai
'
;
export
default
function
MoreFunEffects
()
{
const
setEffect
=
useVideoEffect
();
const
activeEffect
=
useAtomValue
(
activeHighlightEffectAtom
);
return
(
<
ToolbarSection
title
=
"Selected Objects"
borderBottom
=
{
true
}
>
{
moreEffects
.
map
(
effect
=>
{
return
(
<
ToolbarActionIcon
variant
=
"toggle"
key
=
{
effect
.
title
}
icon
=
{
effect
.
Icon
}
title
=
{
effect
.
title
}
isActive
=
{
activeEffect
.
name
===
effect
.
effectName
}
badge
=
{
activeEffect
.
name
===
effect
.
effectName
&&
(
<
EffectVariantBadge
label
=
{
`
${
activeEffect
.
variant
+
1
}
/
${
activeEffect
.
numVariants
}
`
}
/>
)
}
onClick
=
{
()
=>
{
setEffect
(
effect
.
effectName
,
EffectIndex
.
HIGHLIGHT
);
}
}
/>
);
})
}
</
ToolbarSection
>
);
}
demo/frontend/src/common/components/gallery/ChangeVideoModal.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
type
{
VideoGalleryTriggerProps
}
from
'
@/common/components/gallery/DemoVideoGalleryModal
'
;
import
DemoVideoGalleryModal
from
'
@/common/components/gallery/DemoVideoGalleryModal
'
;
import
useVideo
from
'
@/common/components/video/editor/useVideo
'
;
import
Logger
from
'
@/common/logger/Logger
'
;
import
{
isStreamingAtom
,
uploadingStateAtom
,
VideoData
}
from
'
@/demo/atoms
'
;
import
{
useAtomValue
,
useSetAtom
}
from
'
jotai
'
;
import
{
ComponentType
,
useCallback
}
from
'
react
'
;
import
{
useNavigate
}
from
'
react-router-dom
'
;
type
Props
=
{
videoGalleryModalTrigger
?:
ComponentType
<
VideoGalleryTriggerProps
>
;
showUploadInGallery
?:
boolean
;
onChangeVideo
?:
()
=>
void
;
};
export
default
function
ChangeVideoModal
({
videoGalleryModalTrigger
:
VideoGalleryModalTriggerComponent
,
showUploadInGallery
=
true
,
onChangeVideo
,
}:
Props
)
{
const
isStreaming
=
useAtomValue
(
isStreamingAtom
);
const
setUploadingState
=
useSetAtom
(
uploadingStateAtom
);
const
video
=
useVideo
();
const
navigate
=
useNavigate
();
const
handlePause
=
useCallback
(()
=>
{
video
?.
pause
();
},
[
video
]);
function
handlePauseOrAbortVideo
()
{
if
(
isStreaming
)
{
video
?.
abortStreamMasks
();
}
else
{
handlePause
();
}
}
function
handleSwitchVideos
(
video
:
VideoData
)
{
// Retain any search parameter
navigate
(
{
pathname
:
location
.
pathname
,
search
:
location
.
search
,
},
{
state
:
{
video
,
},
},
);
onChangeVideo
?.();
}
function
handleUploadVideoError
(
error
:
Error
)
{
setUploadingState
(
'
error
'
);
Logger
.
error
(
error
);
}
return
(
<
DemoVideoGalleryModal
trigger
=
{
VideoGalleryModalTriggerComponent
}
showUploadInGallery
=
{
showUploadInGallery
}
onOpen
=
{
handlePauseOrAbortVideo
}
onSelect
=
{
handleSwitchVideos
}
onUploadVideoError
=
{
handleUploadVideoError
}
/>
);
}
demo/frontend/src/common/components/gallery/DefaultVideoGalleryModalTrigger.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
ResponsiveButton
from
'
@/common/components/button/ResponsiveButton
'
;
import
type
{
VideoGalleryTriggerProps
}
from
'
@/common/components/gallery/DemoVideoGalleryModal
'
;
import
{
ImageCopy
}
from
'
@carbon/icons-react
'
;
export
default
function
DefaultVideoGalleryModalTrigger
({
onClick
,
}:
VideoGalleryTriggerProps
)
{
return
(
<
ResponsiveButton
color
=
"ghost"
className
=
"hover:!bg-black"
startIcon
=
{
<
ImageCopy
size
=
{
20
}
/>
}
onClick
=
{
onClick
}
>
Change video
</
ResponsiveButton
>
);
}
demo/frontend/src/common/components/gallery/DemoVideoGallery.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
{
DemoVideoGalleryQuery
}
from
'
@/common/components/gallery/__generated__/DemoVideoGalleryQuery.graphql
'
;
import
VideoGalleryUploadVideo
from
'
@/common/components/gallery/VideoGalleryUploadPhoto
'
;
import
VideoPhoto
from
'
@/common/components/gallery/VideoPhoto
'
;
import
useScreenSize
from
'
@/common/screen/useScreenSize
'
;
import
{
VideoData
}
from
'
@/demo/atoms
'
;
import
{
DEMO_SHORT_NAME
}
from
'
@/demo/DemoConfig
'
;
import
{
fontSize
,
fontWeight
,
spacing
}
from
'
@/theme/tokens.stylex
'
;
import
stylex
from
'
@stylexjs/stylex
'
;
import
{
useMemo
}
from
'
react
'
;
import
PhotoAlbum
,
{
Photo
,
RenderPhotoProps
}
from
'
react-photo-album
'
;
import
{
graphql
,
useLazyLoadQuery
}
from
'
react-relay
'
;
import
{
useLocation
,
useNavigate
}
from
'
react-router-dom
'
;
const
styles
=
stylex
.
create
({
container
:
{
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
marginHorizontal
:
spacing
[
1
],
height
:
'
100%
'
,
lineHeight
:
1.2
,
paddingTop
:
spacing
[
8
],
},
headerContainer
:
{
marginBottom
:
spacing
[
8
],
fontWeight
:
fontWeight
[
'
medium
'
],
fontSize
:
fontSize
[
'
2xl
'
],
'
@media screen and (max-width: 768px)
'
:
{
marginTop
:
spacing
[
0
],
marginBottom
:
spacing
[
8
],
marginHorizontal
:
spacing
[
4
],
fontSize
:
fontSize
[
'
xl
'
],
},
},
albumContainer
:
{
flex
:
'
1 1 0%
'
,
width
:
'
100%
'
,
overflowY
:
'
auto
'
,
},
});
type
Props
=
{
showUploadInGallery
?:
boolean
;
onSelect
?:
(
video
:
VideoPhotoData
)
=>
void
;
onUpload
:
(
video
:
VideoData
)
=>
void
;
onUploadStart
?:
()
=>
void
;
onUploadError
?:
(
error
:
Error
)
=>
void
;
};
type
VideoPhotoData
=
Photo
&
VideoData
&
{
poster
:
string
;
isUploadOption
:
boolean
;
};
export
default
function
DemoVideoGallery
({
showUploadInGallery
=
false
,
onSelect
,
onUpload
,
onUploadStart
,
onUploadError
,
}:
Props
)
{
const
navigate
=
useNavigate
();
const
location
=
useLocation
();
const
{
isMobile
:
isMobileScreenSize
}
=
useScreenSize
();
const
data
=
useLazyLoadQuery
<
DemoVideoGalleryQuery
>
(
graphql
`
query DemoVideoGalleryQuery {
videos {
edges {
node {
id
path
posterPath
url
posterUrl
height
width
posterUrl
}
}
}
}
`
,
{},
);
const
allVideos
:
VideoPhotoData
[]
=
useMemo
(()
=>
{
return
data
.
videos
.
edges
.
map
(
video
=>
{
return
{
src
:
video
.
node
.
url
,
path
:
video
.
node
.
path
,
poster
:
video
.
node
.
posterPath
,
posterPath
:
video
.
node
.
posterPath
,
url
:
video
.
node
.
url
,
posterUrl
:
video
.
node
.
posterUrl
,
width
:
video
.
node
.
width
,
height
:
video
.
node
.
height
,
isUploadOption
:
false
,
}
as
VideoPhotoData
;
});
},
[
data
.
videos
.
edges
]);
const
shareableVideos
:
VideoPhotoData
[]
=
useMemo
(()
=>
{
const
filteredVideos
=
[...
allVideos
];
if
(
showUploadInGallery
)
{
const
uploadOption
=
{
src
:
''
,
width
:
1280
,
height
:
720
,
poster
:
''
,
isUploadOption
:
true
,
}
as
VideoPhotoData
;
filteredVideos
.
unshift
(
uploadOption
);
}
return
filteredVideos
;
},
[
allVideos
,
showUploadInGallery
]);
const
renderPhoto
=
({
photo
:
video
,
imageProps
,
}:
RenderPhotoProps
<
VideoPhotoData
>
)
=>
{
const
{
style
}
=
imageProps
;
const
{
url
,
posterUrl
}
=
video
;
return
video
.
isUploadOption
?
(
<
VideoGalleryUploadVideo
style
=
{
style
}
onUpload
=
{
handleUploadVideo
}
onUploadError
=
{
onUploadError
}
onUploadStart
=
{
onUploadStart
}
/>
)
:
(
<
VideoPhoto
src
=
{
url
}
poster
=
{
posterUrl
}
style
=
{
style
}
onClick
=
{
()
=>
{
navigate
(
location
.
pathname
,
{
state
:
{
video
,
},
});
onSelect
?.(
video
);
}
}
/>
);
};
function
handleUploadVideo
(
video
:
VideoData
)
{
navigate
(
location
.
pathname
,
{
state
:
{
video
,
},
});
onUpload
?.(
video
);
}
const
descriptionStyle
=
'
text-sm md:text-base text-gray-400 leading-snug
'
;
return
(
<
div
{
...
stylex
.
props
(
styles
.
container
)
}
>
<
div
{
...
stylex
.
props
(
styles
.
albumContainer
)
}
>
<
div
className
=
"pt-0 md:px-16 md:pt-8 md:pb-8"
>
<
div
{
...
stylex
.
props
(
styles
.
headerContainer
)
}
>
<
h3
className
=
"mb-2"
>
Select a video to try
{
'
'
}
<
span
className
=
"hidden md:inline"
>
with the
{
DEMO_SHORT_NAME
}
</
span
>
</
h3
>
<
p
className
=
{
descriptionStyle
}
>
You’ll be able to download what you make.
</
p
>
</
div
>
<
PhotoAlbum
<
VideoPhotoData
>
layout="rows"
photos=
{
shareableVideos
}
targetRowHeight=
{
isMobileScreenSize
?
120
:
200
}
rowConstraints=
{
{
singleRowMaxHeight
:
isMobileScreenSize
?
120
:
240
,
maxPhotos
:
3
,
}
}
renderPhoto=
{
renderPhoto
}
spacing=
{
4
}
/>
</
div
>
</
div
>
</
div
>
);
}
demo/frontend/src/common/components/gallery/DemoVideoGalleryModal.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
DefaultVideoGalleryModalTrigger
from
'
@/common/components/gallery/DefaultVideoGalleryModalTrigger
'
;
import
{
frameIndexAtom
,
sessionAtom
,
uploadingStateAtom
,
VideoData
,
}
from
'
@/demo/atoms
'
;
import
{
spacing
}
from
'
@/theme/tokens.stylex
'
;
import
{
Close
}
from
'
@carbon/icons-react
'
;
import
stylex
from
'
@stylexjs/stylex
'
;
import
{
useSetAtom
}
from
'
jotai
'
;
import
{
ComponentType
,
useCallback
,
useRef
}
from
'
react
'
;
import
{
Modal
}
from
'
react-daisyui
'
;
import
DemoVideoGallery
from
'
./DemoVideoGallery
'
;
const
styles
=
stylex
.
create
({
container
:
{
position
:
'
relative
'
,
minWidth
:
'
85vw
'
,
minHeight
:
'
85vh
'
,
overflow
:
'
hidden
'
,
color
:
'
#fff
'
,
boxShadow
:
'
0 0 100px 50px #000
'
,
borderRadius
:
16
,
border
:
'
2px solid transparent
'
,
background
:
'
linear-gradient(#1A1C1F, #1A1C1F) padding-box, linear-gradient(to right bottom, #FB73A5,#595FEF,#94EAE2,#FCCB6B) border-box
'
,
},
closeButton
:
{
position
:
'
absolute
'
,
top
:
0
,
right
:
0
,
padding
:
spacing
[
3
],
zIndex
:
10
,
cursor
:
'
pointer
'
,
'
:hover
'
:
{
opacity
:
0.7
,
},
},
galleryContainer
:
{
position
:
'
absolute
'
,
top
:
spacing
[
4
],
left
:
0
,
right
:
0
,
bottom
:
0
,
overflowY
:
'
auto
'
,
},
});
export
type
VideoGalleryTriggerProps
=
{
onClick
:
()
=>
void
;
};
type
Props
=
{
trigger
?:
ComponentType
<
VideoGalleryTriggerProps
>
;
showUploadInGallery
?:
boolean
;
onOpen
?:
()
=>
void
;
onSelect
?:
(
video
:
VideoData
,
isUpload
?:
boolean
)
=>
void
;
onUploadVideoError
?:
(
error
:
Error
)
=>
void
;
};
export
default
function
DemoVideoGalleryModal
({
trigger
:
VideoGalleryModalTrigger
=
DefaultVideoGalleryModalTrigger
,
showUploadInGallery
=
false
,
onOpen
,
onSelect
,
onUploadVideoError
,
}:
Props
)
{
const
modalRef
=
useRef
<
HTMLDialogElement
|
null
>
(
null
);
const
setFrameIndex
=
useSetAtom
(
frameIndexAtom
);
const
setUploadingState
=
useSetAtom
(
uploadingStateAtom
);
const
setSession
=
useSetAtom
(
sessionAtom
);
function
openModal
()
{
const
modal
=
modalRef
.
current
;
if
(
modal
!=
null
)
{
modal
.
style
.
display
=
'
grid
'
;
modal
.
showModal
();
}
}
function
closeModal
()
{
const
modal
=
modalRef
.
current
;
if
(
modal
!=
null
)
{
modal
.
close
();
modal
.
style
.
display
=
'
none
'
;
}
}
const
handleSelect
=
useCallback
(
async
(
video
:
VideoData
,
isUpload
?:
boolean
)
=>
{
closeModal
();
setFrameIndex
(
0
);
onSelect
?.(
video
,
isUpload
);
setUploadingState
(
'
default
'
);
setSession
(
null
);
},
[
setFrameIndex
,
onSelect
,
setUploadingState
,
setSession
],
);
function
handleUploadVideoStart
()
{
setUploadingState
(
'
uploading
'
);
closeModal
();
}
function
handleOpenVideoGalleryModal
()
{
onOpen
?.();
openModal
();
}
return
(
<>
<
VideoGalleryModalTrigger
onClick
=
{
handleOpenVideoGalleryModal
}
/>
<
Modal
ref
=
{
modalRef
}
{
...
stylex
.
props
(
styles
.
container
)
}
>
<
div
onClick
=
{
closeModal
}
{
...
stylex
.
props
(
styles
.
closeButton
)
}
>
<
Close
size
=
{
28
}
/>
</
div
>
<
Modal
.
Body
>
<
div
{
...
stylex
.
props
(
styles
.
galleryContainer
)
}
>
<
DemoVideoGallery
showUploadInGallery
=
{
showUploadInGallery
}
onSelect
=
{
video
=>
handleSelect
(
video
)
}
onUpload
=
{
video
=>
handleSelect
(
video
,
true
)
}
onUploadStart
=
{
handleUploadVideoStart
}
onUploadError
=
{
onUploadVideoError
}
/>
</
div
>
</
Modal
.
Body
>
</
Modal
>
</>
);
}
demo/frontend/src/common/components/gallery/VideoGalleryUploadPhoto.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
useUploadVideo
from
'
@/common/components/gallery/useUploadVideo
'
;
import
useScreenSize
from
'
@/common/screen/useScreenSize
'
;
import
{
VideoData
}
from
'
@/demo/atoms
'
;
import
{
MAX_UPLOAD_FILE_SIZE
}
from
'
@/demo/DemoConfig
'
;
import
{
BLUE_PINK_FILL_BR
}
from
'
@/theme/gradientStyle
'
;
import
{
RetryFailed
,
Upload
}
from
'
@carbon/icons-react
'
;
import
{
CSSProperties
,
ReactNode
}
from
'
react
'
;
import
{
Loading
}
from
'
react-daisyui
'
;
type
Props
=
{
style
:
CSSProperties
;
onUpload
:
(
video
:
VideoData
)
=>
void
;
onUploadStart
?:
()
=>
void
;
onUploadError
?:
(
error
:
Error
)
=>
void
;
};
export
default
function
VideoGalleryUploadVideo
({
style
,
onUpload
,
onUploadStart
,
onUploadError
,
}:
Props
)
{
const
{
getRootProps
,
getInputProps
,
isUploading
,
error
}
=
useUploadVideo
({
onUpload
,
onUploadStart
,
onUploadError
,
});
const
{
isMobile
}
=
useScreenSize
();
return
(
<
div
className
=
{
`cursor-pointer
${
BLUE_PINK_FILL_BR
}
`
}
style
=
{
style
}
>
<
span
{
...
getRootProps
()
}
>
<
input
{
...
getInputProps
()
}
/>
<
div
className
=
"relative w-full h-full"
>
<
div
className
=
"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
>
{
isUploading
&&
(
<
IconWrapper
icon
=
{
<
Loading
size
=
{
isMobile
?
'
md
'
:
'
lg
'
}
className
=
"text-white"
/>
}
title
=
"Uploading ..."
/>
)
}
{
error
!==
null
&&
(
<
IconWrapper
icon
=
{
<
RetryFailed
color
=
"white"
size
=
{
isMobile
?
24
:
32
}
/>
}
title
=
{
error
}
/>
)
}
{
!
isUploading
&&
error
===
null
&&
(
<
IconWrapper
icon
=
{
<
Upload
color
=
"white"
size
=
{
isMobile
?
24
:
32
}
/>
}
title
=
{
<>
Upload
{
'
'
}
<
div
className
=
"text-xs opacity-70"
>
Max
{
MAX_UPLOAD_FILE_SIZE
}
</
div
>
</>
}
/>
)
}
</
div
>
</
div
>
</
span
>
</
div
>
);
}
type
IconWrapperProps
=
{
icon
:
ReactNode
;
title
:
ReactNode
|
string
;
};
function
IconWrapper
({
icon
,
title
}:
IconWrapperProps
)
{
return
(
<>
<
div
className
=
"flex justify-center"
>
{
icon
}
</
div
>
<
div
className
=
"mt-1 text-sm md:text-lg text-white font-medium text-center leading-tight"
>
{
title
}
</
div
>
</>
);
}
demo/frontend/src/common/components/gallery/VideoPhoto.tsx
0 → 100644
View file @
17d316f3
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
Logger
from
'
@/common/logger/Logger
'
;
import
stylex
from
'
@stylexjs/stylex
'
;
import
{
CSSProperties
,
MouseEventHandler
,
useCallback
,
useEffect
,
useRef
,
}
from
'
react
'
;
const
styles
=
stylex
.
create
({
background
:
{
backgroundRepeat
:
'
no-repeat
'
,
backgroundSize
:
'
cover
'
,
backgroundPosition
:
'
center
'
,
cursor
:
'
pointer
'
,
},
video
:
{
width
:
'
100%
'
,
height
:
'
100%
'
,
},
});
type
Props
=
{
onClick
:
MouseEventHandler
<
HTMLVideoElement
>
|
undefined
;
src
:
string
;
poster
:
string
;
style
:
CSSProperties
;
};
export
default
function
VideoPhoto
({
src
,
poster
,
style
,
onClick
}:
Props
)
{
const
videoRef
=
useRef
<
HTMLVideoElement
|
null
>
(
null
);
const
playPromiseRef
=
useRef
<
Promise
<
void
>
|
null
>
(
null
);
const
play
=
useCallback
(()
=>
{
const
video
=
videoRef
.
current
;
// Only play video if it is not already playing
if
(
video
!=
null
&&
video
.
paused
)
{
// This quirky way of handling video play/pause in the browser is needed
// due to the async nature of the video play API:
// https://developer.chrome.com/blog/play-request-was-interrupted/
const
playPromise
=
video
.
play
();
playPromise
.
catch
(
error
=>
{
Logger
.
error
(
'
Failed to play video
'
,
error
);
});
playPromiseRef
.
current
=
playPromise
;
}
},
[]);
const
pause
=
useCallback
(()
=>
{
// Only pause video if it is playing
const
playPromise
=
playPromiseRef
.
current
;
if
(
playPromise
!=
null
)
{
playPromise
.
then
(()
=>
{
videoRef
.
current
?.
pause
();
})
.
catch
(
error
=>
{
Logger
.
error
(
'
Failed to pause video
'
,
error
);
})
.
finally
(()
=>
{
playPromiseRef
.
current
=
null
;
});
}
},
[]);
useEffect
(()
=>
{
return
()
=>
{
pause
();
};
},
[
pause
]);
return
(
<
div
style
=
{
{
...
style
,
backgroundImage
:
`url(
${
poster
}
)`
,
}
}
{
...
stylex
.
props
(
styles
.
background
)
}
>
<
video
ref
=
{
videoRef
}
{
...
stylex
.
props
(
styles
.
video
)
}
preload
=
"none"
playsInline
loop
muted
title
=
"Gallery Video"
poster
=
{
poster
}
onMouseEnter
=
{
play
}
onMouseLeave
=
{
pause
}
onClick
=
{
onClick
}
>
<
source
src
=
{
src
}
type
=
"video/mp4"
/>
Sorry, your browser does not support embedded videos.
</
video
>
</
div
>
);
}
demo/frontend/src/common/components/gallery/__generated__/DemoVideoGalleryModalQuery.graphql.ts
0 → 100644
View file @
17d316f3
/**
* @generated SignedSource<<db7e183e1996cf656749b4e33c2424e6>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import
{
ConcreteRequest
,
Query
}
from
'
relay-runtime
'
;
import
{
FragmentRefs
}
from
"
relay-runtime
"
;
export
type
DemoVideoGalleryModalQuery$variables
=
Record
<
PropertyKey
,
never
>
;
export
type
DemoVideoGalleryModalQuery$data
=
{
readonly
"
$fragmentSpreads
"
:
FragmentRefs
<
"
DatasetsDropdown_datasets
"
|
"
VideoGallery_videos
"
>
;
};
export
type
DemoVideoGalleryModalQuery
=
{
response
:
DemoVideoGalleryModalQuery$data
;
variables
:
DemoVideoGalleryModalQuery$variables
;
};
const
node
:
ConcreteRequest
=
(
function
(){
var
v0
=
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
name
"
,
"
storageKey
"
:
null
}
],
v1
=
[
{
"
kind
"
:
"
Literal
"
,
"
name
"
:
"
after
"
,
"
value
"
:
""
},
{
"
kind
"
:
"
Literal
"
,
"
name
"
:
"
first
"
,
"
value
"
:
20
}
],
v2
=
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
__typename
"
,
"
storageKey
"
:
null
};
return
{
"
fragment
"
:
{
"
argumentDefinitions
"
:
[],
"
kind
"
:
"
Fragment
"
,
"
metadata
"
:
null
,
"
name
"
:
"
DemoVideoGalleryModalQuery
"
,
"
selections
"
:
[
{
"
args
"
:
null
,
"
kind
"
:
"
FragmentSpread
"
,
"
name
"
:
"
DatasetsDropdown_datasets
"
},
{
"
args
"
:
null
,
"
kind
"
:
"
FragmentSpread
"
,
"
name
"
:
"
VideoGallery_videos
"
}
],
"
type
"
:
"
Query
"
,
"
abstractKey
"
:
null
},
"
kind
"
:
"
Request
"
,
"
operation
"
:
{
"
argumentDefinitions
"
:
[],
"
kind
"
:
"
Operation
"
,
"
name
"
:
"
DemoVideoGalleryModalQuery
"
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
DatasetConnection
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
datasets
"
,
"
plural
"
:
false
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
DatasetEdge
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
edges
"
,
"
plural
"
:
true
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
Dataset
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
node
"
,
"
plural
"
:
false
,
"
selections
"
:
(
v0
/*: any*/
),
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
(
v1
/*: any*/
),
"
concreteType
"
:
"
VideoConnection
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
videos
"
,
"
plural
"
:
false
,
"
selections
"
:
[
(
v2
/*: any*/
),
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
PageInfo
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
pageInfo
"
,
"
plural
"
:
false
,
"
selections
"
:
[
(
v2
/*: any*/
),
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
hasPreviousPage
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
hasNextPage
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
startCursor
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
endCursor
"
,
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
VideoEdge
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
edges
"
,
"
plural
"
:
true
,
"
selections
"
:
[
(
v2
/*: any*/
),
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
Video
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
node
"
,
"
plural
"
:
false
,
"
selections
"
:
[
(
v2
/*: any*/
),
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
id
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
path
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
posterPath
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
url
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
posterUrl
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
width
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
height
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
Dataset
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
dataset
"
,
"
plural
"
:
false
,
"
selections
"
:
(
v0
/*: any*/
),
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
VideoPermissions
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
permissions
"
,
"
plural
"
:
false
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
canShare
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
canDownload
"
,
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
cursor
"
,
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
],
"
storageKey
"
:
"
videos(after:
\"\"
,first:20)
"
},
{
"
alias
"
:
null
,
"
args
"
:
(
v1
/*: any*/
),
"
filters
"
:
[
"
datasetName
"
],
"
handle
"
:
"
connection
"
,
"
key
"
:
"
VideoGallery_videos
"
,
"
kind
"
:
"
LinkedHandle
"
,
"
name
"
:
"
videos
"
}
]
},
"
params
"
:
{
"
cacheID
"
:
"
e0bccf553377682e6bc283c2ce53bee5
"
,
"
id
"
:
null
,
"
metadata
"
:
{},
"
name
"
:
"
DemoVideoGalleryModalQuery
"
,
"
operationKind
"
:
"
query
"
,
"
text
"
:
"
query DemoVideoGalleryModalQuery {
\n
...DatasetsDropdown_datasets
\n
...VideoGallery_videos
\n
}
\n\n
fragment DatasetsDropdown_datasets on Query {
\n
datasets {
\n
edges {
\n
node {
\n
name
\n
}
\n
}
\n
}
\n
}
\n\n
fragment VideoGallery_videos on Query {
\n
videos(first: 20, after:
\"\"
) {
\n
__typename
\n
pageInfo {
\n
__typename
\n
hasPreviousPage
\n
hasNextPage
\n
startCursor
\n
endCursor
\n
}
\n
edges {
\n
__typename
\n
node {
\n
__typename
\n
id
\n
path
\n
posterPath
\n
url
\n
posterUrl
\n
width
\n
height
\n
dataset {
\n
name
\n
}
\n
permissions {
\n
canShare
\n
canDownload
\n
}
\n
}
\n
cursor
\n
}
\n
}
\n
}
\n
"
}
};
})();
(
node
as
any
).
hash
=
"
d09e34e2b9f2e25c2d564106de5f9c89
"
;
export
default
node
;
demo/frontend/src/common/components/gallery/__generated__/DemoVideoGalleryQuery.graphql.ts
0 → 100644
View file @
17d316f3
/**
* @generated SignedSource<<20d31a82b5f3b251b0e42b4f0e3522b8>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import
{
ConcreteRequest
,
Query
}
from
'
relay-runtime
'
;
export
type
DemoVideoGalleryQuery$variables
=
Record
<
PropertyKey
,
never
>
;
export
type
DemoVideoGalleryQuery$data
=
{
readonly
videos
:
{
readonly
edges
:
ReadonlyArray
<
{
readonly
node
:
{
readonly
height
:
number
;
readonly
id
:
any
;
readonly
path
:
string
;
readonly
posterPath
:
string
|
null
|
undefined
;
readonly
posterUrl
:
string
;
readonly
url
:
string
;
readonly
width
:
number
;
};
}
>
;
};
};
export
type
DemoVideoGalleryQuery
=
{
response
:
DemoVideoGalleryQuery$data
;
variables
:
DemoVideoGalleryQuery$variables
;
};
const
node
:
ConcreteRequest
=
(
function
(){
var
v0
=
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
VideoConnection
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
videos
"
,
"
plural
"
:
false
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
VideoEdge
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
edges
"
,
"
plural
"
:
true
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
concreteType
"
:
"
Video
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
node
"
,
"
plural
"
:
false
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
id
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
path
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
posterPath
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
url
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
posterUrl
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
height
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
width
"
,
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
];
return
{
"
fragment
"
:
{
"
argumentDefinitions
"
:
[],
"
kind
"
:
"
Fragment
"
,
"
metadata
"
:
null
,
"
name
"
:
"
DemoVideoGalleryQuery
"
,
"
selections
"
:
(
v0
/*: any*/
),
"
type
"
:
"
Query
"
,
"
abstractKey
"
:
null
},
"
kind
"
:
"
Request
"
,
"
operation
"
:
{
"
argumentDefinitions
"
:
[],
"
kind
"
:
"
Operation
"
,
"
name
"
:
"
DemoVideoGalleryQuery
"
,
"
selections
"
:
(
v0
/*: any*/
)
},
"
params
"
:
{
"
cacheID
"
:
"
4dae74153a5528f2631b59dfb0adb021
"
,
"
id
"
:
null
,
"
metadata
"
:
{},
"
name
"
:
"
DemoVideoGalleryQuery
"
,
"
operationKind
"
:
"
query
"
,
"
text
"
:
"
query DemoVideoGalleryQuery {
\n
videos {
\n
edges {
\n
node {
\n
id
\n
path
\n
posterPath
\n
url
\n
posterUrl
\n
height
\n
width
\n
}
\n
}
\n
}
\n
}
\n
"
}
};
})();
(
node
as
any
).
hash
=
"
d22ac5e58f6e4eb696651be49b410e4e
"
;
export
default
node
;
demo/frontend/src/common/components/gallery/__generated__/useUploadVideoMutation.graphql.ts
0 → 100644
View file @
17d316f3
/**
* @generated SignedSource<<76014dced98d6c8989e7322712e38963>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import
{
ConcreteRequest
,
Mutation
}
from
'
relay-runtime
'
;
export
type
useUploadVideoMutation$variables
=
{
file
:
any
;
};
export
type
useUploadVideoMutation$data
=
{
readonly
uploadVideo
:
{
readonly
height
:
number
;
readonly
id
:
any
;
readonly
path
:
string
;
readonly
posterPath
:
string
|
null
|
undefined
;
readonly
posterUrl
:
string
;
readonly
url
:
string
;
readonly
width
:
number
;
};
};
export
type
useUploadVideoMutation
=
{
response
:
useUploadVideoMutation$data
;
variables
:
useUploadVideoMutation$variables
;
};
const
node
:
ConcreteRequest
=
(
function
(){
var
v0
=
[
{
"
defaultValue
"
:
null
,
"
kind
"
:
"
LocalArgument
"
,
"
name
"
:
"
file
"
}
],
v1
=
[
{
"
alias
"
:
null
,
"
args
"
:
[
{
"
kind
"
:
"
Variable
"
,
"
name
"
:
"
file
"
,
"
variableName
"
:
"
file
"
}
],
"
concreteType
"
:
"
Video
"
,
"
kind
"
:
"
LinkedField
"
,
"
name
"
:
"
uploadVideo
"
,
"
plural
"
:
false
,
"
selections
"
:
[
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
id
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
height
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
width
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
url
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
path
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
posterPath
"
,
"
storageKey
"
:
null
},
{
"
alias
"
:
null
,
"
args
"
:
null
,
"
kind
"
:
"
ScalarField
"
,
"
name
"
:
"
posterUrl
"
,
"
storageKey
"
:
null
}
],
"
storageKey
"
:
null
}
];
return
{
"
fragment
"
:
{
"
argumentDefinitions
"
:
(
v0
/*: any*/
),
"
kind
"
:
"
Fragment
"
,
"
metadata
"
:
null
,
"
name
"
:
"
useUploadVideoMutation
"
,
"
selections
"
:
(
v1
/*: any*/
),
"
type
"
:
"
Mutation
"
,
"
abstractKey
"
:
null
},
"
kind
"
:
"
Request
"
,
"
operation
"
:
{
"
argumentDefinitions
"
:
(
v0
/*: any*/
),
"
kind
"
:
"
Operation
"
,
"
name
"
:
"
useUploadVideoMutation
"
,
"
selections
"
:
(
v1
/*: any*/
)
},
"
params
"
:
{
"
cacheID
"
:
"
dcbaf1bf411627fdb9dfbb827592cfc0
"
,
"
id
"
:
null
,
"
metadata
"
:
{},
"
name
"
:
"
useUploadVideoMutation
"
,
"
operationKind
"
:
"
mutation
"
,
"
text
"
:
"
mutation useUploadVideoMutation(
\n
$file: Upload!
\n
) {
\n
uploadVideo(file: $file) {
\n
id
\n
height
\n
width
\n
url
\n
path
\n
posterPath
\n
posterUrl
\n
}
\n
}
\n
"
}
};
})();
(
node
as
any
).
hash
=
"
710e462504d76597af8695b7fc70b4cf
"
;
export
default
node
;
Prev
1
…
21
22
23
24
25
26
27
28
29
…
48
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment