Browse Source

Separate project

Extract project from monorepo.
master
TheoryOfNekomata 3 years ago
commit
2fb6af99ba
7 changed files with 141 additions and 0 deletions
  1. +2
    -0
      .npmignore
  2. +3
    -0
      README.md
  3. +18
    -0
      package.json
  4. +23
    -0
      src/Engine.ts
  5. +55
    -0
      src/Storage.ts
  6. +13
    -0
      src/index.ts
  7. +27
    -0
      tsconfig.json

+ 2
- 0
.npmignore View File

@@ -0,0 +1,2 @@
*.ts
tsconfig.json

+ 3
- 0
README.md View File

@@ -0,0 +1,3 @@
# Zeichen - Local Storage Plugin

Save and load notes in Zeichen using the browser's local storage.

+ 18
- 0
package.json View File

@@ -0,0 +1,18 @@
{
"name": "zeichen-plugin-local-storage",
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"version": "0.1.0",
"description": "Save and load notes in Zeichen using the browser's local storage.",
"keywords": [
"zeichen",
"plugin",
"storage",
"local"
],
"devDependencies": {
"typescript": "^4.0.3"
},
"scripts": {
"build": "tsc **/*.ts"
}
}

+ 23
- 0
src/Engine.ts View File

@@ -0,0 +1,23 @@
export default class LocalStorage<T> {
constructor(
private readonly source: Storage,
private readonly serializer: (t: T) => string = JSON.stringify,
private readonly deserializer: (s: string) => T = JSON.parse,
) {}

getCollection(collectionId: string) {
const raw = this.source.getItem(collectionId)
if (raw !== null) {
return this.deserializer(raw)
}
return null
}

replaceCollection(collectionId: string, collectionData: T) {
this.source.setItem(collectionId, this.serializer(collectionData))
}

removeCollection(collectionId: string) {
this.source.removeItem(collectionId)
}
}

+ 55
- 0
src/Storage.ts View File

@@ -0,0 +1,55 @@
import Storage, { Collection, OutOfSyncError } from '../../core/src/storage'
import Engine from './Engine'

export default class LocalStorage<T> implements Storage<T> {
private readonly engine: Engine<Collection<T>>

constructor(
private readonly ownerId: string,
private readonly storageId: string,
private readonly getItemId = item => item['id'],
) {
this.engine = new Engine<Collection<T>>(window.localStorage)
}

private getMeta() {
const oldMeta = this.engine.getCollection(this.storageId)
if (oldMeta === null) {
throw new OutOfSyncError()
}
return oldMeta
}

private existenceCheck(newItem: T) {
return oldItem => this.getItemId(oldItem) === this.getItemId(newItem)
}

async queryItems() {
return this.getMeta().items
}

async saveItem(newItem: T) {
const oldMeta = this.getMeta()
const isExistingItem = oldMeta.items.some(this.existenceCheck(newItem))
const newMeta: Collection<T> = {
items: isExistingItem
? oldMeta.items.map(oldItem => this.existenceCheck(newItem)(oldItem) ? newItem : oldItem)
: [...oldMeta.items, newItem],
lastModifiedBy: this.ownerId,
lastModifiedAt: new Date(),
}
this.engine.replaceCollection(this.storageId, newMeta)
}

async deleteItem(newItem: T) {
const oldMeta = this.getMeta()
const newItems = oldMeta.items.filter(oldItem => !this.existenceCheck(newItem)(oldItem))
const newMeta: Collection<T> = {
items: newItems,
lastModifiedBy: this.ownerId,
lastModifiedAt: new Date(),
}
this.engine.replaceCollection(this.storageId, newMeta)
return oldMeta.items.length !== newItems.length
}
}

+ 13
- 0
src/index.ts View File

@@ -0,0 +1,13 @@
import { Plugin } from '../../core/src/plugin'
import Storage from './Storage'

type PluginConfig = {}

const LocalStoragePlugin: Plugin<PluginConfig> = config => ({
currentUserId,
}) => {
new Storage(currentUserId, 'notes')
new Storage(currentUserId, 'folders')
}

export default LocalStoragePlugin

+ 27
- 0
tsconfig.json View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "es6",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"declaration": true,
"declarationDir": "./dist",
"sourceMap": true,
"strict": false
},
"exclude": [
"node_modules",
"**/*.test.ts"
],
"include": [
"**/*.ts"
]
}

Loading…
Cancel
Save