Skip to content

Commit 3637dd2

Browse files
feat: image preview, image save (#27)
1 parent 563a5cb commit 3637dd2

34 files changed

+306
-201
lines changed
Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
11
export type IMessages = {
2-
downloadLinkWritten: {
3-
ok: string
4-
},
5-
accountAndDataDeleted: {
6-
ok: string
7-
fail: string
8-
},
9-
dataDeleted: {
10-
ok: string
11-
fail: string
12-
},
13-
generic: {
14-
ok: string,
15-
fail: string
16-
}
2+
downloadLinkWritten: {
3+
ok: string
4+
}
5+
accountAndDataDeleted: {
6+
ok: string
7+
fail: string
8+
}
9+
dataDeleted: {
10+
ok: string
11+
fail: string
12+
}
13+
generic: {
14+
ok: string
15+
fail: string
16+
}
1717
}
1818

1919
const en: IMessages = {
20-
downloadLinkWritten: {
21-
ok: `The link was written to the clipboard.\nPaste it in a browser and the database will download.\nRemember to keep it a secret!`
22-
},
23-
accountAndDataDeleted: {
24-
ok: "Account and your data were successfully deleted",
25-
fail: "Failed to delete your account and/or data"
26-
},
27-
dataDeleted: {
28-
ok: "Failed to delete your data",
29-
fail: "Data successfully deleted"
30-
},
31-
generic: {
32-
ok: "Operation successfully completed",
33-
fail: "Operation failed"
34-
}
20+
downloadLinkWritten: {
21+
ok: `The link was written to the clipboard.\nPaste it in a browser and the database will download.\nRemember to keep it a secret!`
22+
},
23+
accountAndDataDeleted: {
24+
ok: 'Account and your data were successfully deleted',
25+
fail: 'Failed to delete your account and/or data'
26+
},
27+
dataDeleted: {
28+
ok: 'Failed to delete your data',
29+
fail: 'Data successfully deleted'
30+
},
31+
generic: {
32+
ok: 'Operation successfully completed',
33+
fail: 'Operation failed'
34+
}
3535
}
3636

37-
export const messages = (lang: "en" | "tbd" = "en"): IMessages => {
38-
return en
39-
}
37+
export const messages = (lang: 'en' | 'tbd' = 'en'): IMessages => {
38+
return en
39+
}

src/electron/App/EventHandler.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ async function registerUser(rawUser: { name: string; password: string }) {
327327
}
328328

329329
/**
330-
* Events send by the front-end, not Iohook specific
330+
* from frontend, not Iohook specific
331331
*/
332332
const channelsFromRender: IReceiveChannel[] = [
333333
{
@@ -484,6 +484,33 @@ const channelsFromRender: IReceiveChannel[] = [
484484
handler: async (event: any) => {
485485
actionsExported.askPassword()
486486
}
487+
},
488+
{
489+
name: 'to.backend.openImage',
490+
handler: async (event: any, hash: string, width: number, height: number) => {
491+
const item = await ItemRepo.get(hash)
492+
if (!item || item.contentType !== 3) {
493+
actionsExported.alertFrontend('cannot open a non image')
494+
}
495+
const win = new BrowserWindow({
496+
height,
497+
width,
498+
show: false,
499+
autoHideMenuBar: true
500+
})
501+
502+
const content = `data:text/html;charset=utf-8,<html><head><title>image taken on ${item?.modified.toLocaleString()}</title></head><body> <img style="height: 100%;" src="${
503+
item?.content
504+
}"></body></html>`
505+
win.loadURL(content)
506+
win.setAlwaysOnTop(true, 'floating')
507+
win.setVisibleOnAllWorkspaces(true)
508+
win.once('ready-to-show', () => {
509+
win.show()
510+
})
511+
win.focus()
512+
win
513+
}
487514
}
488515
]
489516

src/electron/Data/ItemRepository.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ const update = (item: IClipboardItem): boolean => {
3838
return false
3939
}
4040

41-
4241
const remove = async (hash: string) => {
4342
items.delete(hash)
4443
await RequestService.clips.delete(hash)
@@ -96,7 +95,7 @@ async function loadItemsBeforeDate(date: DateTime, limit: number, masterKey: str
9695
}
9796
let needsLoading = false
9897
if (!state.user) {
99-
throw new Error("user not found")
98+
throw new Error('user not found')
10099
}
101100
// console.log("received items: " + res.data.clips.length)
102101
for (const clip of res.data.clips) {
@@ -136,18 +135,18 @@ export const ItemRepo = {
136135
loadItemsBeforeHash: async (hash: string, password: string, limit = 50) => {
137136
const item = items.get(hash)
138137
if (!item) {
139-
throw new Error("tried to read before unexisting hash!")
138+
throw new Error('tried to read before unexisting hash!')
140139
}
141140
return loadItemsBeforeDate(DateTime.fromJSDate(item.modified), limit, password)
142141
},
143142
cleanUp: async (maxAgeInSeconds: number, maxNumberTotal: number): Promise<boolean> => {
144143
let changed = false
145-
changed = changed || await removeOldUnfavored(maxAgeInSeconds)
146-
changed = changed || await limitMapSize(maxNumberTotal)
144+
changed = changed || (await removeOldUnfavored(maxAgeInSeconds))
145+
changed = changed || (await limitMapSize(maxNumberTotal))
147146
return changed
148147
},
149148
syncWithRemote: async (password: string) => {
150-
console.log("syncing")
149+
console.log('syncing')
151150
const res = await RequestService.clips.getSince(oldestPull)
152151
if (!res.ok || !res.data) {
153152
actionsExported.logFrontend(messages().generic.fail + `.\nFetch of items since ${oldestPull.toISO()}.`)
@@ -156,9 +155,9 @@ export const ItemRepo = {
156155
oldestPull = DateTime.now()
157156
let needsLoading = false
158157
if (!state.user) {
159-
throw new Error("user not found")
158+
throw new Error('user not found')
160159
}
161-
console.log("received items after sync: " + res.data.clips.length)
160+
console.log('received items after sync: ' + res.data.clips.length)
162161
for (const clip of res.data.clips) {
163162
needsLoading = true
164163
try {
@@ -185,8 +184,7 @@ export const ItemRepo = {
185184
item[1].remoteStatus = RemoteItemStatus.pushedToRemote
186185
} else if (res.status == 409) {
187186
item[1].remoteStatus = RemoteItemStatus.needsUpdateOnRemote
188-
}
189-
else {
187+
} else {
190188
console.log(await res.text())
191189
}
192190
needsLoading = true
@@ -204,7 +202,7 @@ export const ItemRepo = {
204202
}
205203
console.log('done syncing')
206204
if (needsLoading) {
207-
console.log("needs loading after remote sync")
205+
console.log('needs loading after remote sync')
208206
actionsExported.sendCurrentItems()
209207
}
210208
}

src/electron/Data/RequestUtils.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,19 @@ export const ClipsEndpoints = {
55
CreateUpdate: `${userPreferences.storeUrl.value}/v1/clips`,
66
Delete: (hash: string) => `${userPreferences.storeUrl.value}/v1/clips/${hash}`,
77
GetOne: (hash: string) => `${userPreferences.storeUrl.value}/v1/clips/${hash}`,
8-
GetAllSince: (time: DateTime) =>
9-
`${userPreferences.storeUrl.value}/v1/clips/since/${time.toUTC().toISO()}`,
8+
GetAllSince: (time: DateTime) => `${userPreferences.storeUrl.value}/v1/clips/since/${time.toUTC().toISO()}`,
109
GetNBefore: (time: DateTime, limit: number) =>
1110
`${userPreferences.storeUrl.value}/v1/clips/before/${time.toUTC().toISO()}/limit/${limit}`,
1211
GetInBetween: (start: DateTime, end: DateTime) =>
1312
`${userPreferences.storeUrl.value}/v1/clips/between/${start.toUTC().toISO()}/${end.toUTC().toISO()}`,
14-
DeletAllData: () =>
15-
`${userPreferences.storeUrl.value}/v1/deleteMyData`,
16-
GetDatabaseFile: () =>
17-
`${userPreferences.storeUrl.value}/v1/database`
13+
DeletAllData: () => `${userPreferences.storeUrl.value}/v1/deleteMyData`,
14+
GetDatabaseFile: () => `${userPreferences.storeUrl.value}/v1/database`
1815
}
1916

2017
export const UserEndpoints = {
2118
Register: `${userPreferences.authUrl.value}/v1/register`,
2219
Login: `${userPreferences.authUrl.value}/v1/login`,
2320
RefreshToken: `${userPreferences.authUrl.value}/v1/refresh`,
2421
DeleteProfile: `${userPreferences.authUrl.value}/v1/deleteAccount`,
25-
Confirm2FA: `${userPreferences.authUrl.value}/v1/confirm2fa`,
22+
Confirm2FA: `${userPreferences.authUrl.value}/v1/confirm2fa`
2623
}

src/electron/Data/Requests.ts

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { DateTime } from "luxon"
1+
import { DateTime } from 'luxon'
22
import fetch from 'node-fetch'
3-
import { actionsExported } from "../App/EventHandler"
4-
import { IClipboardItemEncrypted } from "../DataModels/DataTypes"
5-
import { BulkRes, CommonRes, LoginRes, RefreshRes, RegisterRes } from "../DataModels/RequestTypes"
6-
import { ClipsEndpoints, UserEndpoints } from "./RequestUtils"
3+
import { actionsExported } from '../App/EventHandler'
4+
import { IClipboardItemEncrypted } from '../DataModels/DataTypes'
5+
import { BulkRes, CommonRes, LoginRes, RefreshRes, RegisterRes } from '../DataModels/RequestTypes'
6+
import { ClipsEndpoints, UserEndpoints } from './RequestUtils'
77

88
export const userTokens: Tokens = {
99
access_expires: DateTime.now()
@@ -15,12 +15,12 @@ type Tokens = {
1515
access_expires: DateTime<true>
1616
}
1717

18-
function request(url: string, method: string = "GET", body?: object, headers?: Record<string, string>) {
18+
function request(url: string, method: string = 'GET', body?: object, headers?: Record<string, string>) {
1919
if (!headers) {
2020
headers = {}
2121
}
2222
if (body) {
23-
headers["content-type"] = "application/json"
23+
headers['content-type'] = 'application/json'
2424
}
2525

2626
const opts = {
@@ -33,20 +33,25 @@ function request(url: string, method: string = "GET", body?: object, headers?: R
3333
}
3434

3535
type ResWrapped<T> = {
36-
ok: boolean,
37-
data?: T,
36+
ok: boolean
37+
data?: T
3838
err?: string
3939
code: number
4040
}
4141

42-
async function requestWithResponseBody<T>(url: string, method: string = "GET", body?: object, headers?: Record<string, string>): Promise<ResWrapped<T>> {
42+
async function requestWithResponseBody<T>(
43+
url: string,
44+
method: string = 'GET',
45+
body?: object,
46+
headers?: Record<string, string>
47+
): Promise<ResWrapped<T>> {
4348
const res = await request(url, method, body, headers)
4449
const result: ResWrapped<T> = {
4550
ok: res.ok,
4651
code: res.status
4752
}
4853
if (res.ok) {
49-
result.data = await res.json() as T
54+
result.data = (await res.json()) as T
5055
} else {
5156
result.err = await res.text()
5257
}
@@ -56,7 +61,7 @@ async function requestWithResponseBody<T>(url: string, method: string = "GET", b
5661

5762
async function getAccessToken(): Promise<string> {
5863
if (DateTime.now().plus({ minute: 1 }) > userTokens.access_expires) {
59-
console.log("access token needs a refresh!")
64+
console.log('access token needs a refresh!')
6065
const refreshRes = await RequestService.account.refresh()
6166
if (refreshRes.ok && refreshRes.data) {
6267
userTokens.access_expires = DateTime.now().plus({ minutes: 55 })
@@ -66,25 +71,30 @@ async function getAccessToken(): Promise<string> {
6671
}
6772
}
6873
if (!userTokens.access) {
69-
actionsExported.logFrontend("Tried to call using an empty token!")
70-
throw new Error("token not set")
74+
actionsExported.logFrontend('Tried to call using an empty token!')
75+
throw new Error('token not set')
7176
}
7277
return userTokens.access
7378
}
7479

75-
async function requestToken(url: string, method: string = "GET", body?: object) {
76-
return await request(url, method, body, { 'authorization': `Bearer ${await getAccessToken()}` })
80+
async function requestToken(url: string, method: string = 'GET', body?: object) {
81+
return await request(url, method, body, { authorization: `Bearer ${await getAccessToken()}` })
7782
}
7883

79-
async function requestTokenBody<T>(url: string, method: string = "GET", body?: object) {
80-
return requestWithResponseBody<T>(url, method, body, { 'authorization': `Bearer ${await getAccessToken()}` })
84+
async function requestTokenBody<T>(url: string, method: string = 'GET', body?: object) {
85+
return requestWithResponseBody<T>(url, method, body, { authorization: `Bearer ${await getAccessToken()}` })
8186
}
8287

8388
export const RequestService = {
8489
account: {
85-
register: (username: string, remotePass: string) => requestWithResponseBody<RegisterRes>(UserEndpoints.Register, "POST", { username, password: remotePass }),
86-
login: (username: string, remotePass: string, code2fa: string) => requestWithResponseBody<LoginRes>(UserEndpoints.Login, "POST", { username, password: remotePass, code: code2fa }),
87-
refresh: () => requestWithResponseBody<RefreshRes>(UserEndpoints.RefreshToken, "POST", undefined, { "authorization": `Bearer ${userTokens.refresh}` }),
90+
register: (username: string, remotePass: string) =>
91+
requestWithResponseBody<RegisterRes>(UserEndpoints.Register, 'POST', { username, password: remotePass }),
92+
login: (username: string, remotePass: string, code2fa: string) =>
93+
requestWithResponseBody<LoginRes>(UserEndpoints.Login, 'POST', { username, password: remotePass, code: code2fa }),
94+
refresh: () =>
95+
requestWithResponseBody<RefreshRes>(UserEndpoints.RefreshToken, 'POST', undefined, {
96+
authorization: `Bearer ${userTokens.refresh}`
97+
}),
8898
logout() {
8999
userTokens.refresh = undefined
90100
userTokens.access = undefined
@@ -95,18 +105,24 @@ export const RequestService = {
95105
const url = ClipsEndpoints.GetDatabaseFile() + `?token=${await getAccessToken()}`
96106
return url
97107
},
98-
confirm2fa: (code: string, token: string) => requestWithResponseBody<CommonRes>(UserEndpoints.Confirm2FA, "POST", {
99-
code
100-
}, { "authorization": `Bearer ${token}` })
108+
confirm2fa: (code: string, token: string) =>
109+
requestWithResponseBody<CommonRes>(
110+
UserEndpoints.Confirm2FA,
111+
'POST',
112+
{
113+
code
114+
},
115+
{ authorization: `Bearer ${token}` }
116+
)
101117
},
102118
clips: {
103-
add: (clip: IClipboardItemEncrypted) => requestToken(ClipsEndpoints.CreateUpdate, "POST", clip),
104-
upsert: (clip: IClipboardItemEncrypted) => requestToken(ClipsEndpoints.CreateUpdate, "PUT", clip),
105-
update: (clip: { isFavorite: boolean, hash: string }) => requestToken(ClipsEndpoints.CreateUpdate, "PATCH", clip),
106-
delete: (hash: string) => requestToken(ClipsEndpoints.Delete(hash), "DELETE"),
107-
getSince: (time: DateTime) => requestTokenBody<BulkRes>(ClipsEndpoints.GetAllSince(time), "GET"),
108-
getBetween: (start: DateTime, end: DateTime) => requestTokenBody<BulkRes>(ClipsEndpoints.GetInBetween(start, end), "GET"),
109-
getNBefore: (time: DateTime, limit: number) => requestTokenBody<BulkRes>(ClipsEndpoints.GetNBefore(time, limit), "GET"),
110-
deleteAll: () => requestTokenBody<CommonRes>(ClipsEndpoints.DeletAllData(), "DELETE"),
119+
add: (clip: IClipboardItemEncrypted) => requestToken(ClipsEndpoints.CreateUpdate, 'POST', clip),
120+
upsert: (clip: IClipboardItemEncrypted) => requestToken(ClipsEndpoints.CreateUpdate, 'PUT', clip),
121+
update: (clip: { isFavorite: boolean; hash: string }) => requestToken(ClipsEndpoints.CreateUpdate, 'PATCH', clip),
122+
delete: (hash: string) => requestToken(ClipsEndpoints.Delete(hash), 'DELETE'),
123+
getSince: (time: DateTime) => requestTokenBody<BulkRes>(ClipsEndpoints.GetAllSince(time), 'GET'),
124+
getBetween: (start: DateTime, end: DateTime) => requestTokenBody<BulkRes>(ClipsEndpoints.GetInBetween(start, end), 'GET'),
125+
getNBefore: (time: DateTime, limit: number) => requestTokenBody<BulkRes>(ClipsEndpoints.GetNBefore(time, limit), 'GET'),
126+
deleteAll: () => requestTokenBody<CommonRes>(ClipsEndpoints.DeletAllData(), 'DELETE')
111127
}
112-
}
128+
}

src/electron/DataModels/DataTypes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import stringify from 'json-stable-stringify'
22

3-
43
// export interface IItemsDb {
54
// Add(item: IClipboardItemEncrypted): Promise<IClipboardItemEncrypted>
65
// GetRange(start: number, end: number): Promise<IClipboardItemEncrypted[]>
@@ -68,7 +67,7 @@ export enum RemoteItemStatus {
6867
}
6968

7069
export interface IClipboardItem extends ItemBase {
71-
content: string;
70+
content: string
7271
created: Date
7372
modified: Date
7473
remoteStatus: RemoteItemStatus

0 commit comments

Comments
 (0)