Skip to content

Commit

Permalink
feat: editor history as a stand alone part of the state (#160)
Browse files Browse the repository at this point in the history
Pulling the logic for a the editor history out of the dashboard UI and into a separate class that is part of the state.
  • Loading branch information
Zoramite committed Jul 30, 2021
1 parent 87fd033 commit 36dc4e6
Show file tree
Hide file tree
Showing 6 changed files with 427 additions and 163 deletions.
5 changes: 5 additions & 0 deletions src/sass/parts/_onboarding.sass
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
padding: $le-space-medium
width: 100%

img
max-height: $le-space-xlarge
max-width: $le-space-xlarge
margin-bottom: $le-space-small

.le__part__onboarding__loading
align-items: center
display: flex
Expand Down
8 changes: 7 additions & 1 deletion src/ts/editor/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,10 @@ export interface PartialEditorConfig {
* Overall project information.
*/
export interface ProjectData {
/**
* Url for an avatar to use when showing a project.
*/
avatarUrl?: string;
/**
* Project title
*/
Expand Down Expand Up @@ -1121,6 +1125,7 @@ export type RemoteMediaOptions = GoogleMediaOptions;
* GitHub service installations information.
*/
export interface GitHubInstallationInfo {
avatarUrl?: string;
id: number;
org: string;
url: string;
Expand All @@ -1130,10 +1135,11 @@ export interface GitHubInstallationInfo {
* GitHub service organization installation information.
*/
export interface GitHubOrgInstallationInfo {
avatarUrl?: string;
description: string;
updatedAt?: string;
org: string;
repo: string;
updatedAt?: string;
url: string;
}

Expand Down
282 changes: 282 additions & 0 deletions src/ts/editor/recent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
import {DataStorage} from '../utility/dataStorage';
import {ProjectSource} from './api';

const STORAGE_RECENT_PROJECTS = 'live.history.recent.projects';
const STORAGE_RECENT_PROJECT_PREFIX = 'live.history.';

export interface EditorHistoryConfig {
storage: DataStorage;
}

export interface ProjectHistoryConfig {
id: string;
storage: DataStorage;
}

/**
* Track historical events in the editor.
*
* Uses a project identifier to store the history in the storage.
*
* Example identifiers: `local`, `gh/blinkk/project`.
*
* Usage:
*
* ```
* const editorHistory = new EditorHistory({
* storage: storage,
* });
* const projectHistory = editorHistory.getProject('local');
* projectHistory.recentWorkspaces;
* ```
*
* The history is also used to store history of the general access
* of projects.
*
* ```
* const recentProjects = editorHistory.recentProjects;
* ```
*/
export class EditorHistory {
config: EditorHistoryConfig;

constructor(config: EditorHistoryConfig) {
this.config = config;
}

/**
* Adds a project data to the history of recent projects.
*
* @param project Project information to add to the history.
* @returns Updated recent project history.
*/
addRecentProject(project: RecentProjectData): Array<RecentProjectData> {
const updatedRecentProjects = updateRecentProjects(
this.recentProjects,
project
);
this.config.storage.setItemArray(
STORAGE_RECENT_PROJECTS,
updatedRecentProjects
);
return updatedRecentProjects;
}

/**
* Retrieve the project specific history data.
*/
getProject(projectId: string): ProjectHistory {
return new ProjectHistory({
id: projectId,
storage: this.config.storage,
});
}

/**
* Retrieve the recent projects.
*/
get recentProjects(): Array<RecentProjectData> {
return this.config.storage.getItemArray(STORAGE_RECENT_PROJECTS);
}
}

export class ProjectHistory {
config: ProjectHistoryConfig;
data: ProjectHistoryData;

constructor(config: ProjectHistoryConfig) {
this.config = config;

this.data = this.config.storage.getItemRecord(
`${STORAGE_RECENT_PROJECT_PREFIX}${this.config.id}`
);
}

/**
* Adds file data to the history of the project.
*
* @param workspace Workspace name the file belongs to.
* @param file File information to add to the history.
* @returns Updated recent file history.
*/
addRecentFile(
workspace: string,
file: RecentFileData
): Array<RecentFileData> {
this.data.recentFiles = this.data.recentFiles ?? {};
this.data.recentFiles[workspace] = this.data.recentFiles[workspace] ?? [];
this.data.recentFiles[workspace] = updateRecentFiles(
this.data.recentFiles[workspace],
file
);
this.save();
return this.data.recentFiles[workspace];
}

/**
* Adds workspace to the history of the project.
*
* @param workspace Workspace information to add to the history.
* @returns Updated recent file history.
*/
addRecentWorkspace(
workspace: RecentWorkspaceData
): Array<RecentWorkspaceData> {
this.data.recentWorkspaces = this.data.recentWorkspaces ?? [];
this.data.recentWorkspaces = updateRecentWorkspaces(
this.data.recentWorkspaces,
workspace
);
this.save();
return this.data.recentWorkspaces;
}

getRecentFiles(workspace: string): Array<RecentFileData> {
this.data.recentFiles = this.data.recentFiles ?? {};
this.data.recentFiles[workspace] = this.data.recentFiles[workspace] ?? [];
return this.data.recentFiles[workspace];
}

getRecentWorkspaces(): Array<RecentWorkspaceData> {
this.data.recentWorkspaces = this.data.recentWorkspaces ?? [];
return this.data.recentWorkspaces;
}

save() {
this.config.storage.setItemRecord(
`${STORAGE_RECENT_PROJECT_PREFIX}${this.config.id}`,
this.data
);
}
}

export interface ProjectHistoryData {
recentFiles?: Record<string, Array<RecentFileData>>;
recentWorkspaces?: Array<RecentWorkspaceData>;
}

export interface RecentProjectData {
/**
* Project identifier.
*
* ex: `local`, `blinkk/project`
*/
identifier: string;
/**
* Source of the project.
*/
source?: ProjectSource | string;
/**
* Label for displaying the project to the user.
*/
label: string;
/**
* Avatar url for the project.
*/
avatarUrl?: string;
/**
* Timestamp of last date visited
*/
lastVisited: string;
}

export interface RecentFileData {
/**
* Path for the file.
*/
path: string;
/**
* Timestamp of last date visited
*/
lastVisited: string;
}

export interface RecentWorkspaceData {
/**
* Name of the workspace.
*/
name: string;
/**
* Timestamp of last date visited
*/
lastVisited: string;
}

function updateRecentFiles(
list: Array<RecentFileData>,
value: RecentFileData,
maxCount = 10
): Array<RecentFileData> {
let index: undefined | number = undefined;

// Search for matching item in the list.
for (let i = 0; i < list.length; i++) {
const item = list[i];
if (item.path === value.path) {
index = i;
break;
}
}

return updateRecentList(list, value, index, maxCount);
}

function updateRecentProjects(
list: Array<RecentProjectData>,
value: RecentProjectData,
maxCount = 10
): Array<RecentProjectData> {
let index: undefined | number = undefined;

// Search for matching item in the list.
for (let i = 0; i < list.length; i++) {
const item = list[i];
if (item.source === value.source && item.identifier === value.identifier) {
index = i;
break;
}
}

return updateRecentList(list, value, index, maxCount);
}

function updateRecentWorkspaces(
list: Array<RecentWorkspaceData>,
value: RecentWorkspaceData,
maxCount = 10
): Array<RecentWorkspaceData> {
let index: undefined | number = undefined;

// Search for matching item in the list.
for (let i = 0; i < list.length; i++) {
const item = list[i];
if (item.name === value.name) {
index = i;
break;
}
}

return updateRecentList(list, value, index, maxCount);
}

function updateRecentList<T>(
list: Array<T>,
value: T,
index?: number,
maxCount = 10
): Array<T> {
if (index !== undefined) {
// Already in recent, shift to the beginning of the list.
if (index > 0) {
list = [value, ...list.slice(0, index), ...list.slice(index + 1)];
}
} else {
// Add newest to the beginning of the list.
list.unshift(value);

// Trim old values.
list = list.slice(0, maxCount - 1) || [];
}

return list;
}

0 comments on commit 36dc4e6

Please sign in to comment.