Skip to content

Commit

Permalink
Merge pull request #33 from unipept/feature/recent-projects
Browse files Browse the repository at this point in the history
Application keeps track of recently opened projects
  • Loading branch information
pverscha authored Mar 4, 2020
2 parents fd071ca + e62520e commit 9bbdee2
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 34 deletions.
3 changes: 2 additions & 1 deletion src/components/pages/HomePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import ProjectManager from "@/logic/filesystem/project/ProjectManager";
import fs from "fs";
import InvalidProjectException from "@/logic/filesystem/project/InvalidProjectException";
import Tooltip from "unipept-web-components/src/components/custom/Tooltip.vue";
import RecentProjectsManager from "@/logic/filesystem/project/RecentProjectsManager";
const { dialog } = require("electron").remote;
@Component({
Expand All @@ -59,7 +60,6 @@ export default class HomePage extends Vue {
private errorMessage: string = "";
private errorSnackbarVisible: boolean = false;
private async createProject() {
const chosenPath: string | undefined = dialog.showOpenDialogSync({
properties: ["openDirectory"]
Expand All @@ -76,6 +76,7 @@ export default class HomePage extends Vue {
chosenPath[0],
this.$store.getters.baseUrl
);
await project.initialize();
await this.$store.dispatch("setProject", project);
await this.$router.push("/analysis/single");
Expand Down
65 changes: 32 additions & 33 deletions src/components/project/RecentProjects.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
<template>
<div>
<span>Recent projects</span>
<v-list two-line>
<v-list-item
v-for="recentProject of recentProjects"
:key="recentProject.name"
@click="openPreviouslyLoadedProject(recentProject.path)">
<v-list-item-content>
<v-list-item-title>{{ recentProject.name }}</v-list-item-title>
<v-list-item-subtitle>{{ recentProject.path }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-icon color="grey lighten-1" v-on="on">mdi-information</v-icon>
</template>
<span>Last opened on {{ recentProject.lastOpened }}</span>
</v-tooltip>
</v-list-item-action>
</v-list-item>
</v-list>
<div v-if="recentProjects.length > 0">
<span>Recent projects</span>
<v-list two-line>
<v-list-item
v-for="recentProject of recentProjects"
:key="recentProject.name"
@click="openPreviouslyLoadedProject(recentProject.path)">
<v-list-item-content>
<v-list-item-title>{{ recentProject.name }}</v-list-item-title>
<v-list-item-subtitle>{{ recentProject.path }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-icon color="grey lighten-1" v-on="on">mdi-information</v-icon>
</template>
<span>Last opened on {{ recentProject.lastOpened.toLocaleDateString() }}</span>
</v-tooltip>
</v-list-item-action>
</v-list-item>
</v-list>
</div>
<tooltip message="Select a previously created project to open." position="bottom">
<div class="open-project-button" @click="openProject()">
<v-icon>mdi-folder-open-outline</v-icon>
<a>Open other project...</a>
<a>Open project...</a>
</div>
</tooltip>
</div>
Expand All @@ -34,6 +36,8 @@ import Vue from "vue";
import Component from "vue-class-component";
import Tooltip from "unipept-web-components/src/components/custom/Tooltip.vue";
import ProjectManager from "@/logic/filesystem/project/ProjectManager.ts";
import RecentProjectsManager from "@/logic/filesystem/project/RecentProjectsManager";
import RecentProject from "@/logic/filesystem/project/RecentProject";
const { dialog } = require("electron").remote;
@Component({
Expand All @@ -42,18 +46,13 @@ const { dialog } = require("electron").remote;
}
})
export default class RecentProjects extends Vue {
private recentProjects: { name: string, path: string, lastOpened: string }[] = [
{
name: "My Project",
path: "/usr/pverscha/Desktop/My Project",
lastOpened: "November 22, 2019"
},
{
name: "Test",
path: "/usr/pverscha/Documents/Unipept/Test",
lastOpened: "February 12, 2020"
}
];
private recentProjects: RecentProject[] = [];
private async mounted() {
const recentMng = new RecentProjectsManager();
this.recentProjects.push(...await recentMng.getRecentProjects());
console.log(this.recentProjects);
}
private async openProject() {
const chosenPath: string | undefined = dialog.showOpenDialogSync({
Expand Down
10 changes: 10 additions & 0 deletions src/logic/filesystem/project/ProjectManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as path from "path";
import schema_v1 from "raw-loader!@/db/schemas/schema_v1.sql";
import StudyFileSystemReader from "@/logic/filesystem/study/StudyFileSystemReader";
import Study from "unipept-web-components/src/logic/data-management/study/Study";
import RecentProjectsManager from "@/logic/filesystem/project/RecentProjectsManager";

export default class ProjectManager {
public readonly DB_FILE_NAME: string = "metadata.sqlite";
Expand Down Expand Up @@ -41,6 +42,8 @@ export default class ProjectManager {
await this.loadStudy(`${projectLocation}${directory}`, project);
}

await this.addToRecentProjects(projectLocation);

return project;
}

Expand All @@ -59,6 +62,8 @@ export default class ProjectManager {
});
db.exec(schema_v1);

await this.addToRecentProjects(projectLocation);

return new Project(projectLocation, db, baseUrl);
}

Expand All @@ -82,4 +87,9 @@ export default class ProjectManager {
const studyReader = new StudyFileSystemReader(directory, project);
await study.accept(studyReader);
}

private async addToRecentProjects(path: string) {
const recentManager = new RecentProjectsManager();
await recentManager.addRecentProject(path);
}
}
11 changes: 11 additions & 0 deletions src/logic/filesystem/project/RecentProject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default class RecentProject {
public readonly name: string;
public readonly path: string;
public lastOpened: Date;

constructor(name: string, path: string, lastOpened: Date) {
this.name = name;
this.path = path;
this.lastOpened = lastOpened;
}
}
82 changes: 82 additions & 0 deletions src/logic/filesystem/project/RecentProjectsManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import RecentProject from "@/logic/filesystem/project/RecentProject";
import fs from "fs";
import path from "path";
import IOException from "unipept-web-components/src/logic/exceptions/IOException";

export default class RecentProjectsManager {
public static readonly RECENT_PROJECTS_FILE = "recents.json";
// How many recent projects do we keep track off?
public static readonly AMOUNT_OF_RECENTS = 15;

/**
* @return A list of all projects that were recently opened by the user, sorted descending by date. An empty list
* is returned if the recent projects file does not exist.
* @throws {IOException} If the recent projects file was corrupt, not readable or otherwise damaged.
*/
public async getRecentProjects(): Promise<RecentProject[]> {
try {
if (!fs.existsSync(this.getRecentsFilePath())) {
return [];
}

const projectData: string = fs.readFileSync(this.getRecentsFilePath(), {
encoding: "utf-8"
});

console.log(JSON.parse(projectData));

return JSON.parse(projectData)
.map(obj => new RecentProject(obj.name, obj.path, new Date(parseInt(obj.lastOpened))));
} catch (err) {
throw new IOException(err);
}
}

/**
* Add a new project to the list of recently opened projects. The name of the project will be derived from the path.
* The time of last opening the project will be set to now. If a project with the same path was already opened
* before, it's visited date will be updated.
*
* @param projectPath Location of the project on the user's local filesystem.
*/
public async addRecentProject(projectPath: string): Promise<void> {
if (!projectPath.endsWith("/")) {
projectPath += "/";
}

// First read the previously stored recent projects.
const recentProjects: RecentProject[] = await this.getRecentProjects();

// Now, check if the project to be added already exists in this list (if so, we only need to update it's date).
const existingProject: RecentProject = recentProjects.find(p => p.path === projectPath);

if (existingProject) {
existingProject.lastOpened = new Date();
} else {
recentProjects.push(new RecentProject(path.basename(projectPath), projectPath, new Date()));
}

const toWrite = recentProjects.sort((a, b) => b.lastOpened.getTime() - a.lastOpened.getTime())
.slice(0, RecentProjectsManager.AMOUNT_OF_RECENTS)
.map(p => {
return {
name: p.name,
path: p.path,
lastOpened: p.lastOpened.getTime()
}
});

console.log(toWrite);

fs.writeFileSync(this.getRecentsFilePath(), JSON.stringify(toWrite), {
encoding: "utf-8"
});
}

private getRecentsFilePath(): string {
const { app } = require("electron").remote;
// Get a reference to the user data folder in which configuration data will be stored.
const configurationFolder = app.getPath("userData");
return configurationFolder + "/" + RecentProjectsManager.RECENT_PROJECTS_FILE;
}
}

0 comments on commit 9bbdee2

Please sign in to comment.