+12
-12
src/Components/Managers/ConfirmationBoxManager.tsx
+12
-12
src/Components/Managers/ConfirmationBoxManager.tsx
···
9
9
this._setConfirmationBoxText = setConfirmationBoxText;
10
10
11
11
let confirmationBox: HTMLElement;
12
-
12
+
13
+
document.body.appendChild(<div class="confirmation-box" ref={( el ) => confirmationBox = el}>
14
+
<div class="confirmation-box-container">
15
+
{ confirmationBoxText() }<br /><br />
16
+
17
+
<div class="button-danger" onClick={() => { this._confirmationBoxCallback(); setConfirmationBoxText('') }}>Confirm</div>
18
+
<div class="button" onClick={() => setConfirmationBoxText('') }>Deny</div>
19
+
</div>
20
+
</div> as HTMLElement);
21
+
13
22
createEffect(() => {
14
23
if(confirmationBoxText() !== ''){
15
24
confirmationBox.style.display = 'block';
16
-
25
+
17
26
setTimeout(() => {
18
27
confirmationBox.style.opacity = '1';
19
28
}, 1);
20
29
} else{
21
30
confirmationBox.style.opacity = '0';
22
-
31
+
23
32
setTimeout(() => {
24
33
confirmationBox.style.display = 'none';
25
34
}, 250);
26
35
}
27
36
})
28
-
29
-
document.body.appendChild(<div class="confirmation-box" ref={( el ) => confirmationBox = el}>
30
-
<div class="confirmation-box-container">
31
-
{ confirmationBoxText() }<br /><br />
32
-
33
-
<div class="button-danger" onClick={() => { this._confirmationBoxCallback(); setConfirmationBoxText('') }}>Confirm</div>
34
-
<div class="button" onClick={() => setConfirmationBoxText('') }>Deny</div>
35
-
</div>
36
-
</div> as HTMLElement);
37
37
}
38
38
39
39
public SetConfirmationBox( text: string, cb: () => void ){
+2
-3
src/Components/Managers/PhotoViewerManager.tsx
+2
-3
src/Components/Managers/PhotoViewerManager.tsx
···
2
2
import { Photo } from "../Structs/Photo";
3
3
4
4
export class PhotoViewerManager{
5
-
private _currentPhoto: Accessor<Photo | null>;
6
-
5
+
public CurrentPhoto: Accessor<Photo | null>;
7
6
private _setCurrentPhoto: Setter<Photo | null>;
8
7
9
8
constructor(){
10
-
[ this._currentPhoto, this._setCurrentPhoto ] = createSignal<Photo | null>(null);
9
+
[ this.CurrentPhoto, this._setCurrentPhoto ] = createSignal<Photo | null>(null);
11
10
}
12
11
13
12
public Close(){
+61
src/Components/Managers/WorldCacheManager.tsx
+61
src/Components/Managers/WorldCacheManager.tsx
···
1
+
import { invoke } from "@tauri-apps/api/core";
2
+
import { WorldCache } from "../Structs/WorldCache";
3
+
import { listen } from "@tauri-apps/api/event";
4
+
5
+
export class WorldCacheManager{
6
+
private _worldCache: WorldCache[] = [];
7
+
private _resolveWorld: ( world: WorldCache | null ) => void = () => {};
8
+
9
+
constructor(){
10
+
invoke('get_config_value_string', { key: 'worldcache' })
11
+
.then((data: any) => {
12
+
if(data)this._worldCache = JSON.parse(data);
13
+
})
14
+
15
+
listen('world_data', ( event: any ) => {
16
+
let worldData = {
17
+
expiresOn: Date.now() + 1.2096E+09,
18
+
worldData: {
19
+
id: event.payload.id,
20
+
name: event.payload.name.split('\\').join('').slice(1, -1),
21
+
author: event.payload.author.split('\\').join('').slice(1, -1),
22
+
authorId: event.payload.authorId.split('\\').join('').slice(1, -1),
23
+
desc: event.payload.desc.split('\\').join('').slice(1, -1),
24
+
img: event.payload.img.split('\\').join('').slice(1, -1),
25
+
maxUsers: event.payload.maxUsers,
26
+
visits: event.payload.visits,
27
+
favourites: event.payload.favourites,
28
+
tags: event.payload.tags,
29
+
from: event.payload.from,
30
+
fromSite: event.payload.fromSite,
31
+
found: event.payload.found
32
+
}
33
+
}
34
+
35
+
this._worldCache.push(worldData);
36
+
invoke('set_config_value_string', { key: 'worldcache', value: JSON.stringify(this._worldCache) });
37
+
38
+
this._resolveWorld(worldData);
39
+
})
40
+
}
41
+
42
+
getWorldById( id: string ): Promise<WorldCache | null>{
43
+
let promise = new Promise<WorldCache | null>(( res ) => { this._resolveWorld = res });
44
+
let worldData = this._worldCache.find(x => x.worldData.id === id);
45
+
46
+
if(!worldData){
47
+
console.log('Fetching new world data');
48
+
49
+
invoke('find_world_by_id', { worldId: id });
50
+
} else if(worldData.expiresOn < Date.now()){
51
+
console.log('Fetching new world data since cache has expired');
52
+
53
+
this._worldCache = this._worldCache.filter(x => x.worldData.id !== id);
54
+
invoke('find_world_by_id', { worldId: id });
55
+
} else{
56
+
this._resolveWorld(worldData);
57
+
}
58
+
59
+
return promise;
60
+
}
61
+
}
+3
-5
src/Components/PhotoList.tsx
+3
-5
src/Components/PhotoList.tsx
···
9
9
let months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ];
10
10
11
11
class PhotoListProps{
12
-
setCurrentPhotoView!: ( view: any ) => any;
13
-
currentPhotoView!: () => any;
14
12
photoNavChoice!: () => string;
15
13
setPhotoNavChoice!: ( view: any ) => any;
16
14
isPhotosSyncing!: () => boolean;
···
79
77
switch(action){
80
78
case 'prev':
81
79
if(!window.PhotoLoadingManager.FilteredPhotos[currentPhotoIndex - 1])break;
82
-
props.setCurrentPhotoView(window.PhotoLoadingManager.FilteredPhotos[currentPhotoIndex - 1]);
80
+
window.PhotoViewerManager.OpenPhoto(window.PhotoLoadingManager.FilteredPhotos[currentPhotoIndex - 1]);
83
81
84
82
currentPhotoIndex--;
85
83
break;
86
84
case 'next':
87
85
if(!window.PhotoLoadingManager.FilteredPhotos[currentPhotoIndex + 1])break;
88
-
props.setCurrentPhotoView(window.PhotoLoadingManager.FilteredPhotos[currentPhotoIndex + 1]);
86
+
window.PhotoViewerManager.OpenPhoto(window.PhotoLoadingManager.FilteredPhotos[currentPhotoIndex + 1]);
89
87
90
88
currentPhotoIndex++;
91
89
break;
···
335
333
);
336
334
337
335
if(photo){
338
-
props.setCurrentPhotoView(photo);
336
+
window.PhotoViewerManager.OpenPhoto(photo);
339
337
currentPhotoIndex = window.PhotoLoadingManager.FilteredPhotos.indexOf(photo);
340
338
} else
341
339
currentPhotoIndex = -1;
+51
-152
src/Components/PhotoViewer.tsx
+51
-152
src/Components/PhotoViewer.tsx
···
1
1
import { For, Show, createEffect, onCleanup, onMount } from "solid-js";
2
2
import { invoke } from '@tauri-apps/api/core';
3
-
import { listen } from '@tauri-apps/api/event';
4
3
import anime from 'animejs';
4
+
import { WorldCache } from "./Structs/WorldCache";
5
5
6
6
class PhotoViewerProps{
7
-
currentPhotoView!: () => any;
8
-
setCurrentPhotoView!: ( view: any ) => any;
9
7
setPhotoNavChoice!: ( view: any ) => any;
10
8
}
11
9
12
-
class WorldCache{
13
-
expiresOn!: number;
14
-
worldData!: {
15
-
id: string,
16
-
name: string,
17
-
author: string,
18
-
authorId: string,
19
-
desc: string,
20
-
img: string,
21
-
maxUsers: number,
22
-
visits: number,
23
-
favourites: number,
24
-
tags: any,
25
-
from: string,
26
-
fromSite: string,
27
-
found: boolean
28
-
}
29
-
}
30
-
31
-
let worldCache: WorldCache[] = [];
32
-
33
-
invoke('get_config_value_string', { key: 'worldcache' })
34
-
.then((data: any) => {
35
-
if(data)worldCache = JSON.parse(data);
36
-
})
37
-
38
10
let PhotoViewer = ( props: PhotoViewerProps ) => {
39
11
let viewer: HTMLElement;
40
12
let imageViewer: HTMLImageElement;
···
48
20
let photoTrayCloseBtn: HTMLElement;
49
21
50
22
let worldInfoContainer: HTMLElement;
51
-
let photoPath: string;
52
23
53
24
let viewerContextMenu: HTMLElement;
54
25
let viewerContextMenuButtons: HTMLElement[] = [];
···
59
30
let switchPhotoWithKey = ( e: KeyboardEvent ) => {
60
31
switch(e.key){
61
32
case 'Escape':
62
-
props.setCurrentPhotoView(null);
33
+
window.PhotoViewerManager.Close();
63
34
64
35
break;
65
36
case 'ArrowUp':
···
114
85
})
115
86
}
116
87
88
+
let copyImage = () => {
89
+
let canvas = document.createElement('canvas');
90
+
let ctx = canvas.getContext('2d')!;
91
+
92
+
canvas.width = window.PhotoViewerManager.CurrentPhoto()?.width || 0;
93
+
canvas.height = window.PhotoViewerManager.CurrentPhoto()?.height || 0;
94
+
95
+
ctx.drawImage(imageViewer, 0, 0);
96
+
97
+
canvas.toBlob(( blob ) => {
98
+
navigator.clipboard.write([
99
+
new ClipboardItem({
100
+
'image/png': blob!
101
+
})
102
+
]);
103
+
104
+
canvas.remove();
105
+
106
+
anime.set('.copy-notif', { translateX: '-50%', translateY: '-100px' });
107
+
anime({
108
+
targets: '.copy-notif',
109
+
opacity: 1,
110
+
translateY: '0px'
111
+
});
112
+
113
+
setTimeout(() => {
114
+
anime({
115
+
targets: '.copy-notif',
116
+
opacity: 0,
117
+
translateY: '-100px'
118
+
});
119
+
}, 2000);
120
+
});
121
+
}
122
+
117
123
let closeTray = () => {
118
124
if(!trayOpen || trayInAnimation)return;
119
125
trayInAnimation = true;
···
171
177
window.CloseAllPopups.forEach(p => p());
172
178
// Context Menu -> Open file location
173
179
174
-
let path = await invoke('get_user_photos_path') + '\\' + props.currentPhotoView().path;
180
+
let path = await invoke('get_user_photos_path') + '\\' + window.PhotoViewerManager.CurrentPhoto()?.path;
175
181
invoke('open_folder', { url: path });
176
182
}
177
183
178
184
viewerContextMenuButtons[1].onclick = () => {
179
185
window.CloseAllPopups.forEach(p => p());
180
186
// Context Menu -> Copy image
181
-
182
-
let canvas = document.createElement('canvas');
183
-
let ctx = canvas.getContext('2d')!;
184
-
185
-
canvas.width = props.currentPhotoView().width;
186
-
canvas.height = props.currentPhotoView().height;
187
-
188
-
ctx.drawImage(imageViewer, 0, 0);
189
-
190
-
canvas.toBlob(( blob ) => {
191
-
navigator.clipboard.write([
192
-
new ClipboardItem({
193
-
'image/png': blob!
194
-
})
195
-
]);
196
-
197
-
canvas.remove();
198
-
199
-
anime.set('.copy-notif', { translateX: '-50%', translateY: '-100px' });
200
-
anime({
201
-
targets: '.copy-notif',
202
-
opacity: 1,
203
-
translateY: '0px'
204
-
});
205
-
206
-
setTimeout(() => {
207
-
anime({
208
-
targets: '.copy-notif',
209
-
opacity: 0,
210
-
translateY: '-100px'
211
-
});
212
-
}, 2000);
213
-
});
187
+
copyImage();
214
188
}
215
189
216
190
imageViewer.oncontextmenu = ( e ) => {
···
249
223
}
250
224
251
225
createEffect(() => {
252
-
let photo = props.currentPhotoView();
226
+
let photo = window.PhotoViewerManager.CurrentPhoto();
253
227
allowedToOpenTray = false;
254
228
255
229
imageViewer.style.opacity = '0';
256
230
257
231
if(photo){
258
-
(async () => {
259
-
if(!photoPath)
260
-
photoPath = await invoke('get_user_photos_path') + '/';
261
-
262
-
imageViewer.src = (window.OS === "windows" ? "http://photo.localhost/" : 'photo://localhost') + (photoPath + props.currentPhotoView().path).split('\\').join('/') + "?full";
263
-
imageViewer.crossOrigin = 'anonymous';
264
-
})();
232
+
imageViewer.src = (window.OS === "windows" ? "http://photo.localhost/" : 'photo://localhost') + window.PhotoViewerManager.CurrentPhoto()?.path.split('\\').join('/') + "?full";
233
+
imageViewer.crossOrigin = 'anonymous';
265
234
266
235
anime({
267
236
targets: imageViewer,
···
276
245
photo.onMetaLoaded = () => {}
277
246
278
247
let meta = JSON.parse(photo.metadata);
279
-
let worldData = worldCache.find(x => x.worldData.id === meta.world.id);
280
248
281
249
allowedToOpenTray = true;
282
250
trayButton.style.display = 'flex';
···
306
274
</div> as Node
307
275
);
308
276
309
-
if(!worldData){
310
-
console.log('Fetching new world data');
311
-
312
-
invoke('find_world_by_id', { worldId: meta.world.id });
313
-
} else if(worldData.expiresOn < Date.now()){
314
-
console.log('Fetching new world data since cache has expired');
315
-
316
-
worldCache = worldCache.filter(x => x.worldData.id !== meta.world.id)
317
-
invoke('find_world_by_id', { worldId: meta.world.id });
318
-
} else
319
-
loadWorldData(worldData);
277
+
window.WorldCacheManager.getWorldById(meta.world.id)
278
+
.then(worldData => {
279
+
if(worldData)
280
+
loadWorldData(worldData);
281
+
});
320
282
} else{
321
283
trayButton.style.display = 'none';
322
284
closeTray();
323
285
}
324
286
}
325
287
326
-
photo.onMetaLoaded = () => handleMetaDataLoaded();
327
288
handleMetaDataLoaded();
328
-
329
-
photo.loadImage();
330
289
}
331
290
332
291
if(photo && !isOpen){
···
382
341
})
383
342
384
343
let loadWorldData = ( data: WorldCache ) => {
385
-
let meta = props.currentPhotoView().metadata;
344
+
let meta = window.PhotoViewerManager.CurrentPhoto()?.metadata;
386
345
if(!meta)return;
387
346
388
347
worldInfoContainer.innerHTML = '';
···
411
370
)
412
371
}
413
372
414
-
listen('world_data', ( event: any ) => {
415
-
let worldData = {
416
-
expiresOn: Date.now() + 1.2096E+09,
417
-
worldData: {
418
-
id: event.payload.id,
419
-
name: event.payload.name.split('\\').join('').slice(1, -1),
420
-
author: event.payload.author.split('\\').join('').slice(1, -1),
421
-
authorId: event.payload.authorId.split('\\').join('').slice(1, -1),
422
-
desc: event.payload.desc.split('\\').join('').slice(1, -1),
423
-
img: event.payload.img.split('\\').join('').slice(1, -1),
424
-
maxUsers: event.payload.maxUsers,
425
-
visits: event.payload.visits,
426
-
favourites: event.payload.favourites,
427
-
tags: event.payload.tags,
428
-
from: event.payload.from,
429
-
fromSite: event.payload.fromSite,
430
-
found: event.payload.found
431
-
}
432
-
}
433
-
434
-
worldCache.push(worldData);
435
-
invoke('set_config_value_string', { key: 'worldcache', value: JSON.stringify(worldCache) });
436
-
437
-
loadWorldData(worldData);
438
-
})
439
-
440
373
return (
441
374
<div class="photo-viewer" ref={( el ) => viewer = el}>
442
375
<div class="photo-context-menu" ref={( el ) => viewerContextMenu = el}>
···
444
377
<div ref={( el ) => viewerContextMenuButtons.push(el)}>Copy image</div>
445
378
</div>
446
379
447
-
<div class="viewer-close viewer-button" onClick={() => props.setCurrentPhotoView(null)}>
380
+
<div class="viewer-close viewer-button" onClick={() => window.PhotoViewerManager.Close()}>
448
381
<div class="icon" style={{ width: '10px', margin: '0' }}>
449
382
<img draggable="false" src="/icon/x-solid.svg"></img>
450
383
</div>
···
484
417
<div class="viewer-button"
485
418
onMouseOver={( el ) => anime({ targets: el.currentTarget, width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })}
486
419
onMouseLeave={( el ) => anime({ targets: el.currentTarget, width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })}
487
-
onClick={() => {
488
-
let canvas = document.createElement('canvas');
489
-
let ctx = canvas.getContext('2d')!;
490
-
491
-
canvas.width = props.currentPhotoView().width;
492
-
canvas.height = props.currentPhotoView().height;
493
-
494
-
ctx.drawImage(imageViewer, 0, 0);
495
-
496
-
canvas.toBlob(( blob ) => {
497
-
navigator.clipboard.write([
498
-
new ClipboardItem({
499
-
'image/png': blob!
500
-
})
501
-
]);
502
-
503
-
canvas.remove();
504
-
505
-
anime.set('.copy-notif', { translateX: '-50%', translateY: '-100px' });
506
-
anime({
507
-
targets: '.copy-notif',
508
-
opacity: 1,
509
-
translateY: '0px'
510
-
});
511
-
512
-
setTimeout(() => {
513
-
anime({
514
-
targets: '.copy-notif',
515
-
opacity: 0,
516
-
translateY: '-100px'
517
-
});
518
-
}, 2000);
519
-
});
520
-
}}
521
-
>
420
+
onClick={() => { copyImage(); }}>
522
421
<div class="icon" style={{ width: '12px', margin: '0' }}>
523
422
<img draggable="false" src="/icon/copy-solid.svg"></img>
524
423
</div>
···
537
436
onMouseOver={( el ) => anime({ targets: el.currentTarget, width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })}
538
437
onMouseLeave={( el ) => anime({ targets: el.currentTarget, width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })}
539
438
onClick={() => window.ConfirmationBoxManager.SetConfirmationBox("Are you sure you want to delete this photo?", async () => { invoke("delete_photo", {
540
-
path: props.currentPhotoView().path,
439
+
path: window.PhotoViewerManager.CurrentPhoto()?.path,
541
440
token: (await invoke('get_config_value_string', { key: 'token' })) || "none",
542
441
isSyncing: window.AccountManager.hasAccount() ? window.AccountManager.Storage()?.isSyncing : false
543
442
});
+2
-4
src/Components/SettingsMenu.tsx
+2
-4
src/Components/SettingsMenu.tsx
···
1
-
import { createSignal, onCleanup, onMount, Show } from "solid-js";
1
+
import { onCleanup, onMount, Show } from "solid-js";
2
2
import { bytesToFormatted } from "../utils";
3
3
import { invoke } from '@tauri-apps/api/core';
4
4
import anime from "animejs";
···
12
12
let finalPathInput: HTMLElement;
13
13
let finalPathData: string;
14
14
let finalPathPreviousData: string;
15
-
16
-
let [ deletingPhotos, setDeletingPhotos ] = createSignal(false);
17
15
18
16
let closeWithKey = ( e: KeyboardEvent ) => {
19
17
if(e.key === 'Escape'){
···
352
350
<div class="account-notice">To enable cloud storage or get more storage please contact "_phaz" on discord</div>
353
351
354
352
<div class="account-notice" style={{ display: 'flex' }}>
355
-
<Show when={!deletingPhotos()} fallback={ "We are deleting your photos, please leave this window open while we delete them." }>
353
+
<Show when={false} fallback={ "We are deleting your photos, please leave this window open while we delete them." }>
356
354
<div class="button-danger" onClick={() => window.ConfirmationBoxManager.SetConfirmationBox("You are about to delete all your photos from the cloud, and disable syncing. This will NOT delete any local files.", async () => {
357
355
// TODO: Rework all of this
358
356
+18
src/Components/Structs/WorldCache.ts
+18
src/Components/Structs/WorldCache.ts
···
1
+
export class WorldCache{
2
+
expiresOn!: number;
3
+
worldData!: {
4
+
id: string,
5
+
name: string,
6
+
author: string,
7
+
authorId: string,
8
+
desc: string,
9
+
img: string,
10
+
maxUsers: number,
11
+
visits: number,
12
+
favourites: number,
13
+
tags: any,
14
+
from: string,
15
+
fromSite: string,
16
+
found: boolean
17
+
}
18
+
}
+4
-1
src/index.tsx
+4
-1
src/index.tsx
···
7
7
LoadingManager: LoadingManager;
8
8
PhotoLoadingManager: PhotoLoadingManager;
9
9
ConfirmationBoxManager: ConfirmationBoxManager;
10
-
PhotoViewerManager: PhotoViewerManager;
10
+
PhotoViewerManager: PhotoViewerManager;
11
+
WorldCacheManager: WorldCacheManager;
11
12
12
13
CloseAllPopups: (() => void)[];
13
14
OS: string;
···
27
28
import { PhotoLoadingManager } from "./Components/Managers/PhotoLoadingManager";
28
29
import { ConfirmationBoxManager } from "./Components/Managers/ConfirmationBoxManager";
29
30
import { PhotoViewerManager } from "./Components/Managers/PhotoViewerManager";
31
+
import { WorldCacheManager } from "./Components/Managers/WorldCacheManager";
30
32
31
33
window.AccountManager = new AccountManager();
32
34
window.LoadingManager = new LoadingManager();
33
35
window.PhotoLoadingManager = new PhotoLoadingManager();
34
36
window.ConfirmationBoxManager = new ConfirmationBoxManager();
35
37
window.PhotoViewerManager = new PhotoViewerManager();
38
+
window.WorldCacheManager = new WorldCacheManager();
36
39
37
40
(async () => {
38
41
window.OS = await invoke('get_os');