Skip to content

Commit 921904c

Browse files
committed
[General] UX improvements in loading screens
1 parent d09eb6a commit 921904c

File tree

15 files changed

+170
-89
lines changed

15 files changed

+170
-89
lines changed

public/locales/en/translation.json

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
},
143143
"Library": "Library",
144144
"loading": {
145+
"default": "Loading",
145146
"website": "Loading Website"
146147
},
147148
"login": {

src/App.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ html,
2929
overflow-x: hidden;
3030
}
3131

32-
.App .content .updateIcon {
32+
.App .content .UpdateComponent--topLevel {
3333
height: 100vh;
3434
position: initial;
3535
width: var(--content-width);

src/components/UI/ToggleSwitch/index.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ export default function ToggleSwitch(props: Props) {
1717
title,
1818
dataTestId = 'toggleSwitch'
1919
} = props
20+
// TODO fixes errors in the console, but the props may not be necessary at all
21+
const checkmarkProps = {
22+
value,
23+
title,
24+
datatestid: dataTestId
25+
}
2026
return (
2127
<div className="switch" aria-label={title}>
2228
<input
@@ -28,7 +34,7 @@ export default function ToggleSwitch(props: Props) {
2834
aria-label={title}
2935
/>
3036

31-
<span {...props} className="checkmark" />
37+
<span {...checkmarkProps} className="checkmark" />
3238
</div>
3339
)
3440
}

src/components/UI/UpdateComponent/index.css

+25-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1-
.updateIcon {
2-
display: flex;
3-
align-items: center;
1+
.UpdateComponent {
42
width: 100%;
53
height: 100%;
6-
position: absolute;
74
font-size: 24px;
5+
color: var(--text-default);
6+
}
7+
8+
.UpdateComponent--topLevel {
9+
display: flex;
10+
align-items: center;
11+
position: absolute;
812
justify-content: center;
913
flex-direction: column;
10-
color: var(--text-default);
1114
}
1215

13-
.updateIcon .icon {
16+
.UpdateComponent__wrapper {
17+
position: sticky;
18+
top: 0;
19+
z-index: 10;
20+
width: 100%;
21+
display: flex;
22+
justify-content: center;
23+
flex-direction: column;
24+
align-items: center;
25+
}
26+
27+
.UpdateComponent--inline .UpdateComponent__wrapper {
28+
height: 100%;
29+
min-height: 100px;
30+
}
31+
32+
.UpdateComponent__icon {
1433
font-size: 40px;
1534
animation: refreshing 1.5s infinite;
1635
}
+65-10
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,73 @@
1-
import './index.css'
2-
3-
import React from 'react'
4-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
51
import { faSyncAlt } from '@fortawesome/free-solid-svg-icons'
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3+
import cx from 'classnames'
4+
import React, { useEffect, useMemo, useRef } from 'react'
5+
import './index.css'
66

7-
type Props = {
8-
message?: string
7+
export interface UpdateComponentProps {
8+
message: string
9+
inline?: boolean
910
}
1011

11-
export default function UpdateComponent({ message = 'Loading' }: Props) {
12+
export default function UpdateComponent({
13+
message,
14+
inline = false
15+
}: UpdateComponentProps) {
16+
const componentRef = useRef<HTMLDivElement>(null)
17+
const wrapperRef = useRef<HTMLDivElement>(null)
18+
const observer = useMemo(() => {
19+
return new IntersectionObserver(
20+
(entries) => {
21+
if (wrapperRef.current) {
22+
for (const entry of entries) {
23+
const ratio =
24+
entry.intersectionRect.height / entry.boundingClientRect.height
25+
wrapperRef.current.style.height = `${100 * ratio}%`
26+
}
27+
}
28+
},
29+
{
30+
threshold: Array(100)
31+
.fill(0)
32+
.map((_, i) => (i + 1) / 100)
33+
}
34+
)
35+
}, [wrapperRef.current])
36+
37+
useEffect(() => {
38+
return () => observer.disconnect()
39+
}, [observer])
40+
41+
useEffect(() => {
42+
if (inline) {
43+
const component = componentRef.current
44+
if (component) {
45+
observer.observe(component)
46+
return () => {
47+
observer.unobserve(component)
48+
}
49+
}
50+
}
51+
return
52+
}, [inline, observer, componentRef.current])
53+
1254
return (
13-
<div className="updateIcon" data-testid="updateComponent">
14-
<FontAwesomeIcon className="icon" icon={faSyncAlt} />
15-
<span>{message}</span>
55+
<div
56+
className={cx('UpdateComponent', {
57+
'UpdateComponent--inline': inline,
58+
'UpdateComponent--topLevel': !inline
59+
})}
60+
data-testid="updateComponent"
61+
ref={componentRef}
62+
>
63+
<div
64+
className="UpdateComponent__wrapper"
65+
data-testid="updateComponent"
66+
ref={wrapperRef}
67+
>
68+
<FontAwesomeIcon className="UpdateComponent__icon" icon={faSyncAlt} />
69+
<div>{message}</div>
70+
</div>
1671
</div>
1772
)
1873
}

src/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ i18next
7373
ReactDOM.render(
7474
<React.StrictMode>
7575
<I18nextProvider i18n={i18next}>
76-
<Suspense fallback={<UpdateComponent />}>
76+
<Suspense fallback={<UpdateComponent message="Loading" />}>
7777
<GlobalState>
7878
<App />
7979
</GlobalState>

src/screens/Game/GamePage/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,12 @@ export default function GamePage(): JSX.Element | null {
422422
</div>
423423
</>
424424
) : (
425-
<UpdateComponent />
425+
<UpdateComponent message={t('loading.default')} />
426426
)}
427427
</div>
428428
)
429429
}
430-
return <UpdateComponent />
430+
return <UpdateComponent message={t('loading.default')} />
431431

432432
function getPlayBtnClass() {
433433
if (isUpdating) {

src/screens/Library/components/InstallModal/index.css

-4
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,6 @@
110110
margin: 0 10px;
111111
}
112112

113-
.modalContainer > .updateIcon > svg {
114-
z-index: 10;
115-
}
116-
117113
.installPath {
118114
display: flex;
119115
flex-direction: column;

src/screens/Library/components/InstallModal/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ export default function InstallModal({
392392
</div>
393393
</div>
394394
) : (
395-
<UpdateComponent />
395+
<UpdateComponent message={t('loading.default')} />
396396
)}
397397
<span className="backdrop" onClick={() => backdropClick()} />
398398
</span>

src/screens/Library/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const Library = ({ library, showRecentsOnly }: Props) => {
6161
}
6262

6363
if (refreshing && !showRecentsOnly) {
64-
return <UpdateComponent />
64+
return <UpdateComponent inline message={t('loading.default')} />
6565
}
6666

6767
function titleWithIcons() {

src/screens/Login/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default function NewLogin() {
7272
<div className="loginPage">
7373
{loading && (
7474
<div>
75-
<UpdateComponent />
75+
<UpdateComponent message={t('loading.default')} />
7676
</div>
7777
)}
7878
{showSidLogin && (

src/screens/Settings/components/LogSettings/index.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
border-radius: 10px;
2525
padding-block: 4px;
2626
padding-inline: 6px;
27-
max-height: 25em;
28-
max-width: 622px;
27+
height: 25em;
28+
width: 622px;
2929
overflow: auto;
3030
}
3131

src/screens/Settings/components/LogSettings/index.tsx

+61-57
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,69 @@
1-
import './index.css'
1+
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
2+
import FolderOpenIcon from '@mui/icons-material/FolderOpen'
23
import React, { useEffect, useState } from 'react'
34
import { useTranslation } from 'react-i18next'
4-
import FolderOpenIcon from '@mui/icons-material/FolderOpen'
5-
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
6-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
7-
import { faSyncAlt } from '@fortawesome/free-solid-svg-icons'
5+
import { UpdateComponent } from '../../../../components/UI'
6+
import './index.css'
87

98
const { ipcRenderer } = window.require('electron')
109

11-
interface Props {
10+
interface LogBoxProps {
11+
logFileContent: string
12+
}
13+
14+
const LogBox: React.FC<LogBoxProps> = ({ logFileContent }) => {
15+
const { t } = useTranslation()
16+
const maxLines = 1000
17+
let sliced = false
18+
let lines = logFileContent.split('\n')
19+
if (lines.length > maxLines) {
20+
lines = ['...', ...lines.slice(-maxLines)]
21+
sliced = true
22+
}
23+
24+
return (
25+
<>
26+
{sliced && (
27+
<span className="setting long-log-hint">
28+
{t(
29+
'settings.log.long-log-hint',
30+
'Log truncated, last 1000 lines are shown!'
31+
)}
32+
</span>
33+
)}
34+
<span className="setting log-box">
35+
{lines.map((line, key) => {
36+
if (line.toLowerCase().includes(' err')) {
37+
return (
38+
<p key={key} className="log-error">
39+
{line}
40+
</p>
41+
)
42+
} else if (line.toLowerCase().includes(' warn')) {
43+
return (
44+
<p key={key} className="log-warning">
45+
{line}
46+
</p>
47+
)
48+
} else {
49+
return (
50+
<p key={key} className="log-info">
51+
{line}
52+
</p>
53+
)
54+
}
55+
})}
56+
</span>
57+
</>
58+
)
59+
}
60+
61+
export interface LogSettingsProps {
1262
isDefault: boolean
1363
appName: string
1464
}
1565

16-
export default function LogSettings({ isDefault, appName }: Props) {
66+
export default function LogSettings({ isDefault, appName }: LogSettingsProps) {
1767
const { t } = useTranslation()
1868
const [logFileContent, setLogFileContent] = useState<string>('')
1969
const [logFileExist, setLogFileExist] = useState<boolean>(false)
@@ -46,7 +96,7 @@ export default function LogSettings({ isDefault, appName }: Props) {
4696
}, 1000)
4797
return () => clearInterval(interval)
4898
}
49-
}, [defaultLast])
99+
}, [isDefault, defaultLast])
50100

51101
function showLogFileInFolder() {
52102
ipcRenderer.send('showLogFileInFolder', { isDefault, appName })
@@ -79,11 +129,11 @@ export default function LogSettings({ isDefault, appName }: Props) {
79129
</span>
80130
)}
81131
{refreshing ? (
82-
<span className="setting">
83-
<FontAwesomeIcon className="icon" icon={faSyncAlt} />
132+
<span className="setting log-box">
133+
<UpdateComponent inline message={t('loading.default')} />
84134
</span>
85135
) : (
86-
formatLogBox()
136+
<LogBox logFileContent={logFileContent} />
87137
)}
88138
{logFileExist && (
89139
<span className="footerFlex">
@@ -127,50 +177,4 @@ export default function LogSettings({ isDefault, appName }: Props) {
127177
)}
128178
</>
129179
)
130-
131-
function formatLogBox() {
132-
const maxLines = 1000
133-
let sliced = false
134-
let lines = logFileContent.split('\n')
135-
if (lines.length > maxLines) {
136-
lines = ['...', ...lines.slice(-maxLines)]
137-
sliced = true
138-
}
139-
140-
return (
141-
<>
142-
{sliced && (
143-
<span className="setting long-log-hint">
144-
{t(
145-
'settings.log.long-log-hint',
146-
'Log truncated, last 1000 lines are shown!'
147-
)}
148-
</span>
149-
)}
150-
<span className="setting log-box">
151-
{lines.map((line, key) => {
152-
if (line.toLowerCase().includes(' err')) {
153-
return (
154-
<p key={key} className="log-error">
155-
{line}
156-
</p>
157-
)
158-
} else if (line.toLowerCase().includes(' warn')) {
159-
return (
160-
<p key={key} className="log-warning">
161-
{line}
162-
</p>
163-
)
164-
} else {
165-
return (
166-
<p key={key} className="log-info">
167-
{line}
168-
</p>
169-
)
170-
}
171-
})}
172-
</span>
173-
</>
174-
)
175-
}
176180
}

src/screens/Settings/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ function Settings() {
361361
}, [isCopiedToClipboard])
362362

363363
if (!title) {
364-
return <UpdateComponent />
364+
return <UpdateComponent message={t('loading.default')} />
365365
}
366366

367367
return (

0 commit comments

Comments
 (0)