Compare commits

..

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

16 changed files with 828 additions and 4327 deletions

44
main.js
View File

@ -1,38 +1,28 @@
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({
autoHideMenuBar: true, autoHideMenuBar: true,
width: 800, width: 800,
height: 600, height: 600,
icon: __dirname + '/src/img/chains.png', icon: __dirname + '/src/img/chains.png',
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')
} }
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;
case 'add':
res = await DbRecord.add(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash) switch(arg.func){
break case 'add':
case 'delete': res = await DbRecord.add(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash);
res = await DbRecord.delete(arg.uuid) break;
break case 'delete':
case 'update': res = await DbRecord.delete(arg.uuid);
res = await DbRecord.update(arg.uuid, arg.content, arg.mime, arg.hash) break;
break case 'update':
case 'find': res = await DbRecord.update(arg.uuid,arg.content,arg.mime,arg.hash);
res = await DbRecord.find(arg.search, arg.sort, arg.limit, arg.offset) break;
break case 'find':
case 'get': res = await DbRecord.find(arg.search,arg.sort,arg.limit,arg.offset);
res = await DbRecord.get(arg.uuid) break;
break case 'get':
default: res = await DbRecord.get(arg.uuid);
res = new Error('unknown command') break;
} default:
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;
case 'add':
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;
case 'like':
res = await DbTag.like(arg.str)
break;
default:
res = new Error('Command Unknown')
}
return res switch(arg.func){
}) case 'add':
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;
default:
res = new Error('Command Unknown');
}
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;
case 'add':
res = await DbTagLink.add(arg.uuid, arg.tag)
break
case 'delete':
res = await DbTagLink.delete(arg.uuid, arg.tag)
break
case 'delete-tag':
res = await DbTagLink.deleteTag(arg.tag)
break
case 'delete-record':
res = await DbTagLink.deleteRecord(arg.uuid)
break
case 'get-records':
res = await DbTagLink.getRecords(atg.tag)
break
case 'get-tags':
res = await DbTagLink.getTags(atg.uuid)
break
default:
res = Error('Commande not known')
}
return res switch(arg.func){
}) case 'add':
res = await DbTagLink.add(arg.uuid,arg.tag)
break;
case 'delete':
res = await DbTagLink.delete(arg.uuid,arg.tag);
break;
case 'delete-tag':
res = await DbTagLink.deleteTag(arg.tag);
break;
case 'delete-record':
res = await DbTagLink.deleteRecord(arg.uuid);
break;
case 'get-records':
res = await DbTagLink.getRecords(atg.tag);
break;
case 'get-tags':
res = await DbTagLink.getTags(atg.uuid);
break;
default:
res = Error("Commande not known");
}
return res;
});

View File

