Compare commits

..

No commits in common. "feature_db_location" and "master" have entirely different histories.

16 changed files with 828 additions and 4327 deletions

18
main.js
View File

@ -1,18 +1,8 @@
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
const path = require('path') require('./src/data/sqlite-electron-ipc');
const here = app.getAppPath()
console.log('here', here)
require('./src/data/sqlite-electron-ipc') let mainWin;
// Preload
const Sentry = require('@sentry/browser')
Sentry.init({
dsn: 'http://a12d5c5f800b406f8d1c0c5d2ed63a78@216.128.138.128:8000/1'
})
let mainWin
function createWindow () { function createWindow () {
mainWin = new BrowserWindow({ mainWin = new BrowserWindow({
@ -23,12 +13,11 @@ function createWindow () {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false, contextIsolation: false,
preload: path.join(here, 'preload.js')
}, },
nodeIntegration: true nodeIntegration: true
}) })
// win.autoHideMenuBar = true; //win.autoHideMenuBar = true;
mainWin.loadFile('index.html') mainWin.loadFile('index.html')
} }
@ -36,3 +25,4 @@ function createWindow () {
app.whenReady().then(() => { app.whenReady().then(() => {
createWindow() createWindow()
}) })

3431
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "time-chain", "name": "time-chain",
"version": "1.1.4", "version": "1.0.0",
"description": "timechain", "description": "timechain",
"author": "Jaosn Tudisco", "author": "Jaosn Tudisco",
"license": "UNLICENSED", "license": "UNLICENSED",
@ -17,7 +17,7 @@
"watch": "parcel --dist-dir ./src/dist --target \"jason\" start.js", "watch": "parcel --dist-dir ./src/dist --target \"jason\" start.js",
"build-main": "parcel build --dist-dir ./src/dist --target \"jason\" main.js", "build-main": "parcel build --dist-dir ./src/dist --target \"jason\" main.js",
"rebuild": "electron-rebuild -f -w better-sqlite3", "rebuild": "electron-rebuild -f -w better-sqlite3",
"xxxpostinstall": "electron-builder install-app-deps" "postinstall": "electron-builder install-app-deps"
}, },
"browserslist": "> 0.5%, last 2 versions, not dead", "browserslist": "> 0.5%, last 2 versions, not dead",
"targets": { "targets": {
@ -29,13 +29,12 @@
"@parcel/transformer-less": "^2.3.0", "@parcel/transformer-less": "^2.3.0",
"@parcel/transformer-sass": "^2.3.0", "@parcel/transformer-sass": "^2.3.0",
"@riotjs/compiler": "^6.1.3", "@riotjs/compiler": "^6.1.3",
"@riotjs/parcel-transformer-riot": "https://git.ptud.biz/tudisco/parcel-transformer-riot/archive/v8.tar.gz", "@riotjs/parcel-transformer-riot": "^7.0.3",
"electron": "^16.0.7", "electron": "^16.0.7",
"electron-rebuild": "^3.2.7", "electron-rebuild": "^3.2.7",
"jest": "^27.4.7", "jest": "^27.4.7",
"less": "^4.1.2", "less": "^4.1.2",
"parcel": "^2", "parcel": "^2"
"standard": "^16.0.4"
}, },
"dependencies": { "dependencies": {
"@sentry/browser": "^6.17.6", "@sentry/browser": "^6.17.6",
@ -44,8 +43,6 @@
"better-sqlite3": "^7.5.0", "better-sqlite3": "^7.5.0",
"conf": "^10.1.1", "conf": "^10.1.1",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"debounce": "^1.2.1",
"electron-config": "^2.0.0",
"empty-lite": "^1.2.0", "empty-lite": "^1.2.0",
"es6-interface": "^3.2.1", "es6-interface": "^3.2.1",
"hash.js": "^1.1.7", "hash.js": "^1.1.7",
@ -53,7 +50,6 @@
"nanoid": "^3.2.0", "nanoid": "^3.2.0",
"pubsub-js": "^1.9.4", "pubsub-js": "^1.9.4",
"riot": "^6.1.2", "riot": "^6.1.2",
"sanitize-html": "^2.6.1", "sanitize-html": "^2.6.1"
"sweetalert2": "^11.4.8"
} }
} }

View File

@ -1,22 +0,0 @@
// Preload
const Sentry = require('@sentry/browser')
// const { BrowserTracing } require( '@sentry/tracing' )
Sentry.init({
dsn: 'http://a12d5c5f800b406f8d1c0c5d2ed63a78@216.128.138.128:8000/1',
// Alternatively, use `process.env.npm_package_version` for a dynamic release version
// if your build tool supports it.
release: 'timechain@1.0.0'
// integrations: [new BrowserTracing()],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
// tracesSampleRate: 1.0
})
window.Sentry = Sentry
window.Timechain_isElectron = true
window.Timechain_dbModule = 'sqlite-client'
window.Timechain_canChooseDbLocation = true

View File

@ -32,14 +32,6 @@ class TimeChainDataSqliteTag //extends Interface(InterfaceTag)
return this.send('has',{tag:tag}) return this.send('has',{tag:tag})
} }
all(){
return this.send('all',{})
}
like(str){
return this.send('like',{str:str})
}
} }
class TimeChainDataSqliteTagLink //extends Interface(InterfaceTagLink) class TimeChainDataSqliteTagLink //extends Interface(InterfaceTagLink)

View File

