Skip to content

Commit

Permalink
Merge pull request #265 from NicDoesCode/219-optimise-getting-project…
Browse files Browse the repository at this point in the history
…s-from-local-storage

#219 - Optimise getting projects from local storage
  • Loading branch information
NicDoesCode authored May 9, 2024
2 parents 6986386 + 9ea93c5 commit dd297ae
Show file tree
Hide file tree
Showing 13 changed files with 678 additions and 67 deletions.
24 changes: 24 additions & 0 deletions wwwroot/modules/factory/projectEntryFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ProjectEntry from "../models/projectEntry.js";

export default class ProjectEntryFactory {


/**
* Creates a new instance of the project entry class.
* @param {ProjectEntryInitialValues} values - Initial values for the project entry.
*/
static create(values) {
return new ProjectEntry(values.id, values.title, values.systemType, values.dateLastModified);
}


}

/**
* @typedef {Object} ProjectEntryInitialValues
* @property {string?} id - Unique ID of the new project, if not supplied one will be created.
* @property {string?} title - Title, if not supplied one will be created.
* @property {string?} systemType - Type of system targetted, either 'smsgg', 'gb' or 'nes', default is 'smsgg'.
* @property {Date|number} dateLastModified - Date of last modification for the project.
* @exports
*/
90 changes: 50 additions & 40 deletions wwwroot/modules/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import PaletteListFactory from "./factory/paletteListFactory.js";
import TileUtil from "./util/tileUtil.js";
import FileUtil from "./util/fileUtil.js";
import Project from "./models/project.js";
import ProjectEntry from "./models/projectEntry.js";
import GeneralUtil from "./util/generalUtil.js";
import ProjectWatcher from "./components/projectWatcher.js";
import ImageUtil from "./util/imageUtil.js";
Expand Down Expand Up @@ -64,6 +65,7 @@ import TileSet from "./models/tileSet.js";
import SampleProjectManager from "./components/sampleProjectManager.js";
import KeyboardManager, { KeyDownHandler, KeyUpHandler } from "./components/keyboardManager.js";
import ProjectList from "./models/projectList.js";
import ProjectEntryList from "./models/projectEntryList.js";
import SystemUtil from "./util/systemUtil.js";
import { DropPosition } from "./types.js";
import TileMapUtil from "./util/tileMapUtil.js";
Expand Down Expand Up @@ -855,6 +857,7 @@ function handleWatcherEvent(args) {
});
}
formatForProject();
uiRefreshProjectLists();
break;

