Skip to content

Commit ba78075

Browse files
committed
sync via git, wip
1 parent 566de06 commit ba78075

File tree

12 files changed

+481
-45
lines changed

12 files changed

+481
-45
lines changed

actionHandling/handler/DefaultActions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import DeleteFolderAction from 'src/tabsets/actions/DeleteFolderAction.vue'
1111
import DeleteTabsetAction from 'src/tabsets/actions/DeleteTabsetAction.vue'
1212
import EditFolderAction from 'src/tabsets/actions/EditFolderAction.vue'
1313
import EditTabsetAction from 'src/tabsets/actions/EditTabsetAction.vue'
14+
import ExportTabsetAction from 'src/tabsets/actions/ExportTabsetAction.vue'
1415
import OpenAllInMenuAction from 'src/tabsets/actions/OpenAllInMenuAction.vue'
1516
import OpenTabsetAction from 'src/tabsets/actions/OpenTabsetAction.vue'
1617
import ShowGalleryAction from 'src/tabsets/actions/ShowGalleryAction.vue'
@@ -44,6 +45,10 @@ export class DefaultActions {
4445

4546
actions.push(OpenAllInMenuAction)
4647

48+
if (useFeaturesStore().hasFeature(FeatureIdent.DEV_MODE)) {
49+
actions.push(ExportTabsetAction)
50+
}
51+
4752
// open existing tabset for url
4853
if (!DefaultActions.alreadyInTabset() && DefaultActions.tabsetsForUrl().length > 0) {
4954
actions.push(OpenTabsetAction)

actions/ExportTabsetAction.vue

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<template>
2+
<q-separator inset />
3+
<ContextMenuItem
4+
v-close-popup
5+
@was-clicked="clicked()"
6+
icon="o_file_download"
7+
color="primary"
8+
label="Export Tabset" />
9+
</template>
10+
11+
<script lang="ts" setup>
12+
import { useQuasar } from 'quasar'
13+
import ContextMenuItem from 'src/core/components/helper/ContextMenuItem.vue'
14+
import { ActionProps } from 'src/tabsets/actions/models/ActionProps'
15+
import ExportDialog from 'src/tabsets/dialogues/ExportDialog.vue'
16+
17+
const $q = useQuasar()
18+
19+
const props = defineProps<ActionProps>()
20+
21+
const clicked = () => {
22+
const filename = `tabset-${props.tabset.name}-${import.meta.env.PACKAGE_VERSION}.json`
23+
$q.dialog({ component: ExportDialog, componentProps: { filename: filename, tabset: props.tabset } }).onOk(
24+
(tabsetId: any) => {
25+
//useTabsetService().selectTabset(tabsetId)
26+
},
27+
)
28+
}
29+
</script>

commands/github/GithubLogCommand.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ export class GithubLogCommand extends GithubCommands<string> {
1212
}
1313

1414
async execute(): Promise<ExecutionResult<string>> {
15+
const useData = JSON.stringify(this.entry, null, 2)
16+
//console.log('useData', useData, this.entry)
1517
if (!LocalStorage.getItem(GITHUB_LOG)) {
1618
return Promise.resolve(new ExecutionResult('done', 'not active'))
1719
}
1820
const today = new Date().toISOString().slice(0, 10)
1921
const filename = (this.event + '_' + new Date().getTime()).replace(STRIP_CHARS_IN_USER_INPUT, '')
2022
try {
21-
const useData = JSON.stringify(this.entry, null, 2)
22-
const result = await this.githubPutContentRequest('logs/' + today, filename, useData)
23+
const result = await this.githubPutContentRequest('logs/' + today, useData)
2324
console.log('result', result)
2425
return Promise.resolve(new ExecutionResult('', 'done'))
2526
} catch (error) {
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { LocalStorage } from 'quasar'
2+
import BrowserApi from 'src/app/BrowserApi'
3+
import { GITHUB_AUTO_SYNC, GITHUB_AUTO_SYNC_LASTUPDATE } from 'src/boot/constants'
4+
import { ExecutionResult } from 'src/core/domain/ExecutionResult'
5+
import { Notebook } from 'src/notes/models/Notebook'
6+
import { useNotesStore } from 'src/notes/stores/NotesStore'
7+
import { Space } from 'src/spaces/models/Space'
8+
import { useSpacesStore } from 'src/spaces/stores/spacesStore'
9+
import { GithubCommands } from 'src/tabsets/commands/github/GithubCommands'
10+
import {
11+
NoteEvent,
12+
SpaceEvent,
13+
TabEvent,
14+
TabsetEvent,
15+
TabsetEventType,
16+
} from 'src/tabsets/commands/github/GithubWriteEventCommand'
17+
import { Tab } from 'src/tabsets/models/Tab'
18+
import { Tabset } from 'src/tabsets/models/Tabset'
19+
import { useTabsetsStore } from 'src/tabsets/stores/tabsetsStore'
20+
import { useUiStore } from 'src/ui/stores/uiStore'
21+
22+
export class GithubReadEventsCommand extends GithubCommands<string> {
23+
constructor(public lastUpdate: number) {
24+
super()
25+
}
26+
27+
async execute(): Promise<ExecutionResult<string>> {
28+
const githubPath = 'events'
29+
//console.log('useData', useData, this.event)
30+
if (!LocalStorage.getItem(GITHUB_AUTO_SYNC)) {
31+
return Promise.resolve(new ExecutionResult('done', 'not active'))
32+
}
33+
try {
34+
const events = await this.githubGetContentRequest(githubPath)
35+
//console.log('events', events)
36+
// if (events.status == 404) {
37+
// await this.githubPutContentRequest(githubPath, useData)
38+
39+
if (events.ok) {
40+
let firstErrorOccuredAt: number | undefined = undefined
41+
let lastSuccessOccuredAt: number | undefined = undefined
42+
43+
const existing = await events.json()
44+
const decodedContent = decodeURIComponent(escape(window.atob(existing.content)))
45+
// console.log('got existing', decodedContent)
46+
let index = 0
47+
const lines = decodedContent.split('\n')
48+
for (const line of lines) {
49+
if (line.trim().length === 0) {
50+
continue
51+
}
52+
const parts = line.split('|')
53+
let timestampLine = 0
54+
try {
55+
timestampLine = Number.parseInt(parts[0]!)
56+
if (timestampLine < this.lastUpdate) {
57+
continue
58+
}
59+
60+
const progress = Math.round((100 * index) / lines.length)
61+
useUiStore().setProgress(progress / 100, `syncing... ${progress}%`)
62+
63+
if (parts.length < 5) {
64+
console.warn(`found incomplete line ${line}"`)
65+
continue
66+
}
67+
68+
switch (parts[1]) {
69+
case 'tabset':
70+
if (parts[3] && parts[5]) {
71+
// 1743063915313|tabset|added|0339a468-4df1-4e00-94ba-496171cf8d88||Mails
72+
const tabsetEvent = new TabsetEvent(
73+
parts[2] as TabsetEventType,
74+
parts[3],
75+
parts[4],
76+
parts[5]!,
77+
parts[6] ? parts[6].split(',') : [],
78+
)
79+
if (tabsetEvent.event === 'added') {
80+
if (tabsetEvent.parentId) {
81+
// console.log('processing line', line)
82+
const folderChain = useTabsetsStore().getFolderChain(tabsetEvent.parentId)
83+
// console.log('folderChain', folderChain)
84+
if (folderChain.length > 0) {
85+
const rootTabset = useTabsetsStore().getTabset(folderChain[folderChain.length - 1]!)
86+
//console.log('found root tabset', rootTabset)
87+
const tabsetOrFolder = useTabsetsStore().getActiveFolder(rootTabset!, tabsetEvent.parentId)
88+
//console.log('tabsetOrfolder', tabsetOrFolder)
89+
if (tabsetOrFolder) {
90+
const newTabset = new Tabset(
91+
tabsetEvent.tabsetId,
92+
tabsetEvent.name,
93+
[],
94+
[],
95+
tabsetEvent.spaces,
96+
)
97+
tabsetOrFolder.folders.push(newTabset)
98+
await useTabsetsStore().saveTabset(rootTabset!)
99+
}
100+
}
101+
} else {
102+
console.log('processing line II', line)
103+
const newTabset = new Tabset(tabsetEvent.tabsetId, tabsetEvent.name, [], [], tabsetEvent.spaces)
104+
await useTabsetsStore().addTabset(newTabset)
105+
}
106+
}
107+
}
108+
break
109+
case 'tab':
110+
if (parts[3] && parts[6]) {
111+
const tabsetEvent = new TabEvent(parts[2] as TabsetEventType, parts[3], parts[4], parts[5]!, parts[6])
112+
const ts = useTabsetsStore().getTabset(tabsetEvent.tabsetId)
113+
if (!ts) {
114+
console.log('could not find tabset for id', tabsetEvent.tabsetId)
115+
break
116+
}
117+
if (tabsetEvent.event === 'added') {
118+
ts.tabs.push(
119+
new Tab(tabsetEvent.tabId!, BrowserApi.createChromeTabObject(tabsetEvent.name, tabsetEvent.url!)),
120+
)
121+
await useTabsetsStore().saveTabset(ts)
122+
}
123+
}
124+
break
125+
case 'space':
126+
if (parts[3]) {
127+
const spaceEvent = new SpaceEvent(parts[2] as TabsetEventType, parts[3]!, undefined, parts[5]!)
128+
if (spaceEvent.event === 'added') {
129+
// useTabsetsStore().addTabset(new Tabset(tabsetEvent.tabsetId!, tabsetEvent.name, []))
130+
const space = new Space(spaceEvent.spaceId, spaceEvent.name)
131+
console.log('space', space)
132+
await useSpacesStore().addSpace(space)
133+
}
134+
}
135+
break
136+
case 'note':
137+
// console.log('processing line', line)
138+
if (parts[3]) {
139+
const decoded = atob(parts[5]!)
140+
//console.log('decoded', decoded)
141+
const noteEvent = new NoteEvent(
142+
parts[2] as TabsetEventType,
143+
parts[3]!,
144+
parts[4]!,
145+
JSON.parse(decoded),
146+
)
147+
if (noteEvent.event === 'added') {
148+
//console.log('noteEvent', noteEvent)
149+
// const notebook = new Notebook(uid(), noteEvent.tabsetId, NotebookType.TABSET, noteEvent.name)
150+
// console.log('notebook', notebook)
151+
await useNotesStore().saveNotebook(noteEvent.content as unknown as Notebook)
152+
}
153+
}
154+
break
155+
default:
156+
console.log(`unknown part '${parts[1]}' in '${line}'`)
157+
}
158+
} catch (err: any) {
159+
if (!firstErrorOccuredAt) {
160+
firstErrorOccuredAt = timestampLine - 1
161+
}
162+
console.warn('issue with sync: ', err)
163+
}
164+
lastSuccessOccuredAt = timestampLine
165+
index++
166+
}
167+
168+
// const sha = existing['sha' as keyof object]
169+
// await this.githubPutContentRequest(githubPath, decodedContent + useData, sha)
170+
console.log(
171+
`sync finished with firstErrorOccuredAt ${firstErrorOccuredAt}, lastSuccessOccuredAt ${lastSuccessOccuredAt}`,
172+
)
173+
if (firstErrorOccuredAt) {
174+
LocalStorage.setItem(GITHUB_AUTO_SYNC_LASTUPDATE, firstErrorOccuredAt)
175+
} else if (lastSuccessOccuredAt) {
176+
LocalStorage.setItem(GITHUB_AUTO_SYNC_LASTUPDATE, lastSuccessOccuredAt)
177+
}
178+
}
179+
useUiStore().stopProgress()
180+
181+
// const result = await this.githubPutContentRequest('logs/' + today, useData)
182+
// console.log('result', result)
183+
return Promise.resolve(new ExecutionResult('', 'done'))
184+
} catch (error) {
185+
return Promise.reject(error)
186+
}
187+
}
188+
}
189+
190+
GithubReadEventsCommand.prototype.toString = function cmdToString() {
191+
return `GithubReadEventsCommand: {timestamp: ${this.lastUpdate}`
192+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { LocalStorage } from 'quasar'
2+
import { GITHUB_AUTO_SYNC } from 'src/boot/constants'
3+
import { ExecutionResult } from 'src/core/domain/ExecutionResult'
4+
import { NotesPage } from 'src/notes/models/NotesPage'
5+
import { GithubCommands } from 'src/tabsets/commands/github/GithubCommands'
6+
7+
export type TabsetEventClass = 'tab' | 'tabset' | 'space' | 'note'
8+
export type TabsetEventType = 'added' | 'deleted'
9+
10+
export class TabsetEvent {
11+
timestamp: number
12+
clazz: TabsetEventClass
13+
14+
constructor(
15+
public event: TabsetEventType,
16+
public tabsetId: string,
17+
public parentId: string | undefined,
18+
public name: string,
19+
public spaces: string[],
20+
) {
21+
this.timestamp = new Date().getTime()
22+
this.name = this.name.trim()
23+
this.clazz = 'tabset'
24+
}
25+
format() {
26+
let line = `${this.timestamp}|`
27+
line += `${this.clazz}|${this.event}|`
28+
line += `${this.tabsetId}|${this.parentId ? this.parentId : ''}|`
29+
line += this.name.replaceAll('|', '').replace(/\s/g, ' ').trim() + '|'
30+
line += this.spaces.join(',')
31+
line += '\n'
32+
//return `${this.timestamp}|${this.clazz}|${this.event}|${this.tabsetId}|${this.activeFolderId ? this.activeFolderId : ''}|${this.name.replaceAll('|', '').replace(/\s/g, ' ').trim()}\n`
33+
return line
34+
}
35+
}
36+
37+
export class TabEvent {
38+
timestamp: number
39+
clazz: TabsetEventClass
40+
41+
constructor(
42+
public event: TabsetEventType,
43+
public tabsetId: string,
44+
public tabId: string | undefined,
45+
public name: string,
46+
public url: string | undefined,
47+
) {
48+
this.timestamp = new Date().getTime()
49+
this.name = this.name.trim()
50+
this.clazz = 'tab'
51+
}
52+
format() {
53+
return `${this.timestamp}|${this.clazz}|${this.event}|${this.tabsetId}|${this.tabId}|${this.name.replaceAll('|', '').replace(/\s/g, ' ').trim()}|${this.url}\n`
54+
}
55+
}
56+
57+
export class SpaceEvent {
58+
timestamp: number
59+
clazz: TabsetEventClass
60+
61+
constructor(
62+
public event: TabsetEventType,
63+
public spaceId: string,
64+
public tabsetId: string | undefined,
65+
public name: string,
66+
) {
67+
this.timestamp = new Date().getTime()
68+
this.name = this.name.trim()
69+
this.clazz = 'space'
70+
}
71+
format() {
72+
return `${this.timestamp}|${this.clazz}|${this.event}|${this.spaceId}|${this.tabsetId ? this.tabsetId : ''}|${this.name.replaceAll('|', '').replace(/\s/g, ' ').trim()}\n`
73+
}
74+
}
75+
76+
export class NoteEvent {
77+
timestamp: number
78+
clazz: TabsetEventClass
79+
80+
constructor(
81+
public event: TabsetEventType,
82+
public tabsetId: string,
83+
public name: string,
84+
public content: NotesPage,
85+
) {
86+
this.timestamp = new Date().getTime()
87+
this.name = this.name.trim()
88+
this.clazz = 'note'
89+
}
90+
format() {
91+
const encoded = btoa(JSON.stringify(this.content))
92+
console.log('content', this.content, encoded)
93+
return `${this.timestamp}|${this.clazz}|${this.event}|${this.tabsetId}|${this.name.replaceAll('|', '').replace(/\s/g, ' ').trim()}|${encoded}\n`
94+
}
95+
}
96+
97+
export class GithubWriteEventCommand extends GithubCommands<string> {
98+
constructor(public event: TabsetEvent | TabEvent) {
99+
super()
100+
}
101+
102+
async execute(): Promise<ExecutionResult<string>> {
103+
const githubPath = 'events'
104+
const useData = this.event.format()
105+
console.log('useData', useData, this.event)
106+
if (!LocalStorage.getItem(GITHUB_AUTO_SYNC)) {
107+
return Promise.resolve(new ExecutionResult('done', 'not active'))
108+
}
109+
try {
110+
const events = await this.githubGetContentRequest(githubPath)
111+
console.log('events', events.status)
112+
if (events.status == 404) {
113+
await this.githubPutContentRequest(githubPath, useData)
114+
} else if (events.ok) {
115+
const existing = await events.json()
116+
console.log('got existing', existing)
117+
const decodedContent = decodeURIComponent(escape(window.atob(existing.content)))
118+
const sha = existing['sha' as keyof object]
119+
await this.githubPutContentRequest(githubPath, decodedContent + useData, sha)
120+
}
121+
122+
// const result = await this.githubPutContentRequest('logs/' + today, useData)
123+
// console.log('result', result)
124+
return Promise.resolve(new ExecutionResult('', 'done'))
125+
} catch (error) {
126+
return Promise.reject(error)
127+
}
128+
}
129+
}
130+
131+
GithubWriteEventCommand.prototype.toString = function cmdToString() {
132+
return `GithubWriteEventCommand: {event: ${this.event.format()}`
133+
}

0 commit comments

Comments
 (0)