Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Mod manager for [Satisfactory](https://www.satisfactorygame.com/).
Handles all the steps of installing mods for you.

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

## Installation and Usage

Expand Down
59 changes: 59 additions & 0 deletions backend/migration/migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package migration

import (
"errors"
"fmt"
"log/slog"
"os"
)

type migration struct {
smm2Dir string
}

var Migration *migration

func Init() error {
if Migration == nil {
dir, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("failed to get user home directory for migration check: %w", err)
}
Migration = &migration{}
Migration.smm2Dir = dir + "\\SatisfactoryModManager\\profiles\\"
}
return nil
}

const migrationSuccessMarkerFile = ".smm3_migration_acknowledged"

// https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go
func pathExists(path string) bool {
if _, err := os.Stat(path); err == nil {
return true
} else if errors.Is(err, os.ErrNotExist) {
return false
} else {
slog.Warn("Error when checking path exists, so assuming it does not exist: "+path, slog.Any("error", err))
return false
}
}

func (m *migration) NeedsSmm2Migration() bool {
if pathExists(m.smm2Dir) {
return !pathExists(m.smm2Dir + migrationSuccessMarkerFile)
}
return false
}

func (m *migration) MarkSmm2MigrationSuccess() error {
file, err := os.Create(m.smm2Dir + migrationSuccessMarkerFile)
if err != nil {
return fmt.Errorf("failed to create migration success marker file: %w", err)
}
err = file.Close()
if err != nil {
return fmt.Errorf("failed to close file: %w", err)
}
return nil
}
30 changes: 30 additions & 0 deletions backend/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"

psUtilDisk "github.com/shirou/gopsutil/v3/disk"
"github.com/spf13/viper"
wailsRuntime "github.com/wailsapp/wails/v2/pkg/runtime"

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

Debug bool `json:"debug,omitempty"`

NewUserSetupComplete bool `json:"newUserSetupComplete,omitempty"`
}