@ -1,198 +1,146 @@
const { ipcMain } = require('electron') const { ipcMain } = require('electron');
const { app } = require('electron')
const path = require('path')
const fs = require('fs')
// const config = app.getPath('userData');
const Config = require('electron-config') const {app} = require('electron');
const config = new Config() //const config = app.getPath('userData');
let DBPath = config.get('database.path') const {TimeChainDataSqliteRecord,ConnectToDatabase, TimeChainDataSqliteFile, TimeChainDataSqliteTag, TimeChainDataSqliteTagLink} = require('./sqlite');
if (!DBPath) {
DBPathDir = app.getPath('userData')
DBPath = path.join(DBPathDir, '/timechain.db')
config.set('database.path', DBPath)
}
const { TimeChainDataSqliteRecord, ConnectToDatabase, TimeChainDataSqliteFile, TimeChainDataSqliteTag, TimeChainDataSqliteTagLink } = require('./sqlite')
console.log(DBPath) const appPath = app.getPath('userData');
const DB = ConnectToDatabase(DBPath) const DBPath = appPath+"/timechain.db";
console.log(DBPath);
const DbRecord = new TimeChainDataSqliteRecord() const DB = ConnectToDatabase(DBPath);
const DbFile = new TimeChainDataSqliteFile()
const DbTag = new TimeChainDataSqliteTag()
const DbTagLink = new TimeChainDataSqliteTagLink()
// ** Extra IPC Functions const DbRecord = new TimeChainDataSqliteRecord();
const DbFile = new TimeChainDataSqliteFile();
const DbTag = new TimeChainDataSqliteTag();
const DbTagLink = new TimeChainDataSqliteTagLink();
//const configDir = app.getPath('userData');
//const appPath = app.getPath('exe');
ipcMain.handle('select-sqlite-file', (event, arg) => {
const { dialog } = require('electron')
const files = dialog.showOpenDialog({
filters: [{
name: 'Sqlite File',
extensions: ['db', 'db3']
}]
})
if (!files) return false
return files
})
ipcMain.handle('create-sqlite-file', (event, arg) => {
const { dialog } = require('electron')
const files = dialog.showSaveDialogSync({
filters: [{
name: 'Sqlite File',
extensions: ['db', 'db3']
}]
})
if (!files) return false // ** Extra Data
return files ipcMain.handle('timechain-config-dir', (event,arg) => {
}) return false;
});
/**
* Allows the UI to select another database file
*/
ipcMain.handle('timechain-database-open', (event, arg) => {
if (!arg.filename) return false
if (!fs.existsSync(arg.filename)) return false
config.set('database.path', arg.filename)
app.relaunch()
app.quit()
// DB = ConnectToDatabase(arg.filename)
return true
})
/**
* Allows the UI to create a new database instance
*/
ipcMain.handle('timechain-database-create', (event, arg) => {
if (!arg.filename) return false
console.log('database.path', arg.filename)
config.set('database.path', arg.filename)
app.relaunch()
app.quit()
// DB = ConnectToDatabase(arg.filename)
return true
})
// ** RECORD ** // ** RECORD **
console.log('Register timechain-record') console.log("Register timechain-record");
ipcMain.handle('timechain-record', async (event, arg) => { ipcMain.handle('timechain-record', async (event,arg) => {
let res = null
switch (arg.func) { let res = null;
switch(arg.func){
case 'add': case 'add':
res = await DbRecord.add(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash) res = await DbRecord.add(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash);
break break;
case 'delete': case 'delete':
res = await DbRecord.delete(arg.uuid) res = await DbRecord.delete(arg.uuid);
break break;
case 'update': case 'update':
res = await DbRecord.update(arg.uuid, arg.content, arg.mime, arg.hash) res = await DbRecord.update(arg.uuid,arg.content,arg.mime,arg.hash);
break break;
case 'find': case 'find':
res = await DbRecord.find(arg.search, arg.sort, arg.limit, arg.offset) res = await DbRecord.find(arg.search,arg.sort,arg.limit,arg.offset);
break break;
case 'get': case 'get':
res = await DbRecord.get(arg.uuid) res = await DbRecord.get(arg.uuid);
break break;
default: default:
res = new Error('unknown command') res = new Error('unknown command');
} }
return res return res;
}) })
// ** FILE ** // ** FILE **
ipcMain.handle('timechain-file', async (event, arg) => { ipcMain.handle('timechain-file', async (event, arg) => {
let res = null let res = null;
switch (arg.func) { switch(arg.func){
case 'add': case 'add':
res = await DbFile.add(arg.uuid_record, arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash) res = await DbFile.add(arg.uuid_record,arg.uuid,arg.timestamp,arg.content,arg.mime,arg.hash);
break break;
case 'update': case 'update':
res = await DbFile.update(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash) res = await DbFile.update(arg.uuid,arg.timestamp,arg.content,arg.mime,arg.hash);
break break;
case 'get-record': case 'get-record':
res = await DbFile.getByRecord(arg.uuid_record) res = await DbFile.getByRecord(arg.uuid_record);
break break;
case 'delete-record': case 'delete-record':
res = await DbFile.deleteRecord(arg.uuid_record) res = await DbFile.deleteRecord(arg.uuid_record);
break break;
case 'get': case 'get':
res = await DbFile.get(arg.uuid) res = await DbFile.get(arg.uuid);
break break;
case 'delete': case 'delete':
res = await DbFile.delete(arg.uuid) res = await DbFile.delete(arg.uuid);
break break;
default: default:
res = new Error('Unknow a command') res = new Error('Unknow a command');
} }
return res return res;
}) });
// ** TAG ** // ** TAG **
ipcMain.handle('timechain-tag', async (event, arg) => { ipcMain.handle('timechain-tag', async (event, arg) => {
let res = null
switch (arg.func) { let res = null;
switch(arg.func){
case 'add': case 'add':
res = await DbTag.add(arg.tag) res = await DbTag.add(arg.tag);
break
case 'delete':
res = await DbTag.delete(arg.tag)
break
case 'has':
res = await DbTag.delete(arg.tag)
break
case 'all':
res = await DbTag.all()
break; break;
case 'like': case 'delete':
res = await DbTag.like(arg.str) res = await DbTag.delete(arg.tag);
break;
case 'has':
res = await DbTag.delete(arg.tag);
break; break;
default: default:
res = new Error('Command Unknown') res = new Error('Command Unknown');
} }
return res return res;
})
});
// TAG LINK // TAG LINK
ipcMain.handle('timechain-taglink', async (event, arg) => { ipcMain.handle('timechain-taglink', async (event, arg) => {
let res = null
switch (arg.func) { let res = null;
switch(arg.func){
case 'add': case 'add':
res = await DbTagLink.add(arg.uuid, arg.tag) res = await DbTagLink.add(arg.uuid,arg.tag)
break break;
case 'delete': case 'delete':
res = await DbTagLink.delete(arg.uuid, arg.tag) res = await DbTagLink.delete(arg.uuid,arg.tag);
break break;
case 'delete-tag': case 'delete-tag':
res = await DbTagLink.deleteTag(arg.tag) res = await DbTagLink.deleteTag(arg.tag);
break break;
case 'delete-record': case 'delete-record':
res = await DbTagLink.deleteRecord(arg.uuid) res = await DbTagLink.deleteRecord(arg.uuid);
break break;
case 'get-records': case 'get-records':
res = await DbTagLink.getRecords(atg.tag) res = await DbTagLink.getRecords(atg.tag);
break break;
case 'get-tags': case 'get-tags':
res = await DbTagLink.getTags(atg.uuid) res = await DbTagLink.getTags(atg.uuid);
break break;
default: default:
res = Error('Commande not known') res = Error("Commande not known");
} }
return res return res;
}) });

