Skip to content

Commit 9f4dd03

Browse files
authored
SMM2->3 migration modal, new user modal, disk space warning, markdown format annoucements (#224)
1 parent 35e7fc2 commit 9f4dd03

File tree

14 files changed

+444
-14
lines changed

14 files changed

+444
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Mod manager for [Satisfactory](https://www.satisfactorygame.com/).
44
Handles all the steps of installing mods for you.
55

6-
Implemented in [Wails](https://wails.io/).
6+
Implemented in [Wails](https://wails.io/) using [Svelte](https://svelte.dev/) and [Skeleton](https://www.skeleton.dev/).
77

88
## Installation and Usage
99

backend/migration/migration.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package migration
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/spf13/viper"
11+
)
12+
13+
type migration struct {
14+
smm2Dir string
15+
migrationSuccessMarkerPath string
16+
}
17+
18+
var Migration *migration
19+
20+
func Init() {
21+
if Migration == nil {
22+
Migration = &migration{}
23+
Migration.smm2Dir = filepath.Join(viper.GetString("smm-local-dir"), "profiles")
24+
Migration.migrationSuccessMarkerPath = filepath.Join(Migration.smm2Dir, migrationSuccessMarkerFile)
25+
}
26+
}
27+
28+
const migrationSuccessMarkerFile = ".smm3_migration_acknowledged"
29+
30+
// https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go
31+
func pathExists(path string) bool {
32+
if _, err := os.Stat(path); err == nil {
33+
return true
34+
} else if errors.Is(err, os.ErrNotExist) {
35+
return false
36+
} else {
37+
slog.Warn("Error when checking path exists, so assuming it does not exist: "+path, slog.Any("error", err))
38+
return false
39+
}
40+
}
41+
42+
func (m *migration) NeedsSmm2Migration() bool {
43+
if pathExists(m.smm2Dir) {
44+
return !pathExists(Migration.migrationSuccessMarkerPath)
45+
}
46+
return false
47+
}
48+
49+
func (m *migration) MarkSmm2MigrationSuccess() error {
50+
file, err := os.Create(Migration.migrationSuccessMarkerPath)
51+
if err != nil {
52+
return fmt.Errorf("failed to create migration success marker file: %w", err)
53+
}
54+
err = file.Close()
55+
if err != nil {
56+
return fmt.Errorf("failed to close file: %w", err)
57+
}
58+
return nil
59+
}

backend/settings/settings.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path/filepath"
99

10+
psUtilDisk "github.com/shirou/gopsutil/v3/disk"
1011
"github.com/spf13/viper"
1112
wailsRuntime "github.com/wailsapp/wails/v2/pkg/runtime"
1213

@@ -65,6 +66,8 @@ type settings struct {
6566
CacheDir string `json:"cacheDir,omitempty"`
6667

6768
Debug bool `json:"debug,omitempty"`
69+
70+
NewUserSetupComplete bool `json:"newUserSetupComplete,omitempty"`
6871
}
6972

7073
var Settings = &settings{
@@ -95,6 +98,18 @@ var Settings = &settings{
9598
LaunchButton: "normal",
9699

97100
Debug: false,
101+
102+
NewUserSetupComplete: false,
103+
}
104+
105+
func (s *settings) GetNewUserSetupComplete() bool {
106+
return s.NewUserSetupComplete
107+
}
108+
109+
func (s *settings) SetNewUserSetupComplete(value bool) {
110+
slog.Info("changing NewUserSetupComplete state", slog.Bool("value", value))
111+
s.NewUserSetupComplete = value
112+
_ = SaveSettings()
98113
}
99114

100115
func (s *settings) FavoriteMod(modReference string) (bool, error) {
@@ -314,6 +329,21 @@ func ValidateCacheDir(dir string) error {
314329
return nil
315330
}
316331

332+
// GetCacheDirDiskSpaceLeft returns the amount of disk space left on the cache directory's disk in bytes
333+
func (s *settings) GetCacheDirDiskSpaceLeft() (uint64, error) {
334+
cacheDir := s.GetCacheDir()
335+
err := ValidateCacheDir(cacheDir)
336+
if err != nil {
337+
return 0, fmt.Errorf("cache directory to check space on failed to validate: %w", err)
338+
}
339+
340+
usage, err := psUtilDisk.Usage(cacheDir)
341+
if err != nil {
342+
return 0, fmt.Errorf("failed to get disk free space: %w", err)
343+
}
344+
return usage.Free, nil
345+
}
346+
317347
func moveCacheDir(newDir string) error {
318348
if newDir == viper.GetString("cache-dir") {
319349
return nil

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"Maximised",
1818
"Minimised",
1919
"mircearoata",
20+
"noclose",
2021
"Nyan",
2122
"smmanager",
2223
"smmprofile",

frontend/src/App.svelte

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
import ErrorDetails from '$lib/components/modals/ErrorDetails.svelte';
1414
import ErrorModal from '$lib/components/modals/ErrorModal.svelte';
1515
import ExternalInstallMod from '$lib/components/modals/ExternalInstallMod.svelte';
16+
import MigrationModal from '$lib/components/modals/MigrationModal.svelte';
1617
import { supportedProgressTypes } from '$lib/components/modals/ProgressModal.svelte';
18+
import FirstTimeSetupModal from '$lib/components/modals/first-time-setup/FirstTimeSetupModal.svelte';
1719
import { modalRegistry } from '$lib/components/modals/modalsRegistry';
1820
import ImportProfile from '$lib/components/modals/profiles/ImportProfile.svelte';
1921
import { isUpdateOnStart } from '$lib/components/modals/smmUpdate/smmUpdate';
@@ -23,9 +25,11 @@
2325
import { getModalStore, initializeModalStore } from '$lib/skeletonExtensions';
2426
import { installs, invalidInstalls, progress } from '$lib/store/ficsitCLIStore';
2527
import { error, expandedMod, siteURL } from '$lib/store/generalStore';
26-
import { konami, language, updateCheckMode } from '$lib/store/settingsStore';
28+
import { cacheDir, konami, language, updateCheckMode } from '$lib/store/settingsStore';
2729
import { smmUpdate, smmUpdateReady } from '$lib/store/smmUpdateStore';
2830
import { ExpandMod, UnexpandMod } from '$wailsjs/go/app/app';
31+
import { NeedsSmm2Migration } from '$wailsjs/go/migration/migration';
32+
import { GetCacheDirDiskSpaceLeft, GetNewUserSetupComplete } from '$wailsjs/go/settings/settings';
2933
import { Environment, EventsOn } from '$wailsjs/runtime';
3034
3135
initializeStores();
@@ -138,11 +142,19 @@
138142
meta: {
139143
persistent: true,
140144
},
141-
});
145+
});
142146
}
143147
}
144148
}
145-
149+
150+
$: GetCacheDirDiskSpaceLeft().then((spaceLeftBytes) => {
151+
if (spaceLeftBytes < 10e9) {
152+
const spaceLeftGbReadable = (spaceLeftBytes * 1e-9).toFixed(1);
153+
$error = `The drive your cache directory is on (${$cacheDir}) is very low on disk space (Only ~${spaceLeftGbReadable} GB left). Please free up some space or move the cache directory to another drive in the Mod Manager Settings.`;
154+
}
155+
}).catch((err) => {
156+
$error = `failed to check cache directory disk space left: ${err}`;
157+
});
146158
$: if ($smmUpdateReady && $updateCheckMode === 'ask') {
147159
modalStore.trigger({
148160
type: 'component',
@@ -163,6 +175,35 @@
163175
$error = null;
164176
}
165177
178+
// Order of checks is intentional
179+
NeedsSmm2Migration().then((needsMigration) => {
180+
if (needsMigration) {
181+
modalStore.trigger({
182+
type: 'component',
183+
component: {
184+
ref: MigrationModal,
185+
},
186+
meta: {
187+
persistent: true,
188+
},
189+
});
190+
}
191+
}).then(() => {
192+
GetNewUserSetupComplete().then((wasSetupCompleted) => {
193+
if (!wasSetupCompleted) {
194+
modalStore.trigger({
195+
type: 'component',
196+
component: {
197+
ref: FirstTimeSetupModal,
198+
},
199+
meta: {
200+
persistent: true,
201+
},
202+
});
203+
}
204+
});
205+
});
206+
166207
EventsOn('externalInstallMod', (modReference: string, version: string) => {
167208
if (!modReference) return;
168209
modalStore.trigger({

frontend/src/_global.postcss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ button {
3838
}
3939
}
4040

41+
.announcement-markdown-content {
42+
& a {
43+
@apply text-yellow-500;
44+
text-decoration: underline;
45+
}
46+
}
47+
4148
.markdown-content {
4249
@apply text-base;
4350

frontend/src/lib/components/announcements/Announcement.svelte

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import SvgIcon from '$lib/components/SVGIcon.svelte';
55
import { type Announcement, AnnouncementImportance } from '$lib/generated';
66
import { viewedAnnouncements } from '$lib/store/settingsStore';
7+
import { markdown as renderMarkdown } from '$lib/utils/markdown';
78
89
export let announcement: Pick<Announcement, 'id' | 'importance' | 'message'>;
910
@@ -20,14 +21,19 @@
2021
return mdiInformationOutline;
2122
}
2223
})();
24+
25+
$: rendered = renderMarkdown(announcement.message);
2326
</script>
2427

2528
<div class="announcement-{importanceLower} announcement-bg p-1.5 h-full" class:announcement-new={isNew}>
2629
<div class="flex items-center announcement-bg-text p-1 h-full">
2730
<SvgIcon class="w-8 h-8 mr-3 shrink-0" icon={icon} />
2831
<div class="grow wrap text-lg">
2932
<slot>
30-
{announcement.message}
33+
<div class="announcement-markdown-content">
34+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
35+
{@html rendered}
36+
</div>
3137
</slot>
3238
</div>
3339
</div>
@@ -77,4 +83,4 @@
7783
.announcement-new.announcement-bg {
7884
animation: slide 6s linear infinite;
7985
}
80-
</style>
86+
</style>

frontend/src/lib/components/announcements/AnnouncementsBar.svelte

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { AnnouncementImportance, type Announcement as AnnouncementType, GetAnnouncementsDocument, SmrHealthcheckDocument } from '$lib/generated';
1010
import { type PopupSettings, popup } from '$lib/skeletonExtensions';
1111
import { offline, viewedAnnouncements } from '$lib/store/settingsStore';
12+
import { markdown as renderMarkdown } from '$lib/utils/markdown';
1213
import { SetAnnouncementViewed } from '$wailsjs/go/settings/settings';
1314
1415
const { t } = getTranslate();
@@ -118,16 +119,23 @@
118119
},
119120
placement: 'bottom',
120121
} satisfies PopupSettings;
122+
123+
$: renderedTooltip = (() => {
124+
const content = $offline ? offlineAnnouncement.message : announcements[currentIndex]?.message;
125+
if (content?.length > 0) {
126+
return renderMarkdown(content);
127+
}
128+
return content;
129+
})();
121130
</script>
122131

123132
<!-- the if gets executed before this is added to the DOM for some reason if this is below the ifs, so the use:popup would not find the element -->
124133
<Tooltip disabled={!$offline && !announcements[currentIndex]} {popupId}>
125134
<span>
126-
{#if $offline}
127-
{offlineAnnouncement.message}
128-
{:else}
129-
{announcements[currentIndex]?.message}
130-
{/if}
135+
<div class="announcement-markdown-content">
136+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
137+
{@html renderedTooltip}
138+
</div>
131139
</span>
132140
</Tooltip>
133141

frontend/src/lib/components/left-bar/LeftBar.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,9 @@
364364
</button>
365365
</div>
366366
<div class="flex flex-col gap-2">
367-
<span class="pl-4 sticky top-0 z-[1] bg-surface-50-900-token">Links</span>
367+
<span class="pl-4 sticky top-0 z-[1] bg-surface-50-900-token">
368+
<T defaultValue="Links" keyName="left-bar.links"/>
369+
</span>
368370
<button
369371
class="btn w-full bg-surface-200-700-token px-4 h-8 text-sm"
370372
on:click={() => BrowserOpenURL($siteURL)}>

frontend/src/lib/components/left-bar/Settings.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { mdiBug, mdiCheck, mdiCheckboxBlankOutline, mdiCheckboxMarkedOutline, mdiChevronRight, mdiClipboard, mdiCog, mdiDownload, mdiFolderEdit, mdiLanConnect, mdiTune } from '@mdi/js';
2+
import { mdiBug, mdiCheck, mdiCheckboxBlankOutline, mdiCheckboxMarkedOutline, mdiChevronRight, mdiClipboard, mdiCog, mdiDownload, mdiEggEaster, mdiFolderEdit, mdiLanConnect, mdiTune } from '@mdi/js';
33
import { ListBox, ListBoxItem } from '@skeletonlabs/skeleton';
44
import { getTranslate } from '@tolgee/svelte';
55
import { getContextClient } from '@urql/svelte';
@@ -465,7 +465,7 @@
465465
{#if $konami}
466466
<hr class="divider" />
467467
<li class="section-header">
468-
<span class="h-5 w-5"><SvgIcon class="h-full w-full" icon={mdiCog}/></span>
468+
<span class="h-5 w-5"><SvgIcon class="h-full w-full" icon={mdiEggEaster}/></span>
469469
<span class="flex-auto">
470470
<T defaultValue="Secret settings" keyName="settings.secret-settings"/>
471471
</span>

0 commit comments

Comments
 (0)