1
- import { Button , Divider , Flex , Icon , Link , Spinner , Text } from '@invoke-ai/ui-library' ;
1
+ import {
2
+ Alert ,
3
+ AlertDescription ,
4
+ AlertIcon ,
5
+ Button ,
6
+ Divider ,
7
+ Flex ,
8
+ Icon ,
9
+ Link ,
10
+ Spinner ,
11
+ Text ,
12
+ } from '@invoke-ai/ui-library' ;
2
13
import { useAppDispatch , useAppSelector } from 'app/store/storeHooks' ;
3
14
import { IAINoContentFallback } from 'common/components/IAIImageFallback' ;
4
15
import { InvokeLogoIcon } from 'common/components/InvokeLogoIcon' ;
@@ -13,47 +24,23 @@ import { Trans, useTranslation } from 'react-i18next';
13
24
import { PiArrowSquareOutBold , PiImageBold } from 'react-icons/pi' ;
14
25
import { useMainModels } from 'services/api/hooks/modelsByType' ;
15
26
16
- const ExternalLink = ( props : PropsWithChildren < { href : string } > ) => {
17
- return (
18
- < Link isExternal display = "inline-flex" alignItems = "center" href = { props . href } color = "base.50" >
19
- { props . children }
20
- < Icon display = "inline" verticalAlign = "middle" marginInlineStart = { 2 } as = { PiArrowSquareOutBold } />
21
- </ Link >
22
- ) ;
23
- } ;
24
-
25
27
export const NoContentForViewer = memo ( ( ) => {
26
28
const hasImages = useHasImages ( ) ;
27
29
const [ mainModels , { data } ] = useMainModels ( ) ;
28
30
const isLocal = useAppSelector ( selectIsLocal ) ;
29
31
const isEnabled = useFeatureStatus ( 'starterModels' ) ;
30
32
const { t } = useTranslation ( ) ;
31
- const dispatch = useAppDispatch ( ) ;
32
-
33
- const handleClickDownloadStarterModels = useCallback ( ( ) => {
34
- dispatch ( setActiveTab ( 'models' ) ) ;
35
- $installModelsTab . set ( 3 ) ;
36
- } , [ dispatch ] ) ;
37
-
38
- const handleClickImportModels = useCallback ( ( ) => {
39
- dispatch ( setActiveTab ( 'models' ) ) ;
40
- $installModelsTab . set ( 0 ) ;
41
- } , [ dispatch ] ) ;
42
33
43
34
const showStarterBundles = useMemo ( ( ) => {
44
35
return isEnabled && data && mainModels . length === 0 ;
45
36
} , [ mainModels . length , data , isEnabled ] ) ;
46
37
47
38
if ( hasImages === LOADING_SYMBOL ) {
48
- return (
49
- // Blank bg w/ a spinner. The new user experience components below have an invoke logo, but it's not centered.
50
- // If we show the logo while loading, there is an awkward layout shift where the invoke logo moves a bit. Less
51
- // jarring to show a blank bg with a spinner - it will only be shown for a moment as we do the initial images
52
- // fetching.
53
- < Flex position = "relative" width = "full" height = "full" alignItems = "center" justifyContent = "center" >
54
- < Spinner label = "Loading" color = "grey" position = "absolute" size = "sm" width = { 8 } height = { 8 } right = { 4 } bottom = { 4 } />
55
- </ Flex >
56
- ) ;
39
+ // Blank bg w/ a spinner. The new user experience components below have an invoke logo, but it's not centered.
40
+ // If we show the logo while loading, there is an awkward layout shift where the invoke logo moves a bit. Less
41
+ // jarring to show a blank bg with a spinner - it will only be shown for a moment as we do the initial images
42
+ // fetching.
43
+ return < LoadingSpinner /> ;
57
44
}
58
45
59
46
if ( hasImages ) {
@@ -63,71 +50,123 @@ export const NoContentForViewer = memo(() => {
63
50
return (
64
51
< Flex flexDir = "column" gap = { 8 } alignItems = "center" textAlign = "center" maxW = "600px" >
65
52
< InvokeLogoIcon w = { 32 } h = { 32 } />
66
- < Flex flexDir = "column" gap = { 8 } alignItems = "center" textAlign = "center" >
67
- { isLocal ? (
68
- < >
69
- < Text fontSize = "md" color = "base.200" >
70
- < Trans
71
- i18nKey = "newUserExperience.toGetStartedLocal"
72
- components = { {
73
- StrongComponent : < Text as = "span" color = "white" fontSize = "md" fontWeight = "semibold" /> ,
74
- } }
75
- />
76
- </ Text >
77
- < Text fontSize = "md" color = "base.200" >
78
- < Trans
79
- i18nKey = "newUserExperience.lowVRAMMode"
80
- components = { {
81
- LinkComponent : < ExternalLink href = "https://invoke-ai.github.io/InvokeAI/features/low-vram/" /> ,
82
- } }
83
- />
84
- </ Text >
85
- </ >
86
- ) : (
87
- < Text fontSize = "md" color = "base.200" >
88
- < Trans
89
- i18nKey = "newUserExperience.toGetStarted"
90
- components = { {
91
- StrongComponent : < Text as = "span" color = "white" fontSize = "md" fontWeight = "semibold" /> ,
92
- } }
93
- />
94
- </ Text >
95
- ) }
96
-
97
- { showStarterBundles && (
98
- < Flex flexDir = "column" gap = { 2 } alignItems = "center" >
99
- < Text fontSize = "md" color = "base.200" >
100
- { t ( 'newUserExperience.noModelsInstalled' ) }
101
- </ Text >
102
- < Flex gap = { 3 } alignItems = "center" >
103
- < Button size = "sm" onClick = { handleClickDownloadStarterModels } >
104
- { t ( 'newUserExperience.downloadStarterModels' ) }
105
- </ Button >
106
- < Text fontSize = "sm" color = "base.200" >
107
- { t ( 'common.or' ) }
108
- </ Text >
109
- < Button size = "sm" onClick = { handleClickImportModels } >
110
- { t ( 'newUserExperience.importModels' ) }
111
- </ Button >
112
- </ Flex >
113
- </ Flex >
114
- ) }
115
-
53
+ < Flex flexDir = "column" gap = { 4 } alignItems = "center" textAlign = "center" >
54
+ { isLocal ? < GetStartedLocal /> : < GetStartedCommercial /> }
55
+ { showStarterBundles && < StarterBundlesCallout /> }
116
56
< Divider />
117
-
118
- < Text fontSize = "md" color = "base.200" >
119
- < Trans
120
- i18nKey = "newUserExperience.gettingStartedSeries"
121
- components = { {
122
- LinkComponent : (
123
- < ExternalLink href = "https://www.youtube.com/playlist?list=PLvWK1Kc8iXGrQy8r9TYg6QdUuJ5MMx-ZO" />
124
- ) ,
125
- } }
126
- />
127
- </ Text >
57
+ < GettingStartedVideosCallout />
58
+ { isLocal && < LowVRAMAlert /> }
128
59
</ Flex >
129
60
</ Flex >
130
61
) ;
131
62
} ) ;
132
63
133
64
NoContentForViewer . displayName = 'NoContentForViewer' ;
65
+
66
+ const LoadingSpinner = ( ) => {
67
+ return (
68
+ < Flex position = "relative" width = "full" height = "full" alignItems = "center" justifyContent = "center" >
69
+ < Spinner label = "Loading" color = "grey" position = "absolute" size = "sm" width = { 8 } height = { 8 } right = { 4 } bottom = { 4 } />
70
+ </ Flex >
71
+ ) ;
72
+ } ;
73
+
74
+ const ExternalLink = ( props : PropsWithChildren < { href : string } > ) => {
75
+ return (
76
+ < Button
77
+ as = { Link }
78
+ variant = "link"
79
+ isExternal
80
+ display = "inline-flex"
81
+ alignItems = "center"
82
+ href = { props . href }
83
+ color = "base.50"
84
+ >
85
+ { props . children }
86
+ < Icon display = "inline" verticalAlign = "middle" marginInlineStart = { 2 } as = { PiArrowSquareOutBold } />
87
+ </ Button >
88
+ ) ;
89
+ } ;
90
+
91
+ const InlineButton = ( props : PropsWithChildren < { onClick : ( ) => void } > ) => {
92
+ return (
93
+ < Button variant = "link" size = "md" onClick = { props . onClick } color = "base.50" >
94
+ { props . children }
95
+ </ Button >
96
+ ) ;
97
+ } ;
98
+
99
+ const StrongComponent = < Text as = "span" color = "base.50" fontSize = "md" /> ;
100
+
101
+ const GetStartedLocal = ( ) => {
102
+ return (
103
+ < Text fontSize = "md" color = "base.200" >
104
+ < Trans i18nKey = "newUserExperience.toGetStartedLocal" components = { { StrongComponent } } />
105
+ </ Text >
106
+ ) ;
107
+ } ;
108
+
109
+ const GetStartedCommercial = ( ) => {
110
+ return (
111
+ < Text fontSize = "md" color = "base.200" >
112
+ < Trans i18nKey = "newUserExperience.toGetStarted" components = { { StrongComponent } } />
113
+ </ Text >
114
+ ) ;
115
+ } ;
116
+
117
+ const GettingStartedVideosCallout = ( ) => {
118
+ return (
119
+ < Text fontSize = "md" color = "base.200" >
120
+ < Trans
121
+ i18nKey = "newUserExperience.gettingStartedSeries"
122
+ components = { {
123
+ LinkComponent : (
124
+ < ExternalLink href = "https://www.youtube.com/playlist?list=PLvWK1Kc8iXGrQy8r9TYg6QdUuJ5MMx-ZO" />
125
+ ) ,
126
+ } }
127
+ />
128
+ </ Text >
129
+ ) ;
130
+ } ;
131
+
132
+ const StarterBundlesCallout = ( ) => {
133
+ const dispatch = useAppDispatch ( ) ;
134
+
135
+ const handleClickDownloadStarterModels = useCallback ( ( ) => {
136
+ dispatch ( setActiveTab ( 'models' ) ) ;
137
+ $installModelsTab . set ( 3 ) ;
138
+ } , [ dispatch ] ) ;
139
+
140
+ const handleClickImportModels = useCallback ( ( ) => {
141
+ dispatch ( setActiveTab ( 'models' ) ) ;
142
+ $installModelsTab . set ( 0 ) ;
143
+ } , [ dispatch ] ) ;
144
+
145
+ return (
146
+ < Text fontSize = "md" color = "base.200" >
147
+ < Trans
148
+ i18nKey = "newUserExperience.noModelsInstalled"
149
+ components = { {
150
+ DownloadStarterModelsButton : < InlineButton onClick = { handleClickDownloadStarterModels } /> ,
151
+ ImportModelsButton : < InlineButton onClick = { handleClickImportModels } /> ,
152
+ } }
153
+ />
154
+ </ Text >
155
+ ) ;
156
+ } ;
157
+
158
+ const LowVRAMAlert = ( ) => {
159
+ return (
160
+ < Alert status = "warning" borderRadius = "base" fontSize = "md" shadow = "md" w = "fit-content" >
161
+ < AlertIcon />
162
+ < AlertDescription >
163
+ < Trans
164
+ i18nKey = "newUserExperience.lowVRAMMode"
165
+ components = { {
166
+ LinkComponent : < ExternalLink href = "https://invoke-ai.github.io/InvokeAI/features/low-vram/" /> ,
167
+ } }
168
+ />
169
+ </ AlertDescription >
170
+ </ Alert >
171
+ ) ;
172
+ } ;
0 commit comments