View File

@ -1,140 +1,111 @@
const Interface = require('es6-interface') const Interface = require("es6-interface");
const { InterfaceRecord, InterfaceFile, InterfaceTag, InterfaceTagLink } = require('./interfaces') const {InterfaceRecord,InterfaceFile,InterfaceTag,InterfaceTagLink} = require("./interfaces");
const Database = require('better-sqlite3')
let db = null let db = null;
const ConnectToDatabase = (path) => { const ConnectToDatabase = (path)=>{
if (db) { db = require('better-sqlite3')(path, {});
db.close() return db;
db = null
}
db = new Database(path, {})
return db
} }
class TimeChainDataSqliteTag extends Interface(InterfaceTag) { class TimeChainDataSqliteTag extends Interface(InterfaceTag) {
constructor () {
super() constructor(){
super();
const table = `CREATE TABLE IF NOT EXISTS "tags" ( const table = `CREATE TABLE IF NOT EXISTS "tags" (
"tag" TEXT UNIQUE, "tag" TEXT UNIQUE,
"created_at" INTEGER NOT NULL, "created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL, "updated_at" INTEGER NOT NULL,
PRIMARY KEY("tag") PRIMARY KEY("tag")
);` );`;
const table_idx = '' const table_idx = ``;
if (!db) { if(!db){
throw new Error('Database is not connected') throw new Error("Database is not connected");
} }
db.exec(table + '\n' + table_idx)
this.db = db db.exec(table + "\n" + table_idx);
this.db = db;
} }
get table_add () { get table_add(){
if (!this._table_add) { if(!this._table_add){
this._table_add = db.prepare('INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)') this._table_add = db.prepare("INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)");
} }
return this._table_add return this._table_add;
} }
get table_delete () { get table_delete(){
if (!this._table_delete) { if(!this._table_delete){
this._table_delete = db.prepare('DELETE FROM tags WHERE tag = ?') this._table_delete = db.prepare("DELETE FROM tags WHERE tag = ?");
} }
return this._table_delete return this._table_delete;
} }
get table_get () { get table_get(){
if (!this._table_get) { if(!this._table_get){
this._table_get = db.prepare('SELECT * FROM tags WHERE tag = ?') this._table_get = db.prepare("SELECT * FROM tags WHERE tag = ?");
} }
return this._table_get return this._table_get;
} }
get table_get_like () { get table_count(){
if (!this._table_get) { if(!this._table_count){
this._table_get = db.prepare('SELECT * FROM tags WHERE tag LIKE ?') this._table_count = db.prepare("SELECT count(tag) as cnt FROM tags WHERE tag = ?");
} }
return this._table_get return this._table_count;
} }
get table_get_all() { add(tag){
if (!this._table_get_all) { return new Promise(resolve=>{
this._table_get_all = db.prepare('SELECT * FROM tags') this.has(tag).then(cnt=>{
if(cnt){
return resolve(1);
}else{
const dt = Math.floor(Date.now());
const res = this.table_add.run(tag,dt,dt);
return resolve(res?.changes);
} }
return this._table_get_all });
});
} }
get table_count () { delete(tag){
if (!this._table_count) { return new Promise(resolve=>{
this._table_count = db.prepare('SELECT count(tag) as cnt FROM tags WHERE tag = ?') const dt = Math.floor(Date.now());
} const res = this.table_delete.run(tag);
return this._table_count return resolve(res?.changes);
});
} }
add (tag) { get(tag){
return new Promise(resolve => { return new Promise(resolve=>{
this.has(tag).then(cnt => { const res = this.table_get.get(tag);
if (cnt) {
return resolve(1)
} else {
const dt = Math.floor(Date.now())
const res = this.table_add.run(tag, dt, dt)
return resolve(res?.changes)
}
})
})
}
all() {
return new Promise(resolve => {
const res = this.table_get_all.all();
return resolve(res); return resolve(res);
}) });
} }
like(str){ has(tag){
return new Promise(resolve => { return new Promise(resolve=>{
const res = this.table_get_like.all(str + '%'); const res = this.table_count.get(tag);
return resolve(res); return resolve(res && res.cnt>0);
}) });
} }
delete (tag) {
return new Promise(resolve => {
const dt = Math.floor(Date.now())
const res = this.table_delete.run(tag)
return resolve(res?.changes)
})
}
get (tag) {
return new Promise(resolve => {
const res = this.table_get.get(tag)
return resolve(res)
})
}
has (tag) {
return new Promise(resolve => {
const res = this.table_count.get(tag)
return resolve(res && res.cnt > 0)
})
}
} }
class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) { class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) {
constructor () {
super() constructor(){
super();
this.table = `CREATE TABLE IF NOT EXISTS "taglink" ( this.table = `CREATE TABLE IF NOT EXISTS "taglink" (
"uuid" TEXT NOT NULL, "uuid" TEXT NOT NULL,
"tag" TEXT NOT NULL, "tag" TEXT NOT NULL,
"created_at" INTEGER NOT NULL "created_at" INTEGER NOT NULL
);` );`;
this.table_idx = ` this.table_idx = `
CREATE INDEX IF NOT EXISTS "uuid_idx" ON "taglink" ( CREATE INDEX IF NOT EXISTS "uuid_idx" ON "taglink" (
"uuid" "uuid"
@ -142,107 +113,108 @@ class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) {
CREATE INDEX IF NOT EXISTS "tag_idx" ON "taglink" ( CREATE INDEX IF NOT EXISTS "tag_idx" ON "taglink" (
"tag" "tag"
); );
` `;
db.exec(this.table + '\n' + this.table_idx) db.exec(this.table + "\n" + this.table_idx);
this.db = db this.db = db;
} }
get table_add () { get table_add(){
if (!this._table_add) { if(!this._table_add){
this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)') this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)');
} }
return this._table_add return this._table_add;
} }
get table_delete () { get table_delete(){
if (!this._table_delete) { if(!this._table_delete){
this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?') this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?');
} }
return this._table_delete return this._table_delete;
} }
get table_delete_tag () { get table_delete_tag(){
if (!this._table_delete_tag) { if(!this._table_delete_tag){
this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?') this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?');
} }
return this._table_delete_tag return this._table_delete_tag;
} }
get table_delete_record () { get table_delete_record(){
if (!this._table_delete_record) { if(!this._table_delete_record){
this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?') this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?');
} }
return this._table_delete_record return this._table_delete_record;
} }
get table_get_records () { get table_get_records(){
if (!this._table_get_records) { if(!this._table_get_records){
this._table_get_records = db.prepare('SELECT * FROM taglink WHERE tag=?') this._table_get_records = db.prepare("SELECT * FROM taglink WHERE tag=?");
} }
return this._table_get_records return this._table_get_records;
} }
get table_get_tags () { get table_get_tags(){
if (!this._table_get_tags) { if(!this._table_get_tags){
this._table_get_tags = db.prepare('SELECT * FROM taglink WHERE uuid=?') this._table_get_tags = db.prepare("SELECT * FROM taglink WHERE uuid=?");
} }
return this._table_get_tags return this._table_get_tags;
} }
add (uuid, tag) { add(uuid,tag){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_add const prepare = this.table_add;
const dt = Math.floor(Date.now()) const dt = Math.floor(Date.now());
const res = prepare.run(uuid, tag, dt) const res = prepare.run(uuid,tag,dt);
return resolve(res?.changes) return resolve(res?.changes);
});
}
delete(uuid,tag){
return new Promise(resolve=>{
const prepare = this.table_delete;
const res = prepare.run(uuid,tag);
return resolve(res?.changes);
});
}
deleteTag(tag){
return new Promise(resolve=>{
const prepare = this.table_delete_tag;
const res = prepare.run(tag);
return resolve(res?.changes);
});
}
deleteRecord(uuid){
return new Promise(resolve=>{
const prepare = this.table_delete_record;
const res = prepare.run(uuid);
return resolve(res?.changes);
});
}
getRecords(tag){
return new Promise(resolve=>{
const prepare = this.table_get_records;
const res = prepare.all(tag);
return resolve(res);
}) })
} }
delete (uuid, tag) { getTags(uuid){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_delete const prepare = this.table_get_tags;
const res = prepare.run(uuid, tag) const res = prepare.all(uuid);
return resolve(res?.changes) return resolve(res);
})
}
deleteTag (tag) {
return new Promise(resolve => {
const prepare = this.table_delete_tag
const res = prepare.run(tag)
return resolve(res?.changes)
})
}
deleteRecord (uuid) {
return new Promise(resolve => {
const prepare = this.table_delete_record
const res = prepare.run(uuid)
return resolve(res?.changes)
})
}
getRecords (tag) {
return new Promise(resolve => {
const prepare = this.table_get_records
const res = prepare.all(tag)
return resolve(res)
})
}
getTags (uuid) {
return new Promise(resolve => {
const prepare = this.table_get_tags
const res = prepare.all(uuid)
return resolve(res)
}) })
} }
} }
class TimeChainDataSqliteFile extends Interface(InterfaceFile) { class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
constructor () {
super() constructor(){
super();
const table = `CREATE TABLE IF NOT EXISTS "files" ( const table = `CREATE TABLE IF NOT EXISTS "files" (
"uuid" TEXT UNIQUE, "uuid" TEXT UNIQUE,
"uuid_record" TEXT NOT NULL, "uuid_record" TEXT NOT NULL,
@ -253,115 +225,117 @@ class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
"created_at" INTEGER NOT NULL, "created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL, "updated_at" INTEGER NOT NULL,
PRIMARY KEY("uuid") PRIMARY KEY("uuid")
);` );`;
const table_idx = ` const table_idx = `
CREATE INDEX IF NOT EXISTS "files_idx_record" ON "files" ( CREATE INDEX IF NOT EXISTS "files_idx_record" ON "files" (
"uuid_record" "uuid_record"
); );
CREATE INDEX IF NOT EXISTS "files_idx_time" ON "files" ( CREATE INDEX IF NOT EXISTS "files_idx_time" ON "files" (
"timestamp" DESC "timestamp" DESC
);` );`;
if (!db) { if(!db){
throw new Error('Database is not connected') throw new Error("Database is not connected");
} }
db.exec(table + '\n' + table_idx) db.exec(table + "\n" + table_idx);
this.db = db this.db = db;
} }
get table_insert () { get table_insert() {
if (!this._table_insert) { if(!this._table_insert){
this._table_insert = db.prepare('INSERT INTO files (uuid,uuid_record,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)') this._table_insert = db.prepare(`INSERT INTO files (uuid,uuid_record,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)`);
} }
return this._table_insert return this._table_insert;
} }
get table_update () { get table_update() {
if (!this._table_update) { if(!this._table_update){
this._table_update = db.prepare('UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?') this._table_update = db.prepare(`UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?`);
} }
return this._table_update return this._table_update;
} }
get table_delete () { get table_delete() {
if (!this._table_delete) { if(!this._table_delete){
this._table_delete = db.prepare('DELETE FROM files WHERE uuid = ?') this._table_delete = db.prepare(`DELETE FROM files WHERE uuid = ?`);
} }
return this._table_delete return this._table_delete;
} }
get table_delete_record () { get table_delete_record() {
if (!this._table_delete_record) {
this._table_delete_record = db.prepare('DELETE FROM files WHERE uuid_record = ?') if(!this._table_delete_record){
this._table_delete_record = db.prepare(`DELETE FROM files WHERE uuid_record = ?`);
} }
return this._table_delete_record return this._table_delete_record;
} }
get table_find_one () { get table_find_one() {
if (!this._table_find_one) { if(!this._table_find_one){
this._table_find_one = db.prepare('SELECT * FROM files WHERE uuid = ?') this._table_find_one = db.prepare(`SELECT * FROM files WHERE uuid = ?`);
} }
return this._table_find_one return this._table_find_one;
} }
get table_find_record () { get table_find_record(){
if (!this._table_find_record) { if(!this._table_find_record){
this._table_find_record = db.prepare('SELECT * FROM files WHERE uuid_record = ?') this._table_find_record = db.prepare(`SELECT * FROM files WHERE uuid_record = ?`);
} }
return this._table_find_record return this._table_find_record;
} }
add (uuid_record, uuid, timestamp, content, mime, hash) { add(uuid_record,uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_insert.run(uuid,uuid_record,timestamp,content,mime,hash,dt,dt);
return resolve(res?.changes);
});
}
getByRecord(uuid_record){
return new Promise(resolve => { return new Promise(resolve => {
const dt = Math.floor(Date.now()) const res = this.table_find_record.get(uuid_record);
const res = this.table_insert.run(uuid, uuid_record, timestamp, content, mime, hash, dt, dt) return resolve(res);
return resolve(res?.changes) });
}
get(uuid){
return new Promise(resolve => {
const res = this.table_find_one.get(uuid);
return resolve(res);
});
}
delete(uuid){
return new Promise(resolve=>{
const prepare = this.table_delete;
const res = prepare.run(uuid);
return resolve(res?.changes);
}) })
} }
getByRecord (uuid_record) { deleteRecord(uuid_record){
return new Promise(resolve => { return new Promise(resolve=>{
const res = this.table_find_record.get(uuid_record) const res = this.table_delete_record.run(uuid_record);
return resolve(res) return resolve(res?.changes);
}) })
} }
get (uuid) { update(uuid,timestamp,content,mime,hash){
return new Promise(resolve => { return new Promise(resolve=>{
const res = this.table_find_one.get(uuid) const res = this.table_update.run(timestamp,content,mime,hash,uuid);
return resolve(res) return resolve(res?.changes);
})
}
delete (uuid) {
return new Promise(resolve => {
const prepare = this.table_delete
const res = prepare.run(uuid)
return resolve(res?.changes)
})
}
deleteRecord (uuid_record) {
return new Promise(resolve => {
const res = this.table_delete_record.run(uuid_record)
return resolve(res?.changes)
})
}
update (uuid, timestamp, content, mime, hash) {
return new Promise(resolve => {
const res = this.table_update.run(timestamp, content, mime, hash, uuid)
return resolve(res?.changes)
}) })
} }
} }
class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) { class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
constructor () {
super() constructor(){
// TODO: Create Tables Here if not exist super();
//TODO: Create Tables Here if not exist
const table = `CREATE TABLE IF NOT EXISTS "records" ( const table = `CREATE TABLE IF NOT EXISTS "records" (
"uuid" TEXT NOT NULL UNIQUE, "uuid" TEXT NOT NULL UNIQUE,
"timestamp" INTEGER NOT NULL, "timestamp" INTEGER NOT NULL,
@ -371,182 +345,113 @@ class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
"created_at" INTEGER NOT NULL, "created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL, "updated_at" INTEGER NOT NULL,
PRIMARY KEY("uuid") PRIMARY KEY("uuid")
);` );`;
const table_idx = `CREATE INDEX IF NOT EXISTS "records_idx_timestamp" ON "records" ( const table_idx = `CREATE INDEX IF NOT EXISTS "records_idx_timestamp" ON "records" (
"timestamp" DESC "timestamp" DESC
);` );`
if (!db) { if(!db){
throw new Error('Database is not connected') throw new Error("Database is not connected");
} }
db.exec(table + '\n' + table_idx) db.exec(table + "\n" + table_idx);
this.db = db this.db = db;
} }
get table_insert () { get table_insert() {
if (!this._table_insert) { if(!this._table_insert){
this._table_insert = db.prepare('INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)') this._table_insert = db.prepare(`INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)`);
} }
return this._table_insert return this._table_insert;
} }
get table_update () { get table_update(){
if (!this._table_update) { if(!this._table_update){
this._table_update = db.prepare('UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?') this._table_update = db.prepare(`UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?`);
} }
return this._table_update return this._table_update;
} }
get table_delete () { get table_delete(){
if (!this._table_delete) { if(!this._table_delete){
this._table_delete = db.prepare('DELETE FROM records WHERE uuid = ?') this._table_delete = db.prepare(`DELETE FROM records WHERE uuid = ?`);
} }
return this._table_delete return this._table_delete;
} }
get table_fine_one () { get table_fine_one(){
if (!this._table_fine_one) { if(!this._table_fine_one){
this._table_fine_one = db.prepare('SELECT * FROM records WHERE uuid = ?') this._table_fine_one = db.prepare(`SELECT * FROM records WHERE uuid = ?`);
} }
return this._table_fine_one return this._table_fine_one;
} }
get table_find () { get table_find(){
if (!this._table_find) { if(!this._table_find){
this._table_find = db.prepare(` this._table_find = db.prepare(`
select records.*, select records.*, GROUP_CONCAT(taglink.tag,',') AS tags
(SELECT GROUP_CONCAT(tag,',') FROM taglink WHERE taglink.uuid = records.uuid) as tags
from records
order by records.timestamp DESC
limit ? offset ?
`)
}
return this._table_find
}
generator_find_sql (tags) {
let query = `
select records.*,
(SELECT GROUP_CONCAT(tag,',') FROM taglink WHERE taglink.uuid = records.uuid) as tags
from records from records
left join taglink on (taglink.uuid = records.uuid) left join taglink on (taglink.uuid = records.uuid)
{where}
group by records.uuid group by records.uuid
order by records.timestamp DESC order by records.timestamp DESC
limit ? offset ? limit ? offset ?
`; `);
const where = [];
tags.forEach(tag => {
if( tag.indexOf(':') != -1 ){
//Skip Filter
const parts = tag.split(':');
const cmd = parts[0];
const dayjs = require('dayjs');
switch (cmd){
case 'before':
const time_before = Math.floor(dayjs(parts[1]).toDate().getTime());
where.push(`records.timestamp < ${time_before}`);
break;
case 'after':
const time_after = Math.floor(dayjs(parts[1]).toDate().getTime());
where.push(`records.timestamp > ${time_after}`);
break;
} }
}else{ return this._table_find;
let cleantag = tag.replace(/\'/g,"''");
where.push(`taglink.tag = '${cleantag}'`);
} }
add(uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_insert.run(uuid,timestamp,content,mime,hash,dt,dt);
return resolve(res?.changes);
});
}
get(uuid){
return new Promise(resolve=>{
const rec = this.table_fine_one.get(uuid);
return resolve(rec);
});
}
find(search,sort=null,limit=undefined,offset=0){
//TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve=>{
const prepare = this.table_find;
const res = prepare.all(100,0);
//TODO: add count here
const rescount = {cnt:100};
return resolve({
data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
}); });
let where_string = "WHERE " + where.join(" AND "); });
query = query.replace('{where}', where_string);
console.log(query);
return db.prepare(query);
} }
add (uuid, timestamp, content, mime, hash) { delete(uuid){
return new Promise(resolve => { return new Promise(resolve=>{
const dt = Math.floor(Date.now()) const prepare = this.table_delete;
const res = this.table_insert.run(uuid, timestamp, content, mime, hash, dt, dt) const res = prepare.run(uuid);
return resolve(res?.changes) return resolve(res?.changes);
});
}
update(uuid,content,mime,hash){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_update.run(content,mime,hash,dt,uuid);
return resolve(res?.changes);
}) })
} }
get (uuid) {
return new Promise(resolve => {
const rec = this.table_fine_one.get(uuid)
return resolve(rec)
})
}
find (search, sort = null, limit = undefined, offset = 0) {
// TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve => {
if(!search || !search.tags || Object.keys(search.tags).length === 0){
const prepare = this.table_find
const res = prepare.all(100, 0)
// TODO: add count here
const rescount = { cnt: 100 }
return resolve({
data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
})
}else{
console.log(search);
const prepare = this.generator_find_sql(search.tags);
const res = prepare.all(100, 0)
const rescount = { cnt: 100 }
return resolve({
data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
})
}
})
}
delete (uuid) {
return new Promise(resolve => {
const prepare = this.table_delete
const res = prepare.run(uuid)
return resolve(res?.changes)
})
}
update (uuid, content, mime, hash) {
return new Promise(resolve => {
const dt = Math.floor(Date.now())
const res = this.table_update.run(content, mime, hash, dt, uuid)
return resolve(res?.changes)
})
}
} }
class TimeChainDataSqlite { class TimeChainDataSqlite {
@ -561,3 +466,6 @@ module.exports = {
TimeChainDataSqliteTagLink, TimeChainDataSqliteTagLink,
TimeChainDataSqliteTag TimeChainDataSqliteTag
} }

View File

@ -1,15 +0,0 @@
import Swal from 'sweetalert2'
const Toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
didOpen: (toast) => {
toast.addEventListener('mouseenter', Swal.stopTimer)
toast.addEventListener('mouseleave', Swal.resumeTimer)
}
});
module.exports = {Toast};