@ -1,563 +1,471 @@
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(){
const table = `CREATE TABLE IF NOT EXISTS "tags" ( super();
"tag" TEXT UNIQUE, const table = `CREATE TABLE IF NOT EXISTS "tags" (
"created_at" INTEGER NOT NULL, "tag" TEXT UNIQUE,
"updated_at" INTEGER NOT NULL, "created_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
}
get table_add () {
if (!this._table_add) {
this._table_add = db.prepare('INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)')
}
return this._table_add
}
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM tags WHERE tag = ?')
}
return this._table_delete
}
get table_get () {
if (!this._table_get) {
this._table_get = db.prepare('SELECT * FROM tags WHERE tag = ?')
}
return this._table_get
}
get table_get_like () {
if (!this._table_get) {
this._table_get = db.prepare('SELECT * FROM tags WHERE tag LIKE ?')
}
return this._table_get
}
get table_get_all() {
if (!this._table_get_all) {
this._table_get_all = db.prepare('SELECT * FROM tags')
}
return this._table_get_all
}
get table_count () {
if (!this._table_count) {
this._table_count = db.prepare('SELECT count(tag) as cnt FROM tags WHERE tag = ?')
}
return this._table_count
}
add (tag) {
return new Promise(resolve => {
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)
} }
})
})
}
all() {
return new Promise(resolve => {
const res = this.table_get_all.all();
return resolve(res);
})
}
like(str){ db.exec(table + "\n" + table_idx);
return new Promise(resolve => { this.db = db;
const res = this.table_get_like.all(str + '%'); }
return resolve(res);
})
}
delete (tag) { get table_add(){
return new Promise(resolve => { if(!this._table_add){
const dt = Math.floor(Date.now()) this._table_add = db.prepare("INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)");
const res = this.table_delete.run(tag) }
return resolve(res?.changes) return this._table_add;
}) }
}
get (tag) { get table_delete(){
return new Promise(resolve => { if(!this._table_delete){
const res = this.table_get.get(tag) this._table_delete = db.prepare("DELETE FROM tags WHERE tag = ?");
return resolve(res) }
}) return this._table_delete;
} }
get table_get(){
if(!this._table_get){
this._table_get = db.prepare("SELECT * FROM tags WHERE tag = ?");
}
return this._table_get;
}
get table_count(){
if(!this._table_count){
this._table_count = db.prepare("SELECT count(tag) as cnt FROM tags WHERE tag = ?");
}
return this._table_count;
}
add(tag){
return new Promise(resolve=>{
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);
}
});
});
}
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);
});
}
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()
this.table = `CREATE TABLE IF NOT EXISTS "taglink" ( constructor(){
"uuid" TEXT NOT NULL, super();
"tag" TEXT NOT NULL,
"created_at" INTEGER NOT NULL this.table = `CREATE TABLE IF NOT EXISTS "taglink" (
);` "uuid" TEXT NOT NULL,
this.table_idx = ` "tag" TEXT NOT NULL,
"created_at" INTEGER NOT NULL
);`;
this.table_idx = `
CREATE INDEX IF NOT EXISTS "uuid_idx" ON "taglink" ( CREATE INDEX IF NOT EXISTS "uuid_idx" ON "taglink" (
"uuid" "uuid"
); );
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 () {
if (!this._table_add) {
this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)')
} }
return this._table_add
} get table_add(){
if(!this._table_add){
get table_delete () { this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)');
if (!this._table_delete) { }
this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?') return this._table_add;
} }
return this._table_delete
}
get table_delete_tag () { get table_delete(){
if (!this._table_delete_tag) { if(!this._table_delete){
this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?') this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?');
}
return this._table_delete;
} }
return this._table_delete_tag
}
get table_delete_record () { get table_delete_tag(){
if (!this._table_delete_record) { if(!this._table_delete_tag){
this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?') this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?');
}
return this._table_delete_tag;
} }
return this._table_delete_record
}
get table_get_records () { get table_delete_record(){
if (!this._table_get_records) { if(!this._table_delete_record){
this._table_get_records = db.prepare('SELECT * FROM taglink WHERE tag=?') this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?');
}
return this._table_delete_record;
} }
return this._table_get_records
}
get table_get_tags () { get table_get_records(){
if (!this._table_get_tags) { if(!this._table_get_records){
this._table_get_tags = db.prepare('SELECT * FROM taglink WHERE uuid=?') this._table_get_records = db.prepare("SELECT * FROM taglink WHERE tag=?");
}
return this._table_get_records;
} }
return this._table_get_tags
}
add (uuid, tag) { get table_get_tags(){
return new Promise(resolve => { if(!this._table_get_tags){
const prepare = this.table_add this._table_get_tags = db.prepare("SELECT * FROM taglink WHERE uuid=?");
const dt = Math.floor(Date.now()) }
const res = prepare.run(uuid, tag, dt) return this._table_get_tags;
return resolve(res?.changes) }
})
}
delete (uuid, tag) { add(uuid,tag){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_delete const prepare = this.table_add;
const res = prepare.run(uuid, tag) const dt = Math.floor(Date.now());
return resolve(res?.changes) const res = prepare.run(uuid,tag,dt);
}) return resolve(res?.changes);
} });
}
deleteTag (tag) { delete(uuid,tag){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_delete_tag const prepare = this.table_delete;
const res = prepare.run(tag) const res = prepare.run(uuid,tag);
return resolve(res?.changes) return resolve(res?.changes);
}) });
} }
deleteRecord (uuid) { deleteTag(tag){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_delete_record const prepare = this.table_delete_tag;
const res = prepare.run(uuid) const res = prepare.run(tag);
return resolve(res?.changes) return resolve(res?.changes);
}) });
} }
getRecords (tag) { deleteRecord(uuid){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_get_records const prepare = this.table_delete_record;
const res = prepare.all(tag) const res = prepare.run(uuid);
return resolve(res) return resolve(res?.changes);
}) });
} }
getTags (uuid) { getRecords(tag){
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_get_tags const prepare = this.table_get_records;
const res = prepare.all(uuid) const res = prepare.all(tag);
return resolve(res) 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(){
const table = `CREATE TABLE IF NOT EXISTS "files" ( super();
"uuid" TEXT UNIQUE, const table = `CREATE TABLE IF NOT EXISTS "files" (
"uuid_record" TEXT NOT NULL, "uuid" TEXT UNIQUE,
"timestamp" INTEGER NOT NULL, "uuid_record" TEXT NOT NULL,
"content" BLOB NOT NULL, "timestamp" INTEGER NOT NULL,
"mime" TEXT NOT NULL, "content" BLOB NOT NULL,
"hash" TEXT, "mime" TEXT NOT NULL,
"created_at" INTEGER NOT NULL, "hash" TEXT,
"updated_at" INTEGER NOT NULL, "created_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);
this.db = db;
} }
db.exec(table + '\n' + table_idx) get table_insert() {
this.db = db if(!this._table_insert){
} this._table_insert = db.prepare(`INSERT INTO files (uuid,uuid_record,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)`);
}
get table_insert () { return 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 (?,?,?,?,?,?,?,?)')
}
return this._table_insert
}
get table_update () {
if (!this._table_update) {
this._table_update = db.prepare('UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?')
}
return this._table_update
}
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM files WHERE uuid = ?')
}
return this._table_delete
}
get table_delete_record () {
if (!this._table_delete_record) {
this._table_delete_record = db.prepare('DELETE FROM files WHERE uuid_record = ?')
} }
return this._table_delete_record get table_update() {
} if(!this._table_update){
this._table_update = db.prepare(`UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?`);
get table_find_one () { }
if (!this._table_find_one) { return this._table_update;
this._table_find_one = db.prepare('SELECT * FROM files WHERE uuid = ?')
} }
return this._table_find_one
}
get table_find_record () { get table_delete() {
if (!this._table_find_record) { if(!this._table_delete){
this._table_find_record = db.prepare('SELECT * FROM files WHERE uuid_record = ?') this._table_delete = db.prepare(`DELETE FROM files WHERE uuid = ?`);
}
return this._table_delete;
} }
return this._table_find_record
}
add (uuid_record, uuid, timestamp, content, mime, hash) { get table_delete_record() {
return new Promise(resolve => {
const dt = Math.floor(Date.now()) if(!this._table_delete_record){
const res = this.table_insert.run(uuid, uuid_record, timestamp, content, mime, hash, dt, dt) this._table_delete_record = db.prepare(`DELETE FROM files WHERE uuid_record = ?`);
return resolve(res?.changes) }
})
}
getByRecord (uuid_record) { return this._table_delete_record;
return new Promise(resolve => { }
const res = this.table_find_record.get(uuid_record)
return resolve(res)
})
}
get (uuid) { get table_find_one() {
return new Promise(resolve => { if(!this._table_find_one){
const res = this.table_find_one.get(uuid) this._table_find_one = db.prepare(`SELECT * FROM files WHERE uuid = ?`);
return resolve(res) }
}) return this._table_find_one;
} }
delete (uuid) { get table_find_record(){
return new Promise(resolve => { if(!this._table_find_record){
const prepare = this.table_delete this._table_find_record = db.prepare(`SELECT * FROM files WHERE uuid_record = ?`);
const res = prepare.run(uuid) }
return resolve(res?.changes) return this._table_find_record;
}) }
}
deleteRecord (uuid_record) { add(uuid_record,uuid,timestamp,content,mime,hash){
return new Promise(resolve => { return new Promise(resolve=>{
const res = this.table_delete_record.run(uuid_record) const dt = Math.floor(Date.now());
return resolve(res?.changes) const res = this.table_insert.run(uuid,uuid_record,timestamp,content,mime,hash,dt,dt);
}) return resolve(res?.changes);
} });
}
update (uuid, timestamp, content, mime, hash) { getByRecord(uuid_record){
return new Promise(resolve => { return new Promise(resolve => {
const res = this.table_update.run(timestamp, content, mime, hash, uuid) const res = this.table_find_record.get(uuid_record);
return resolve(res?.changes) return resolve(res);
}) });
} }
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);
})
}
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();
const table = `CREATE TABLE IF NOT EXISTS "records" ( //TODO: Create Tables Here if not exist
"uuid" TEXT NOT NULL UNIQUE, const table = `CREATE TABLE IF NOT EXISTS "records" (
"timestamp" INTEGER NOT NULL, "uuid" TEXT NOT NULL UNIQUE,
"content" TEXT NOT NULL, "timestamp" INTEGER NOT NULL,
"mime" TEXT NOT NULL, "content" TEXT NOT NULL,
"hash" TEXT, "mime" TEXT NOT NULL,
"created_at" INTEGER NOT NULL, "hash" TEXT,
"updated_at" INTEGER NOT NULL, "created_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)
this.db = db
}
get table_insert () {
if (!this._table_insert) {
this._table_insert = db.prepare('INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)')
}
return this._table_insert
}
get table_update () {
if (!this._table_update) {
this._table_update = db.prepare('UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?')
}
return this._table_update
}
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM records WHERE uuid = ?')
}
return this._table_delete
}
get table_fine_one () {
if (!this._table_fine_one) {
this._table_fine_one = db.prepare('SELECT * FROM records WHERE uuid = ?')
}
return this._table_fine_one
}
get table_find () {
if (!this._table_find) {
this._table_find = db.prepare(`
select records.*,
(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
left join taglink on (taglink.uuid = records.uuid)
{where}
group by records.uuid
order by records.timestamp DESC
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{
let cleantag = tag.replace(/\'/g,"''");
where.push(`taglink.tag = '${cleantag}'`);
} }
}); db.exec(table + "\n" + table_idx);
this.db = db;
}
let where_string = "WHERE " + where.join(" AND "); get table_insert() {
if(!this._table_insert){
this._table_insert = db.prepare(`INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)`);
}
return this._table_insert;
}
query = query.replace('{where}', where_string); get table_update(){
if(!this._table_update){
this._table_update = db.prepare(`UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?`);
}
return this._table_update;
}
console.log(query); get table_delete(){
if(!this._table_delete){
this._table_delete = db.prepare(`DELETE FROM records WHERE uuid = ?`);
}
return this._table_delete;
}
return db.prepare(query); get table_fine_one(){
if(!this._table_fine_one){
this._table_fine_one = db.prepare(`SELECT * FROM records WHERE uuid = ?`);
}
return this._table_fine_one;
}
} get table_find(){
if(!this._table_find){
this._table_find = db.prepare(`
select records.*, GROUP_CONCAT(taglink.tag,',') AS tags
from records
left join taglink on (taglink.uuid = records.uuid)
group by records.uuid
order by records.timestamp DESC
limit ? offset ?
`);
}
add (uuid, timestamp, content, mime, hash) { return this._table_find;
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) { add(uuid,timestamp,content,mime,hash){
return new Promise(resolve => { return new Promise(resolve=>{
const rec = this.table_fine_one.get(uuid) const dt = Math.floor(Date.now());
return resolve(rec) 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) { find(search,sort=null,limit=undefined,offset=0){
// TODO: add logic for seach and sort (Currently does nothing) //TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve => { return new Promise(resolve=>{
const prepare = this.table_find;
if(!search || !search.tags || Object.keys(search.tags).length === 0){ const res = prepare.all(100,0);
const prepare = this.table_find
const res = prepare.all(100, 0) //TODO: add count here
const rescount = {cnt:100};
// TODO: add count here return resolve({
const rescount = { cnt: 100 } data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
});
});
}
return resolve({ delete(uuid){
data: res, return new Promise(resolve=>{
count: rescount?.cnt, const prepare = this.table_delete;
limit: limit, const res = prepare.run(uuid);
offset: offset 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);
}) })
}
}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 {
} }
module.exports = { module.exports = {
ConnectToDatabase, ConnectToDatabase,
TimeChainDataSqlite, TimeChainDataSqlite,
TimeChainDataSqliteRecord, TimeChainDataSqliteRecord,
TimeChainDataSqliteFile, TimeChainDataSqliteFile,
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 {
@ -26,72 +24,17 @@
</style> </style>
<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){ this.props?.ontag(e.target.value);
if(this.state.rid){
this.props.ontag(e.target.value, this.state.rid);
}else{
console.log("No RID");
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)