Fixed virtual drive saving and loading

This commit is contained in:
Prozilla 2023-08-17 15:06:48 +02:00
parent 334676084a
commit 9c65c7c1f8
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
10 changed files with 248 additions and 58 deletions

View file

@ -59,7 +59,7 @@ export function FileExplorer({ startPath }) {
if (directory) {
setCurrentDirectory(directory);
setPath(directory.path);
setPath(directory.root ? "/" : directory.path);
}
};
@ -68,10 +68,16 @@ export function FileExplorer({ startPath }) {
};
const onKeyDown = (event) => {
const value = event.target.value;
let value = event.target.value;
if (event.key === "Enter") {
setCurrentDirectory(virtualRoot.navigate(value));
if (value === "")
value = "~";
const directory = virtualRoot.navigate(value);
setCurrentDirectory(directory);
setPath(directory.root ? "/" : directory.path);
}
};
@ -139,6 +145,16 @@ export function FileExplorer({ startPath }) {
</button>
</div>
<div className={styles.Main}>
{currentDirectory?.getSubFolders(showHidden)?.map(({ name }, index) =>
<button key={index} tabIndex={0} className={styles["Folder-button"]}
onClick={() => {
changeDirectory(name);
}}
>
<FontAwesomeIcon icon={faFolder}/>
<p>{name}</p>
</button>
)}
{currentDirectory?.getFiles(showHidden)?.map((file, index) =>
<button key={index} tabIndex={0} className={styles["File-button"]}
onClick={(event) => {
@ -150,16 +166,6 @@ export function FileExplorer({ startPath }) {
<p>{file.id}</p>
</button>
)}
{currentDirectory?.getSubFolders(showHidden)?.map(({ name }, index) =>
<button key={index} tabIndex={0} className={styles["Folder-button"]}
onClick={() => {
changeDirectory(name);
}}
>
<FontAwesomeIcon icon={faFolder}/>
<p>{name}</p>
</button>
)}
</div>
</div>
</div>

View file

@ -4,6 +4,8 @@ import styles from "./Settings.module.css";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js";
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext.js";
const wallpapersPath = "~/Images/Wallpapers";
export function AppearanceSettings() {
const virtualRoot = useVirtualRoot();
const settingsManager = useSettingsManager();
@ -23,7 +25,7 @@ export function AppearanceSettings() {
<div className={styles["Option"]}>
<p className={styles["Label"]}>Wallpaper</p>
<div className={styles["Input"]}>
{virtualRoot.navigate("~/Images")?.getFiles()?.map(({ id, source }) =>
{virtualRoot.navigate(wallpapersPath)?.getFiles()?.toReversed().map(({ id, source }) =>
<label className={styles["Image-select"]} key={id}>
<input
type="radio"

View file

@ -7,6 +7,7 @@ import { useModals } from "../../hooks/modals/modals.js";
import { ModalsView } from "../modals/ModalsView.jsx";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
import { FALLBACK_WALLPAPER } from "../../constants/desktop.js";
export const Desktop = memo(() => {
const settingsManager = useSettingsManager();
@ -28,12 +29,20 @@ export const Desktop = memo(() => {
})();
}, [settingsManager]);
const onError = () => {
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.desktop);
settings.set("wallpaper", FALLBACK_WALLPAPER);
};
return (<>
<div
className={styles.Container}
style={{ backgroundImage: wallpaper ? `url(${wallpaper})` : null }}
onContextMenu={onContextMenu}
>
{wallpaper
? <img src={wallpaper} className={styles.Wallpaper} alt="Desktop wallpaper" onError={onError}/>
: null
}
<ModalsView modalsManager={modalsManager} modals={modals}/>
</div>
</>);

View file

@ -5,6 +5,14 @@
width: 100%;
height: 100%;
z-index: -1;
background-size: cover;
background-position: center;
}
.Wallpaper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
pointer-events: none;
}

View file

@ -7,4 +7,6 @@ export const WALLPAPERS = [
"/media/wallpapers/vibrant-wallpaper-blue-purple-red.png",
"/media/wallpapers/vibrant-wallpaper-purple-yellow.png",
"/media/wallpapers/wave-abstract-wallpaper-teal.png",
];
];
export const FALLBACK_WALLPAPER = WALLPAPERS[4];

View file

@ -6,6 +6,9 @@ export class StorageManager {
* @param {string} value
*/
static store(key, value) {
if (key == null || value == null)
return;
localStorage.setItem(key, value);
}
@ -14,6 +17,9 @@ export class StorageManager {
* @returns {string | null}
*/
static load(key) {
if (key == null)
return null;
return localStorage.getItem(key);
}

View file

@ -1,5 +1,4 @@
import { EventEmitter } from "../utils/events.js";
import { VirtualRoot } from "./virtualRoot.js";
export class VirtualBase extends EventEmitter {
@ -20,8 +19,12 @@ export class VirtualBase extends EventEmitter {
* @returns {VirtualBase}
*/
setName(name) {
if (this.name === name || !this.canBeEdited)
return;
this.name = name;
this.getRoot().saveData();
this.confirmChanges();
return this;
}
@ -30,12 +33,13 @@ export class VirtualBase extends EventEmitter {
* @returns {ThisType}
*/
setAlias(alias) {
if (this.alias === alias)
if (this.alias === alias || !this.canBeEdited)
return;
this.alias = alias;
this.getRoot().addShortcut(alias, this);
this.getRoot().saveData();
this.confirmChanges();
return this;
}
@ -44,34 +48,92 @@ export class VirtualBase extends EventEmitter {
* @returns {VirtualBase}
*/
setParent(parent) {
if (this.parent === parent)
if (this.parent === parent || !this.canBeEdited)
return;
this.parent = parent;
this.getRoot().saveData();
this.confirmChanges();
return this;
}
/**
* @param {boolean} value
* @returns {VirtualBase}
*/
setProtected(value) {
if (!this.canBeEdited)
return;
this.isProtected = value;
return this;
}
delete() {
if (!this.canBeEdited)
return;
const parent = this.parent;
if (parent == null)
return;
parent.remove?.(this);
parent.getRoot()?.saveData();
this.confirmChanges(parent.getRoot());
}
/**
* @param {VirtualRoot} [root]
*/
confirmChanges(root = null) {
if (this.getRoot().loadedDefaultData)
this.editedByUser = true;
if (root == null)
root = this.getRoot();
root?.saveData();
}
open() {}
get path() {
return this.alias ?? this.absolutePath;
return this.alias ?? this.displayPath;
}
get absolutePath() {
/**
* Returns path without using alias
*/
get displayPath() {
return this.parent?.path + "/" + this.id;
}
/**
* Returns path without using any aliases
* @returns {string}
*/
get absolutePath() {
if (this.parent?.isRoot) {
return "/" + this.id;
} else {
return this.parent?.absolutePath + "/" + this.id;
}
}
/**
* Returns whether this can be edited in its current state
* @returns {boolean}
*/
get canBeEdited() {
const isProtected = this.isProtected && this.getRoot().loadedDefaultData;
if (!isProtected && this.parent != null) {
return this.parent.canBeEdited;
} else {
return !isProtected;
}
}
/**
* @returns {VirtualRoot}
*/
@ -85,6 +147,9 @@ export class VirtualBase extends EventEmitter {
return root;
}
/**
* @returns {object | null}
*/
toJSON() {
const object = {
nam: this.name

View file

@ -35,15 +35,15 @@ export class VirtualFile extends VirtualBase {
* @returns {VirtualFile}
*/
setSource(source) {
if (this.source === source)
if (this.source === source || !this.canBeEdited)
return;
this.source = source;
this.content = null;
this.emit(VirtualFile.EVENT_NAMES.CONTENT_CHANGE, this);
this.getRoot().saveData();
this.confirmChanges();
return this;
}
@ -53,15 +53,15 @@ export class VirtualFile extends VirtualBase {
* @returns {VirtualFile}
*/
setContent(content) {
if (this.content === content)
if (this.content === content || !this.canBeEdited)
return;
this.content = content;
this.source = null;
this.emit(VirtualFile.EVENT_NAMES.CONTENT_CHANGE, this);
this.getRoot().saveData();
this.confirmChanges();
return this;
}
@ -93,8 +93,18 @@ export class VirtualFile extends VirtualBase {
});
}
/**
* @returns {object | null}
*/
toJSON() {
// Don't return file if it can't or hasn't been edited
if (!this.canBeEdited || (this.editedByUser == null || !this.editedByUser))
return null;
const object = super.toJSON();
if (object == null)
return null;
if (this.extension != null) {
object.ext = this.extension;

View file

@ -97,6 +97,9 @@ export class VirtualFolder extends VirtualBase {
* @returns {VirtualFolder}
*/
createFile(name, extension, callback) {
if (!this.canBeEdited)
return;
let newFile = this.findFile(name, extension);
if (newFile == null) {
newFile = new VirtualFile(name, extension);
@ -104,7 +107,8 @@ export class VirtualFolder extends VirtualBase {
newFile.parent = this;
}
callback?.(newFile);
this.getRoot().saveData();
newFile.confirmChanges();
return this;
}
@ -116,10 +120,14 @@ export class VirtualFolder extends VirtualBase {
* @returns {VirtualFolder}
*/
createFiles(files) {
if (!this.canBeEdited)
return;
files.forEach(({name, extension}) => {
this.createFile(name, extension);
});
this.getRoot().saveData();
this.confirmChanges();
return this;
}
@ -135,6 +143,9 @@ export class VirtualFolder extends VirtualBase {
* @param {createFolderCallback} callback
*/
createFolder(name, callback) {
if (!this.canBeEdited)
return;
let newFolder = this.findSubFolder(name);
if (newFolder == null) {
newFolder = new VirtualFolder(name);
@ -142,7 +153,8 @@ export class VirtualFolder extends VirtualBase {
newFolder.parent = this;
}
callback?.(newFolder);
this.getRoot().saveData();
newFolder.confirmChanges();
return this;
}
@ -152,18 +164,25 @@ export class VirtualFolder extends VirtualBase {
* @returns {VirtualFolder}
*/
createFolders(folders) {
if (!this.canBeEdited)
return;
folders.forEach((name) => {
this.createFolder(name);
});
this.getRoot().saveData();
this.confirmChanges();
return this;
}
/**
* Removes a file or folder from this folder
* @param {VirtualFile|VirtualFolder} child
* @param {VirtualFile | VirtualFolder} child
*/
remove(child) {
if (!this.canBeEdited)
return;
child.parent = null;
if (child instanceof VirtualFile) {
@ -172,13 +191,13 @@ export class VirtualFolder extends VirtualBase {
removeFromArray(child, this.subFolders);
}
this.getRoot()?.saveData();
child.confirmChanges();
}
/**
* Returns the file or folder at a relative path or null if it doesn't exist
* @param {string} relativePath
* @returns {VirtualFile|VirtualFolder|null}
* @returns {VirtualFile | VirtualFolder | null}
*/
navigate(relativePath) {
const segments = relativePath.split("/");
@ -231,16 +250,20 @@ export class VirtualFolder extends VirtualBase {
* Deletes this folder and all its files and sub-folders recursively
*/
delete() {
if (!this.canBeEdited)
return;
super.delete();
this.files.concat(this.subFolders).forEach((item) => {
item.delete();
});
this.getRoot()?.saveData();
this.confirmChanges();
}
/**
* Returns all files inside this folder
* @param {Boolean=false} showHidden
* @returns {Array<VirtualFile>}
*/
@ -254,6 +277,7 @@ export class VirtualFolder extends VirtualBase {
}
/**
* Returns all subfolders inside this folder
* @param {Boolean=false} showHidden
* @returns {Array<VirtualFolder>}
*/
@ -266,16 +290,36 @@ export class VirtualFolder extends VirtualBase {
);
}
/**
* @returns {object | null}
*/
toJSON() {
const object = super.toJSON();
if (object == null)
return null;
if (this.files.length > 0) {
object.fls = this.files.map((file) => file.toJSON());
const files = this.files
.map((file) => file.toJSON())
.filter((file) => file != null);
if (files.length > 0)
object.fls = files;
}
if (this.subFolders.length > 0) {
object.fds = this.subFolders.map((folder) => folder.toJSON());
const folders = this.subFolders
.map((folder) => folder.toJSON())
.filter((folder) => folder != null);
if (folders.length > 0)
object.fds = folders;
}
// Don't store folder if it's empty
if ((!object.fls || object.fls.length === 0) && (!object.fds || object.fds.length === 0))
return null;
return object;
}
}

View file

@ -7,14 +7,16 @@ import { VirtualFolder } from "./virtualFolder.js";
* A virtual folder that serves as the root folder
*/
export class VirtualRoot extends VirtualFolder {
/**
* @type {boolean}
*/
/** @type {boolean} */
initiated = false;
/** @type {boolean} */
loadedDefaultData = false;
constructor() {
super("root");
this.root = this;
this.isRoot = true;
this.shortcuts = {};
}
@ -51,18 +53,21 @@ export class VirtualRoot extends VirtualFolder {
});
})
.createFolder("Images", (folder) => {
for (let i = 0; i < WALLPAPERS.length; i++) {
const source = WALLPAPERS[i];
folder.createFile(`Wallpaper${i + 1}`, "png", (file) => {
file.setSource(source);
});
}
folder.createFolder("Wallpapers", (folder) => {
folder.setProtected(true);
for (let i = 0; i < WALLPAPERS.length; i++) {
const source = WALLPAPERS[i];
folder.createFile(`Wallpaper${i + 1}`, "png", (file) => {
file.setSource(source);
});
}
});
})
.createFolder("Documents", (folder) => {
folder.createFile("text", "txt", (file) => {
file.setContent("Hello world!");
}).createFile("info", "md", (file) => {
file.setSource("/documents/info.md");
file.setProtected(true).setSource("/documents/info.md");
});
})
.createFolder("Desktop");
@ -117,10 +122,10 @@ export class VirtualRoot extends VirtualFolder {
const addFolder = ({ nam: name, fds: folders, fls: files }, parent = this) => {
parent.createFolder(name, (folder) => {
if (Object.values(shortcuts).includes(folder.absolutePath)) {
if (Object.values(shortcuts).includes(folder.displayPath)) {
let alias;
for (const [key, value] of Object.entries(shortcuts)) {
if (value === folder.absolutePath)
if (value === folder.displayPath)
alias = key;
}
folder.setAlias(alias);
@ -148,23 +153,40 @@ export class VirtualRoot extends VirtualFolder {
addFile(file);
});
}
console.log(this);
}
/**
* Calls the storage manager's store function with this root's data as a string
*/
saveData() {
if (!this.initiated)
return;
StorageManager.store("data", this.toString());
const data = this.toString();
if (data == null)
return;
StorageManager.store("data", data);
}
/**
* Initiates this root by loading the default data and then the user's data on top
* @returns {VirtualRoot}
*/
init() {
this.initiated = false;
// Load default data
this.loadedDefaultData = false;
this.setAlias("/");
this.loadDefaultData();
this.loadedDefaultData = true;
// Load user's data
this.loadData();
this.initiated = true;
return this;
}
@ -179,6 +201,9 @@ export class VirtualRoot extends VirtualFolder {
return this;
}
/**
* Tells the storage manager to clear all data and reloads the window
*/
reset() {
if (window.confirm("Are you sure you want to reset all your data?")) {
StorageManager.clear();
@ -202,13 +227,19 @@ export class VirtualRoot extends VirtualFolder {
return "";
}
get absolutePath() {
get displayPath() {
return "/";
}
/**
* @returns {object | null}
*/
toJSON() {
const object = super.toJSON();
if (object == null)
return null;
if (Object.entries(this.shortcuts).length > 0) {
object.scs = {};
@ -220,8 +251,15 @@ export class VirtualRoot extends VirtualFolder {
return object;
}
/**
* @returns {string | null}
*/
toString() {
const json = this.toJSON();
if (json == null)
return null;
return JSON.stringify(json);
}
}