View File

@ -13,12 +13,6 @@
import TimechainInput from './timechain-input.riot' import TimechainInput from './timechain-input.riot'
import TimechainList from './timechain-list.riot' import TimechainList from './timechain-list.riot'
import AppDivider from './app-divider.riot' import AppDivider from './app-divider.riot'
import keymage from 'keymage';
import pubsub from 'pubsub-js';
if(window.Timechain_isElectron){
const { ipcRenderer } = window.require('electron')
}
export default { export default {
components: { components: {
@ -26,103 +20,7 @@
TimechainInput, TimechainInput,
TimechainList, TimechainList,
AppDivider AppDivider
},
onBeforeMount(){
console.log('Init');
},
onMounted(){
console.log("Application Mounted");
if(window.Timechain_isElectron){
console.log("Is Electron");
this.setupElectronKeyEvents();
}else{
console.log("Is Browser");
} }
},
setupElectronKeyEvents(){
const kevents = [];
kevents.push( keymage('ctrl-o', this.onElectronOpenDatabase.bind(this)) );
kevents.push( keymage('ctrl-n', this.onElectronCreateDatabase.bind(this)) );
this._keyEvents = kevents;
},
async onElectronOpenDatabase(){
const file = await ipcRenderer.invoke('select-sqlite-file',{});
console.log(file)
const filepath = (file && file.filePaths) ? file.filePaths[0] : false;
console.log(filepath);
if(!filepath){
return alert("Could not load file. Filename not provided.")
}
console.log("Loading new database");
const loaded = await ipcRenderer.invoke('timechain-database-open',{filename:filepath});
if(!loaded){
return alert("Database loading failed");
}else{
console.log("%c Database Reloaded","background-color:red;color:white;");
pubsub.publish('database-reloaded',true);
}
},
async onElectronCreateDatabase(){
console.log("Create Datbase Event");
const file = await ipcRenderer.invoke('create-sqlite-file',{});
console.log("Returned")
console.log(file);
//const filepath = (file && file.filePaths) ? file.filePaths[0] : false;
//console.log(filepath);
const filepath = file;
if(!filepath){
console.warn("No file name");
return alert("Could not create file. Filename not provided.",filepath);
}else{
console.log("We have a file name.. Time to make db");
}
console.log("Creating new database")
const loaded = await ipcRenderer.invoke('timechain-database-create',{filename:filepath});
if(!loaded){
return alert("Database not created");
}else{
console.log("%c Database Created","background-color:red;color:white;");
pubsub.publish('database-reloaded', true);
}
}
/*onElectronCreateDatabaseOld(){
return ipcRenderer.invoke('select-sqlite-file',{}).then(file=>{
console.log(file)
const filepath = (file && file.filePaths) ? file.filePaths[0] : false;
console.log(filepath);
if(!filepath){
return alert("Could not load file. Filename not provided.")
return ipcRenderer.invoke('timechain-database-open',{}).then(success=>{
console.log("DB RETURNED");
if(success){
console.log("%c Database Reloaded","background-color:red;color:white;");
pubsub.publish('database-reloaded',true);
}else{
console.log("Database loading failed");
}
return success;
})
}
});
}*/
} }
</script> </script>