var Settings = &settings{
Expand Down Expand Up @@ -95,6 +98,18 @@ var Settings = &settings{
LaunchButton: "normal",

Debug: false,

NewUserSetupComplete: false,
}

func (s *settings) GetNewUserSetupComplete() bool {
return s.NewUserSetupComplete
}

func (s *settings) SetNewUserSetupComplete(value bool) {
slog.Info("changing NewUserSetupComplete state", slog.Bool("value", value))
s.NewUserSetupComplete = value
_ = SaveSettings()
}

func (s *settings) FavoriteMod(modReference string) (bool, error) {
Expand Down Expand Up @@ -314,6 +329,21 @@ func ValidateCacheDir(dir string) error {
return nil
}

// GetCacheDirDiskSpaceLeft returns the amount of disk space left on the cache directory's disk in bytes
func (s *settings) GetCacheDirDiskSpaceLeft() (uint64, error) {
cacheDir := s.GetCacheDir()
err := ValidateCacheDir(cacheDir)
if err != nil {
return 0, fmt.Errorf("cache directory to check space on failed to validate: %w", err)
}

usage, err := psUtilDisk.Usage(cacheDir)
if err != nil {
return 0, fmt.Errorf("failed to get disk free space: %w", err)
}
return usage.Free, nil
}

func moveCacheDir(newDir string) error {
if newDir == viper.GetString("cache-dir") {
return nil
Expand Down
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"Maximised",
"Minimised",
"mircearoata",
"noclose",
"Nyan",
"smmanager",
"smmprofile",
Expand Down
65 changes: 62 additions & 3 deletions frontend/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import ErrorDetails from '$lib/components/modals/ErrorDetails.svelte';
import ErrorModal from '$lib/components/modals/ErrorModal.svelte';
import ExternalInstallMod from '$lib/components/modals/ExternalInstallMod.svelte';
import MigrationModal from '$lib/components/modals/MigrationModal.svelte';
import { supportedProgressTypes } from '$lib/components/modals/ProgressModal.svelte';
import FirstTimeSetupModal from '$lib/components/modals/first-time-setup/FirstTimeSetupModal.svelte';
import { modalRegistry } from '$lib/components/modals/modalsRegistry';
import ImportProfile from '$lib/components/modals/profiles/ImportProfile.svelte';
import { isUpdateOnStart } from '$lib/components/modals/smmUpdate/smmUpdate';
Expand All @@ -23,9 +25,11 @@
import { getModalStore, initializeModalStore } from '$lib/skeletonExtensions';
import { installs, invalidInstalls, progress } from '$lib/store/ficsitCLIStore';
import { error, expandedMod, siteURL } from '$lib/store/generalStore';
import { konami, language, updateCheckMode } from '$lib/store/settingsStore';
import { cacheDir, konami, language, updateCheckMode } from '$lib/store/settingsStore';
import { smmUpdate, smmUpdateReady } from '$lib/store/smmUpdateStore';
import { ExpandMod, UnexpandMod } from '$wailsjs/go/app/app';
import { NeedsSmm2Migration } from '$wailsjs/go/migration/migration';
import { GetCacheDirDiskSpaceLeft, GetNewUserSetupComplete } from '$wailsjs/go/settings/settings';
import { Environment, EventsOn } from '$wailsjs/runtime';
initializeStores();
Expand Down Expand Up @@ -138,11 +142,23 @@
meta: {
persistent: true,
},
});
});
}
}
}
cacheDir.subscribe((cacheDirectory) => {
GetCacheDirDiskSpaceLeft().then((spaceLeftBytes) => {
if (spaceLeftBytes < 10e9) {
const spaceLeftGbReadable = (spaceLeftBytes * 1e-9).toFixed(1);
$error = `The drive your cache directory is on (${cacheDirectory}) 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.`;
}
}).catch((err) => {
$error = `failed to check cache directory disk space left: ${err}`;
});
});
$: if ($smmUpdateReady && $updateCheckMode === 'ask') {
modalStore.trigger({
type: 'component',
Expand All @@ -163,6 +179,49 @@
$error = null;
}
let displayMigrationModal = false;
$: if (displayMigrationModal) {
modalStore.trigger({
type: 'component',
component: {
ref: MigrationModal,
},
meta: {
persistent: true,
},
});
}
let displayFirstTimeSetupModal = false;
$: if (displayFirstTimeSetupModal) {
modalStore.trigger({
type: 'component',
component: {
ref: FirstTimeSetupModal,
},
meta: {
persistent: true,
},
});
}
// Order of checks is intentional
NeedsSmm2Migration().then((needsMigration) => {
if (needsMigration) {
displayMigrationModal = true;
}
}).catch((err) => {
$error = `failed to check if SMM2 migration is needed: ${err}`;
}).then(() => {
GetNewUserSetupComplete().then((wasSetupCompleted) => {
if (!wasSetupCompleted) {
displayFirstTimeSetupModal = true;
}
}).catch((err) => {
$error = `failed to check if new user setup is needed: ${err}`;
});
});
EventsOn('externalInstallMod', (modReference: string, version: string) => {
if (!modReference) return;
modalStore.trigger({
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/_global.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ button {
}
}

.announcement-markdown-content {
& a {
@apply text-yellow-500;
text-decoration: underline;
}
}

.markdown-content {
@apply text-base;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import SvgIcon from '$lib/components/SVGIcon.svelte';
import { type Announcement, AnnouncementImportance } from '$lib/generated';
import { viewedAnnouncements } from '$lib/store/settingsStore';
import { markdown as renderMarkdown } from '$lib/utils/markdown';
export let announcement: Pick<Announcement, 'id' | 'importance' | 'message'>;
Expand All @@ -20,14 +21,17 @@
return mdiInformationOutline;
}
})();
$: rendered = renderMarkdown(announcement.message);
</script>

<div class="announcement-{importanceLower} announcement-bg p-1.5 h-full" class:announcement-new={isNew}>
<div class="flex items-center announcement-bg-text p-1 h-full">
<SvgIcon class="w-8 h-8 mr-3 shrink-0" icon={icon} />
<div class="grow wrap text-lg">
<slot>
{announcement.message}
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html `<div class="announcement-markdown-content">${rendered}</div>`}
</slot>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/lib/components/left-bar/LeftBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,9 @@
</button>
</div>
<div class="flex flex-col gap-2">
<span class="pl-4 sticky top-0 z-[1] bg-surface-50-900-token">Links</span>
<span class="pl-4 sticky top-0 z-[1] bg-surface-50-900-token">
<T defaultValue="Links" keyName="left-bar.links"/>
</span>
<button
class="btn w-full bg-surface-200-700-token px-4 h-8 text-sm"
on:click={() => BrowserOpenURL($siteURL)}>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/components/left-bar/Settings.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { mdiBug, mdiCheck, mdiCheckboxBlankOutline, mdiCheckboxMarkedOutline, mdiChevronRight, mdiClipboard, mdiCog, mdiDownload, mdiFolderEdit, mdiLanConnect, mdiTune } from '@mdi/js';
import { mdiBug, mdiCheck, mdiCheckboxBlankOutline, mdiCheckboxMarkedOutline, mdiChevronRight, mdiClipboard, mdiCog, mdiDownload, mdiEggEaster, mdiFolderEdit, mdiLanConnect, mdiTune } from '@mdi/js';
import { ListBox, ListBoxItem } from '@skeletonlabs/skeleton';
import { getTranslate } from '@tolgee/svelte';
import { getContextClient } from '@urql/svelte';
Expand Down Expand Up @@ -465,7 +465,7 @@
{#if $konami}
<hr class="divider" />
<li class="section-header">
<span class="h-5 w-5"><SvgIcon class="h-full w-full" icon={mdiCog}/></span>
<span class="h-5 w-5"><SvgIcon class="h-full w-full" icon={mdiEggEaster}/></span>
<span class="flex-auto">
<T defaultValue="Secret settings" keyName="settings.secret-settings"/>
</span>
Expand Down
Loading
Loading