-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(0.1.3) new fully private API Recorder with OPFS storage feat(0.2.0) got rid of typescript
- Loading branch information
Сидоркин Олег Валентинович
authored and
Сидоркин Олег Валентинович
committed
Aug 27, 2024
1 parent
1d00b66
commit 18b37b3
Showing
24 changed files
with
6,470 additions
and
2,629 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import globals from "globals"; | ||
import pluginJs from "@eslint/js"; | ||
|
||
|
||
export default [ | ||
{languageOptions: { globals: globals.browser }}, | ||
pluginJs.configs.recommended, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,23 +5,22 @@ | |
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc && vite build && electron-builder" | ||
"build": "vite build && tauri" | ||
}, | ||
"devDependencies": { | ||
"@eslint/js": "^9.9.1", | ||
"autoprefixer": "^10.4.20", | ||
"electron": "^31.4.0", | ||
"electron-builder": "^24.13.3", | ||
"eslint": "^9.9.1", | ||
"globals": "^15.9.0", | ||
"postcss": "^8.4.41", | ||
"tailwindcss": "^3.4.9", | ||
"typescript": "^5.2.2", | ||
"vite": "^5.1.6", | ||
"vite-plugin-electron": "^0.28.6", | ||
"vite-plugin-electron-renderer": "^0.14.5" | ||
"tailwindcss": "^3.4.10", | ||
"vite": "^5.1.6" | ||
}, | ||
"main": "dist-electron/main.js", | ||
"main": "dist-electron/main.mjs", | ||
"packageManager": "[email protected]+sha512.91d93b445d9284e7ed52931369bc89a663414e5582d00eea45c67ddc459a2582919eece27c412d6ffd1bd0793ff35399381cb229326b961798ce4f4cc60ddfdb", | ||
"dependencies": { | ||
"@js-temporal/polyfill": "^0.4.4", | ||
"sqlocal": "^0.11.1" | ||
"@sqlite.org/sqlite-wasm": "3.46.1-build1", | ||
"tauri": "^0.15.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import {dateUtils} from "./date-utils.mjs"; | ||
import {Temporal} from "@js-temporal/polyfill"; | ||
import {getWordCount} from "./getWordCount.mjs"; | ||
|
||
import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; | ||
import path from "node:path"; | ||
import {safeStorage} from "electron"; | ||
|
||
export class DB { | ||
db; | ||
|
||
constructor() { | ||
sqlite3InitModule({print: console.log, printErr: console.error}) | ||
.then((sqlite3Static) => { | ||
this.db = | ||
'opfs' in sqlite3Static | ||
? new sqlite3Static.oo1.OpfsDb('/mydb.sqlite3') | ||
: new sqlite3Static.oo1.DB('/mydb.sqlite3', 'ct'); | ||
}); | ||
|
||
|
||
|
||
this.getDatabaseFile('/mydb.sqlite3').then(databaseFile => { | ||
const fileUrl = URL.createObjectURL(databaseFile); | ||
|
||
const a = document.createElement('a'); | ||
a.href = fileUrl; | ||
a.download = 'database.sqlite3'; | ||
a.click(); | ||
a.remove(); | ||
|
||
URL.revokeObjectURL(fileUrl); | ||
}) | ||
|
||
this.init() | ||
} | ||
|
||
async getDatabaseFile(fileName) { | ||
const tempFileName = `backup-${Date.now()}--${fileName}`; | ||
await this.db.sql`VACUUM INTO ${path.join('/')}/${tempFileName}`; | ||
|
||
let dirHandle = await navigator.storage.getDirectory(); | ||
for (let dirName of path) | ||
dirHandle = await dirHandle.getDirectoryHandle(dirName); | ||
|
||
|
||
const fileHandle = await dirHandle.getFileHandle(tempFileName); | ||
const file = await fileHandle.getFile(); | ||
const fileBuffer = await file.arrayBuffer(); | ||
await dirHandle.removeEntry(tempFileName); | ||
|
||
return new File([fileBuffer], fileName, { | ||
type: 'application/x-sqlite3', | ||
}); | ||
} | ||
|
||
init() { | ||
console.log(safeStorage.isEncryptionAvailable()); | ||
this.db.exec({ | ||
sql: ` | ||
CREATE TABLE IF NOT EXISTS diaries ( | ||
uuid TEXT PRIMARY KEY NOT NULL UNIQUE, | ||
text TEXT NOT NULL, | ||
date DATE NOT NULL UNIQUE, | ||
word_count INTEGER NOT NULL DEFAULT 0 | ||
)` | ||
}) | ||
|
||
this.db.exec({ | ||
sql: ` | ||
CREATE TABLE IF NOT EXISTS audios ( | ||
uuid TEXT PRIMARY KEY NOT NULL UNIQUE, | ||
audio BLOB NOT NULL UNIQUE, | ||
diary TEXT NOT NULL, | ||
FOREIGN KEY(diary) REFERENCES diaries(uuid) | ||
)` | ||
}) | ||
|
||
this.db.exec({ | ||
sql: ` | ||
INSERT INTO diaries (uuid, text, date, word_count) VALUES ( | ||
${crypto.randomUUID()}, | ||
${""}, | ||
${dateUtils.getTodayString()}, | ||
${0} | ||
) | ||
ON CONFLICT(date) DO NOTHING; | ||
` | ||
}) | ||
} | ||
|
||
async today() { | ||
return await this.db.exec({sql: `SELECT * FROM diaries WHERE date = ${dateUtils.getTodayString()}`}) | ||
} | ||
|
||
async timeline(yyyymm) { | ||
const timeline= [] | ||
const isCurrentMonth = dateUtils.nowInCurrentMonth(yyyymm) | ||
const todayDayNumber = Temporal.Now.plainDateISO().day - 1 | ||
const timelineData = await this.db.exec({sql: `SELECT date, word_count FROM diaries WHERE date >= ${dateUtils.getFirstDateOfMonth(yyyymm)} AND date <= ${dateUtils.getLastDateOfMonth(yyyymm)}`}) | ||
|
||
this._fulfillTimeline(yyyymm, timeline, isCurrentMonth, todayDayNumber, timelineData); | ||
|
||
return timeline | ||
} | ||
|
||
_fulfillTimeline(yyyymm, timeline, isCurrentMonth, todayDayNumber, timelineData) { | ||
for (let i = 0; i < yyyymm.daysInMonth; i++) { | ||
timeline[i] = { | ||
day: i, | ||
word_count: 0 | ||
}; | ||
} | ||
|
||
if (isCurrentMonth) { | ||
for (let i = todayDayNumber; i < yyyymm.daysInMonth; i++) { | ||
timeline[i].word_count = -1; | ||
} | ||
} | ||
|
||
for (const {date, word_count} of timelineData) { | ||
const day = Temporal.PlainDate.from(date).day - 1 | ||
|
||
timeline[timeline.findIndex((tlDay) => (tlDay.day === day))] = { | ||
day, | ||
word_count, | ||
is_today: todayDayNumber === day | ||
} | ||
} | ||
} | ||
|
||
async day(yyyymmdd) { | ||
return await this.db.exec({sql: `SELECT * FROM diaries WHERE date = ${yyyymmdd}`}) | ||
} | ||
|
||
async updateText(text) { | ||
await this.db.exec({sql: `UPDATE diaries SET text = ${text}, word_count = ${getWordCount(text)} WHERE date = ${dateUtils.getTodayString()}`}) | ||
} | ||
|
||
async insertAudio(todayUUID, audioFileName) { | ||
await this.db.exec({sql: `INSERT INTO audios (uuid, audio, diary) VALUES (${crypto.randomUUID()}, ${audioFileName}, ${todayUUID})`}) | ||
} | ||
|
||
async getAudiosByDate(todayUUID) { | ||
return await this.db.exec({sql: `SELECT * FROM audios WHERE diary = ${todayUUID}`}) | ||
} | ||
} |
Oops, something went wrong.