View File

@ -54,10 +54,6 @@
onMounted(){ onMounted(){
document.addEventListener('paste', this.pasteEvent.bind(this)); document.addEventListener('paste', this.pasteEvent.bind(this));
this.event_dbreloaded = pubsub.subscribe('database-reloaded', (event,res)=>{
pubsub.publish('timechain-list-update', true);
})
this.setupKeyEvents(); this.setupKeyEvents();
}, },
/** /**
@ -90,7 +86,6 @@
this._keyevents.forEach(f=>{ this._keyevents.forEach(f=>{
f(); f();
}); });
pubsub.unsubscribe(this.event_dbreloaded);
}, },
/** /**
* Window paste event has been fire. * Window paste event has been fire.
@ -126,11 +121,8 @@
break; break;
}else if(t == 'image/png'){ }else if(t == 'image/png'){
isFile = true; isFile = true;
this.isIMAGE(e.clipboardData.items[i], 'image/png'); this.isIMAGE(e.clipboardData.items[i], 'image/png')
break; break;
}else if(t == 'image/gif'){
isFile = true;
this.isIMAGE(e.clipboardData.items[i], 'image/gif');
} }
} }

View File

@ -1,17 +0,0 @@
<timechain-item-menu>
<div id="context-menu">
<div class="item">Copy to Clipboard</div>
</div>
<script>
export default {
}
</script>
</timechain-item-menu>

View File

@ -1,22 +1,16 @@
<timechain-list> <timechain-list>
<div class="search-row">
<div class="search-tag">
<timechain-tag ontag="{onTags}" />
</div>
</div>
<div class="top-gradient"></div> <div class="top-gradient"></div>
<div class="timechain-list"> <div class="timechain-list">
<div class="timechange-item item-{r.iid}" data-iid="{r.iid}" each="{r in state.records}" key="{r.uuid}" if="{!state.loading}" oncontextmenu="{onContextMenu}"> <div class="timechange-item" each="{r in state.records}" key="{r.uuid}" if="{!state.loading}">
<div class="timechain-timestamp"> <div class="timechain-timestamp">
<timestamp-static time={r.timestamp} /> <timestamp-static time={r.timestamp} />
</div> </div>
<div class="timechain-tag-list"> <div class="timechain-tag-list">
<timechain-tag-list tags="{r.tags}" canedit="1" rid="{r.uuid}" ontag="{onTagChange}"/> <timechain-tag-list tags="{r.tags}" />
</div> </div>
<div class="timechain-item-text" if="{r.mime == 'text/plain'}"> <div class="timechain-item-text" if="{r.mime == 'text/plain'}">
@ -28,7 +22,7 @@
</div> </div>
<div class="timechain-item-image" if="{r.imageURL}"> <div class="timechain-item-image" if="{r.imageURL}">
<img src="{r.imageURL}" style="max-width:100%" > <img src="{r.imageURL}" style="max-width:100%">
</div> </div>
</div> </div>
@ -47,16 +41,8 @@
import TimechainTagList from './timechain-tag-list.riot' import TimechainTagList from './timechain-tag-list.riot'
import pubsub from 'pubsub-js' import pubsub from 'pubsub-js'
import TimechainTag from './timechain-tag.riot'
import {dataURLtoBlob} from '../lib/blobconvert.js'
import {Toast} from '../lib/toast.js'
const { const {
TimeChainDataSqliteTag, TimeChainDataSqliteRecord
TimeChainDataSqliteRecord,
TimeChainDataSqliteTagLink
} = require('../data/sqlite-client'); } = require('../data/sqlite-client');
export default { export default {
@ -68,8 +54,7 @@
components: { components: {
Raw, Raw,
TimestampStatic, TimestampStatic,
TimechainTagList, TimechainTagList
TimechainTag
}, },
onMounted(){ onMounted(){
console.log("List mounted"); console.log("List mounted");
@ -82,21 +67,12 @@
pubsub.unsubscribe(this.event_load); pubsub.unsubscribe(this.event_load);
}, },
loadRecords(search){ loadRecords(){
this.update({loading:true}); this.update({loading:true});
const TR = new TimeChainDataSqliteRecord(); const TR = new TimeChainDataSqliteRecord();
if(!search) { TR.find({},null,100,0).then(records=>{
if(this.last_search){
search = this.last_search
}else{
search = {};
}
}
TR.find(search,null,100,0).then(records=>{
console.log("Records", records); console.log("Records", records);
if(records) records.data.forEach(r=>r.iid=this.uid());
this.update({records:records.data,loading:false}); this.update({records:records.data,loading:false});
}).then(()=>{ }).then(()=>{
this.processImages(); this.processImages();
@ -106,114 +82,12 @@
processImages(){ processImages(){
var URLObj = window.URL || window.webkitURL; var URLObj = window.URL || window.webkitURL;
this.state.records.forEach(r=>{ this.state.records.forEach(r=>{
if(typeof r.mime == 'string' && r.mime.startsWith("image/")){ if(r.mime == "image/jpeg" || r.mime == "image/png"){
r.imageURL = r.content; r.imageURL = r.content;
} }
}); });
this.update(); this.update();
},
onTags(tags){
if(tags==''){
return this.loadRecords({});
}
console.log(tags);
tags = JSON.parse(tags);
const search = [];
tags.map(t=>{
//console.log(t.value);
search.push(t.value);
});
this.loadRecords({
tags:search
})
},
onContextMenu(e){
const t = e.target.closest('.timechange-item');
const id = t.dataset.iid;
const record = this.state.records.find(r=>r.iid==id);
console.log("Right click", id, record);
if(!record) return;
if(record.mime && record.mime.includes('image/')){
const b = dataURLtoBlob(record.content);
console.log(b);
const items = { [b.type]: b };
const clipboardItem = new ClipboardItem(items);
navigator.clipboard.write([clipboardItem]).then(()=>{
Toast.fire({
icon: 'success',
title: 'Copied to clipboard'
})
});
}else if(record.mime && (record.mime == 'text/html' || record.mime == 'text/plain')){
const t = record.mime;
const b = new Blob([record.content], {type:t});
console.log(t);
console.log(b);
const data = [new ClipboardItem({ [t]: b })];
navigator.clipboard.write(data).then(()=>{
Toast.fire({
icon: 'success',
title: 'Copied to clipboard'
})
});
}else{
alert("Can't copy");
}
},
onTagChange(tags,rid){
const TAG = new TimeChainDataSqliteTag();
const TL = new TimeChainDataSqliteTagLink();
console.log('LIST', tags,rid);
if(typeof tags == 'string'){
tags = JSON.parse(tags);
}
if(!rid) return;
return TL.deleteRecord(rid).then(()=>{
const tasks = tags.map(t=>{
const tag = t.value;
if(!tag) return false;
return TAG.has(tag).then(v=>{
return v;
}).then(v=>{
if(!v){
return TAG.add(tag);
}
return true;
}).then(v=>{
TL.add(rid,tag);
})
});
return Promise.all(tasks).then(()=>{
console.log("finish adding tags");
});
})
} }
} }

View File

@ -1,8 +1,7 @@
<timechain-tag-list> <timechain-tag-list>
<div class="timechain-tags" ondblclick="{goEdit}"> <div class="timechain-tags">
<span class="timechain-tag" each="{t in state.tags}" if="{!state.isEditing}">{t}</span> <span class="timechain-tag" each="{t in state.tags}">{t}</span>
<timechain-tag if="{state.isEditing}" tags="{state.tags_text}" rid="{state.rid}" ontag="{onTagChange}" onEnter="{onEnter}"></timechain-tag>
</div> </div>
@ -12,7 +11,6 @@
text-align: center; text-align: center;
margin-bottom: 0.5em; margin-bottom: 0.5em;
margin-top: 0.5em; margin-top: 0.5em;
min-height: 0.5em;
} }
.timechain-tags .timechain-tag { .timechain-tags .timechain-tag {
@ -27,71 +25,16 @@
<script> <script>
import TimechainTag from './timechain-tag.riot'
export default { export default {
state: { state: {
tags: [], tags: []
tags_text: "",
canEdit: false,
isEditing: false,
key: '',
rid: '',
}, },
components: { onMounted: function(props){
TimechainTag
},
onBeforeMount(props){
if(props.canedit=='1'){
this.state.canEdit = true;
}
if(props.rid){
this.state.rid = props.rid;
}else{
//console.log("No rid in list");
}
},
onMounted(props){
if(!props.tags) return; if(!props.tags) return;
this.update({ this.update({
tags_text: props.tags,
tags: props.tags.split(',') tags: props.tags.split(',')
}) })
if(this.state.tags.length==0 && this.state.canEdit){
this.update({isEditing:true});
}
},
goEdit(e){
if(!this.state.canEdit) return;
this.state.isEditing = !this.state.isEditing;
this.update();
},
onTagChange(tags,key){
console.log(tags,key);
if(typeof tags == 'string'){
tags = JSON.parse(tags);
}
let tagArr = [];
tags.forEach(t=>{
tagArr.push(t.value);
});
this.update({
tags_text: tagArr.join(','),
tags: tagArr
});
if(this.props?.ontag){
this.props.ontag(tags,key);
}
},
onEnter(){
this.update({isEditing:false});
} }
} }

View File

@ -1,132 +1,27 @@
<timechain-tag> <timechain-tag>
<div class="timechain-tagging"> <div class="timechain-tagging">
<input id="{state.id}" placeholder="tag your paste"> <input id="tagging" placeholder="tag your paste">
</div> </div>
<script> <script>
import Tagify from '@yaireo/tagify' import Tagify from '@yaireo/tagify'
const pubsub = require('pubsub-js'); const pubsub = require('pubsub-js');
const debounce = require('debounce');
const {
TimeChainDataSqliteTag
} = require('../data/sqlite-client');
export default { export default {
state:{ onMounted(){
id:'tagging', const inputElm = this.$('#tagging');
rid: ''
},
onBeforeMount(props){
this.state.id = "tagging"+this.makeid(5);
this.tagdb = new TimeChainDataSqliteTag();
},
onMounted(props, state){
const inputElm = this.$('#'+this.state.id);
if(props.rid){
state.rid = props.rid;
}else{
//console.log("no rid");
}
if(props.tags){
console.log("We have tags", props.tags);
inputElm.value = props.tags;
}else{
console.log("No tags");
console.log(props.tags);
}
this.tagify = new Tagify(inputElm); this.tagify = new Tagify(inputElm);
inputElm.addEventListener('change', this.onChange.bind(this)); inputElm.addEventListener('change', this.onChange.bind(this));
this.event_clean = pubsub.subscribe('tag.clean', this.onClean.bind(this)); this.event_clean = pubsub.subscribe('tag.clean', this.onClean.bind(this));
this.tagify.on('input', debounce( this.onInputEvent.bind(this), 400) )
if(props.tags){
this.setFocus();
this.tagify.on('keydown', this.onKeyDown.bind(this) );
}
}, },
onUnmounted(){ onUnmounted(){
pubsub.unsubscribe(this.event_clean); pubsub.unsubscribe(this.event_clean);
}, },
onChange(e){ onChange(e){
if(this.props?.ontag){
if(this.state.rid){
this.props.ontag(e.target.value, this.state.rid);
}else{
console.log("No RID");
this.props?.ontag(e.target.value); this.props?.ontag(e.target.value);
}
}else{
console.log("No on tag function");
}
}, },
onClean(){ onClean(){
this.tagify.removeAllTags(); this.tagify.removeAllTags();
},
makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
},
setFocus(){
const i = this.$('#'+this.state.id);
if(!i) {
return console.log("No input");
}
const tag = i.closest('.timechain-tagging');
if(!tag){
return console.log("No Tagg");
}
const s = tag.querySelector('.tagify__input');
if(!s){
return console.log("No tag imput");
}
s.focus();
},
onKeyDown(e){
console.log(e);
if(e.detail && e.detail.originalEvent && (e.detail.originalEvent.key == "Enter" || e.detail.originalEvent.key == "Escape") ){
//e.detail.originalEvent.stopPropagation();
//e.detail.originalEvent.preventDefault();
//console.log("ENTER");
if(this.props?.onEnter){
this.props.onEnter(e.detail.originalEvent);
}
}
},
onInputEvent(e){
const val = e.detail.value;
this.tagify.whitelist = null;
console.log('%c Value '+val,'background-color:black;');
this.tagify.loading(true); //dropdown.hide();
this.tagdb.like(val).then(res=>{
const cleaned = res.map(r=>r.tag);
console.log(cleaned);
this.tagify.whitelist = cleaned;
this.tagify.loading(false).dropdown.show(val);
})
} }
} }
</script> </script>

View File

@ -1,3 +0,0 @@
<timechain-tags>
</timechain-tags>

View File

@ -1,23 +1,34 @@
import './src/less/main.less' import './src/less/main.less';
import './src/css/tagify.scss' import './src/css/tagify.scss';
import * as Sentry from "@sentry/browser";
import { BrowserTracing } from "@sentry/tracing";
Sentry.init({
dsn: "http://a12d5c5f800b406f8d1c0c5d2ed63a78@216.128.138.128:8000/1",
// Alternatively, use `process.env.npm_package_version` for a dynamic release version
// if your build tool supports it.
release: "timechain@1.0.0",
integrations: [new BrowserTracing()],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
});
import App from './src/ui/app.riot' import App from './src/ui/app.riot'
import { component, install } from 'riot' import { component } from 'riot'
let id = 0 window.debugging = true;
install(function(component) { setTimeout(()=>{
// all components will pass through here document.getElementById('main-loading')?.remove();
component.uid = ()=>{ component(App)(document.getElementById('timechain'));
return ++id; Sentry.captureMessage('Application Started');
} },0)
})
window.debugging = true
setTimeout(() => {
document.getElementById('main-loading')?.remove()
component(App)(document.getElementById('timechain'))
Sentry.captureMessage('Application Started')
}, 0)