case ProjectWatcher.Events.projectListChanged:
Expand Down Expand Up @@ -945,9 +948,7 @@ function handleProjectToolbarOnCommand(args) {
break;

case ProjectToolbar.Commands.projectLoadById:
const projects = state.getProjectsFromLocalStorage();
const project = projects.getProjectById(args.projectId);
state.setProject(project);
state.setProjectById(args.projectId);
break;

case ProjectToolbar.Commands.projectDelete:
Expand All @@ -963,7 +964,7 @@ function handleProjectToolbarOnCommand(args) {

/** @param {import('./ui/dialogues/projectDropdown.js').ProjectDropdownCommandEventArgs} args */
async function handleProjectDropdownOnCommand(args) {
const projectList = state.getProjectsFromLocalStorage();
const projectEntries = state.getProjectEntries();
switch (args.command) {

case ProjectDropdown.Commands.title:
Expand Down Expand Up @@ -991,8 +992,7 @@ async function handleProjectDropdownOnCommand(args) {
break;

case ProjectDropdown.Commands.projectLoadById:
const project = projectList.getProjectById(args.projectId);
state.setProject(project);
state.setProjectById(args.projectId);
projectDropdown.setState({ visible: false });
break;

Expand All @@ -1014,7 +1014,7 @@ async function handleProjectDropdownOnCommand(args) {
getUIState().projectDropDownSort = sort;
state.savePersistentUIStateToLocalStorage();
projectDropdown.setState({
projects: getSortedProjectArray(projectList, sort)
projects: getSortedProjectArray(projectEntries, sort)
});
break;

Expand All @@ -1028,7 +1028,7 @@ async function handleProjectDropdownOnCommand(args) {
case ProjectDropdown.Commands.showWelcomeScreen:
welcomeScreen.setState({
visible: true,
projects: getSortedProjectArray(projectList, getUIState().projectDropDownSort),
projects: getSortedProjectArray(projectEntries, getUIState().projectDropDownSort),
visibleCommands: ['dismiss']
});
projectDropdown.setState({ visible: false });
Expand Down Expand Up @@ -1948,9 +1948,7 @@ function welcomeScreenOnCommand(args) {
break;

case WelcomeScreen.Commands.projectLoadById:
const projects = state.getProjectsFromLocalStorage();
const project = projects.getProjectById(args.projectId);
state.setProject(project);
state.setProjectById(args.projectId);
welcomeScreen.setState({ visible: false });
break;

Expand All @@ -1972,7 +1970,7 @@ function welcomeScreenOnCommand(args) {
getUIState().welcomeScreenProjectSort = sort;
state.savePersistentUIStateToLocalStorage();
welcomeScreen.setState({
projects: getSortedProjectArray(state.getProjectsFromLocalStorage(), sort)
projects: getSortedProjectArray(state.getProjectEntries(), sort)
});
break;

Expand All @@ -1992,7 +1990,7 @@ function welcomeScreenOnCommand(args) {
* Creates a default tile set and palettes when the data store doesn't contain any.
*/
function createDefaultProjectIfNoneExists() {
const projects = state.getProjectsFromLocalStorage();
const projects = state.getProjectEntries();
if (projects.length === 0) {
const newProject = createEmptyProject();
state.setProject(newProject);
Expand Down Expand Up @@ -2273,27 +2271,31 @@ function isTileMap() {
}

/**
* @param {ProjectList|Project[]} projects
* @param {ProjectEntryList|ProjectEntry[]|ProjectList|Project[]} projects
* @param {import('./types.js').SortEntry} sort
* @returns {Project[]}
* @returns {Project[]|ProjectEntry[]}
*/
function getSortedProjectArray(projects, sort) {
projects = projects instanceof ProjectList ? projects.getProjects() : projects;
if (!projects || !Array.isArray(projects)) return [];
/** @type {Project[]|ProjectEntry[]} */
const projectArray = [];
if (projects instanceof ProjectList) projectArray.push(...projects.getProjects());
if (projects instanceof ProjectEntryList) projectArray.push(...projects.getProjectEntries());
if (Array.isArray(projects)) projectArray.push(...projects);

if (sort?.field === 'title') {
if (sort.direction === 'asc') {
projects.sort((a, b) => a.title > b.title ? 1 : -1);
projectArray.sort((a, b) => a.title > b.title ? 1 : -1);
} else {
projects.sort((a, b) => a.title < b.title ? 1 : -1);
projectArray.sort((a, b) => a.title < b.title ? 1 : -1);
}
} else if (sort?.field === 'dateLastModified') {
if (sort.direction === 'asc') {
projects.sort((a, b) => a.dateLastModified > b.dateLastModified ? 1 : -1);
projectArray.sort((a, b) => a.dateLastModified > b.dateLastModified ? 1 : -1);
} else {
projects.sort((a, b) => a.dateLastModified < b.dateLastModified ? 1 : -1);
projectArray.sort((a, b) => a.dateLastModified < b.dateLastModified ? 1 : -1);
}
}
return projects;
return projectArray;
}

function refreshProjectUI() {
Expand Down Expand Up @@ -2650,27 +2652,29 @@ function displaySelectedProject() {
} else {
formatForNoProject();
// Select default project if one was there
const projects = state.getProjectsFromLocalStorage();
const projectEntries = state.getProjectEntries();
const project = (() => {
const lastProject = projects.getProjectById(getUIState().lastProjectId);
if (lastProject) return lastProject;
if (projects.length > 0) return projects.getProject(0);
if (getUIState().lastProjectId) {
const lastProject = state.getProjectById(getUIState().lastProjectId);
if (lastProject) return lastProject;
}
if (projectEntries.length > 0) return state.getProjectFromLocalStorage(projectEntries[0].id);
return null;
})();
if (project) state.setProject(project);
}
}

function uiRefreshProjectLists() {
const projectList = state.getProjectsFromLocalStorage();
const projectEntryList = state.getProjectEntries();
projectToolbar.setState({
projects: projectList
projects: projectEntryList
});
projectDropdown.setState({
projects: getSortedProjectArray(projectList, getUIState().projectDropDownSort)
projects: getSortedProjectArray(projectEntryList, getUIState().projectDropDownSort)
});
welcomeScreen.setState({
projects: getSortedProjectArray(projectList, getUIState().welcomeScreenProjectSort)
projects: getSortedProjectArray(projectEntryList, getUIState().welcomeScreenProjectSort)
});
}

Expand Down Expand Up @@ -5325,45 +5329,51 @@ window.addEventListener('load', async () => {
await PageModalDialogue.wireUpElementsAsync(document.body);

// Load and set state
state.loadProjectEntriesFromLocalStorage();
state.loadPersistentUIStateFromLocalStorage();

checkPersistentUIValues();

// Load initial projects
const projectList = state.getProjectsFromLocalStorage();
let projectEntryList = state.getProjectEntries();
const sampleManager = await SampleProjectManager.getInstanceAsync();
instanceState.sampleProjects = await sampleManager.getSampleProjectsAsync();

const sampleProjects = instanceState.sampleProjects;
if (projectList.length === 0) {
if (projectEntryList.length === 0) {
for (let i = 0; i < sampleProjects.length; i++) {
const sampleProject = sampleProjects[i];
const loadedProject = await sampleManager.loadSampleProjectAsync(sampleProject.url);
// Add to storage
projectList.addProject(loadedProject);
state.saveProjectToLocalStorage(loadedProject, false);
// Default tile map ID
const defaultTileMap = loadedProject.tileMapList.getTileMapById(sampleProject.defaultTileMapId);
getProjectUIState(loadedProject).tileMapId = defaultTileMap?.tileMapId ?? null;
}
getUIState().lastProjectId = projectList.getProject(0).id;
getUIState().lastProjectId = state.getProjectEntries()[0].id;
state.savePersistentUIStateToLocalStorage();
projectEntryList = state.getProjectEntries();
}

const project = projectList.getProjectById(getUIState().lastProjectId);
state.setProject(project);
try {
state.setProjectById(getUIState().lastProjectId);
} catch {
const firstProjectId = state.getProjectEntries()[0].id;
state.setProjectById(firstProjectId);
}

projectToolbar.setState({
projects: projectList
projects: projectEntryList
});
projectDropdown.setState({
projects: getSortedProjectArray(projectList, getUIState().projectDropDownSort),
projects: getSortedProjectArray(projectEntryList, getUIState().projectDropDownSort),
sampleProjects: sampleProjects
});

// Clean up unused project states
Object.keys(getUIState().projectStates).forEach((projectId) => {
if (!projectList.containsProjectById(projectId)) {
const found = state.getProjectEntries().filter((p) => p.id === projectId);
if (found.length === 0) {
delete getUIState().projectStates[projectId];
}
});
Expand Down Expand Up @@ -5396,7 +5406,7 @@ window.addEventListener('load', async () => {
showWelcomeScreenOnStartUpChecked: getUIState().welcomeVisibleOnStartup,
visibleCommands: getProject() instanceof Project === true ? ['dismiss'] : [],
invisibleCommands: getProject() instanceof Project === false ? ['dismiss'] : [],
projects: getSortedProjectArray(projectList, getUIState().welcomeScreenProjectSort)
projects: getSortedProjectArray(projectEntryList, getUIState().welcomeScreenProjectSort)
});

optionsToolbar.setState({
Expand Down
93 changes: 93 additions & 0 deletions wwwroot/modules/models/projectEntry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import GeneralUtil from "../util/generalUtil.js";

/**
* Represents basic information about a project.
*/
export default class ProjectEntry {


/** Gets or sets the project id. */
get id() {
return this.#id;
}
set id(value) {
this.#id = value;
}

/** Gets or sets the project title. */
get title() {
return this.#title;
}
set title(value) {
this.#title = value;
}

/** Gets or sets the system type (either 'smsgg' or 'gb'). */
get systemType() {
return this.#systemType;
}
set systemType(value) {
this.#systemType = value;
}

/** Gets or sets the date that this project was last modified. */
get dateLastModified() {
return this.#dateLastModified;
}
set dateLastModified(value) {
this.#dateLastModified = value;
}


/** @type {string} */
#id = null;
/** @type {string} */
#title;
/** @type {string} */
#systemType;
/** @type {Date} */
#dateLastModified;


/**
* Creates a new instance of the project entry class.
* @param {string?} id - ID of the project.
* @param {string?} title - Title, if not supplied one will be created.
* @param {string?} systemType - Type of system targetted, either 'smsgg', 'gb' or 'nes', default is 'smsgg'.
* @param {Date|number|null} dateLastModified - Date that the project was last modified.
*/
constructor(id, title, systemType, dateLastModified) {

if (typeof id !== 'undefined' && id !== null && id.length > 0) {
this.#id = id;
} else {
this.id = GeneralUtil.generateRandomString(16);
}

if (typeof title !== 'undefined' && title !== null) {
this.title = title;
} else {
this.title = 'New project';
}

if (typeof systemType !== 'undefined' && systemType !== null) {
switch (systemType) {
case 'gb': this.systemType = 'gb'; break;
case 'nes': this.systemType = 'nes'; break;
case 'smsgg': default: this.systemType = 'smsgg'; break;
}
} else {
this.systemType = 'smsgg';
}

if (dateLastModified instanceof Date) {
this.#dateLastModified = dateLastModified;
} else if (typeof dateLastModified === 'number') {
this.#dateLastModified = new Date(dateLastModified);
} else {
this.#dateLastModified = new Date();
}
}


}
Loading

0 comments on commit dd297ae

Please sign in to comment.