Skip to content
This repository was archived by the owner on Dec 2, 2024. It is now read-only.

Commit 0b5e1ec

Browse files
committed
feat: add import project
1 parent c2c97c3 commit 0b5e1ec

26 files changed

+6196
-131
lines changed

.prettierrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"semi": true,
3+
"singleQuote": true
4+
}

contributing.md

-27
This file was deleted.

package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@
3434
"fabric": "^4.5.0",
3535
"gitconfiglocal": "^2.1.0",
3636
"muuri": "^0.9.4",
37+
"nanoid": "^3.1.23",
3738
"simple-git": "^2.39.0",
39+
"tippy.js": "^6.3.1",
3840
"vue": "^3.0.11",
3941
"vue-mdijs": "^0.5.0",
4042
"vue-router": "^4.0.8",
41-
"vuedraggable": "^4.0.1"
43+
"vue-toastification": "^2.0.0-rc.1",
44+
"vuedraggable": "^4.0.1",
45+
"vuex": "^4.0.1",
46+
"vuex-persistedstate": "^4.0.0-beta.3"
4247
},
4348
"devDependencies": {
4449
"@rollup/plugin-eslint": "^8.0.1",

packages/main/src/index.js

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {app, BrowserWindow} from 'electron';
2-
import {join} from 'path';
3-
import {URL} from 'url';
4-
1+
import { app, BrowserWindow, dialog, ipcMain } from 'electron';
2+
import { join } from 'path';
3+
import { URL } from 'url';
4+
import { readFile } from 'fs/promises';
5+
import store from './lib/electron-store';
56

67
const isSingleInstance = app.requestSingleInstanceLock();
78

@@ -86,6 +87,26 @@ app.on('window-all-closed', () => {
8687
}
8788
});
8889

90+
ipcMain.handle('select-dir', async () => {
91+
try {
92+
const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openDirectory'] });
93+
94+
if (canceled) return { canceled };
95+
96+
const packageJSONPath = join(filePaths[0], 'package.json');
97+
const packageJSON = JSON.parse(await readFile(packageJSONPath));
98+
99+
return { path: filePaths[0], config: packageJSON };
100+
} catch (error) {
101+
throw new Error('Can\'t find package.json file');
102+
}
103+
});
104+
105+
ipcMain.handle('storage-get', (event, { key, def }) => Promise.resolve(store.get(key, def)));
106+
ipcMain.handle('storage-set', (event, { key, value }) => Promise.resolve(store.set(key, value)));
107+
ipcMain.handle('storage-delete', (event, key) => Promise.resolve(store.delete(key)));
108+
ipcMain.handle('storage-has', (event, key) => Promise.resolve(store.delete(key)));
109+
ipcMain.handle('storage-clear', () => Promise.resolve(store.clear()));
89110

90111
app.whenReady()
91112
.then(createWindow)
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Store from 'electron-store';
2+
3+
const scheme = {
4+
projects: {
5+
type: 'object',
6+
patternProperties: {
7+
'[A-Za-z0-9_-]': {
8+
properties: {
9+
name: { type: 'string' },
10+
path: { type: 'string' },
11+
createdAt: {
12+
type: 'number',
13+
default: Date.now(),
14+
},
15+
starred: {
16+
type: 'boolean',
17+
default: false,
18+
},
19+
},
20+
required: ['name', 'path'],
21+
},
22+
},
23+
},
24+
}
25+
26+
const store = new Store({ scheme });
27+
28+
export default store;

packages/preload/src/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/* eslint-disable */
2-
import { contextBridge, remote } from 'electron';
2+
import { contextBridge, ipcRenderer } from 'electron';
33

44
const apiKey = 'electron';
55
/**
66
* @see https://github.com/electron/electron/issues/21437#issuecomment-573522360
77
*/
88
const api = {
9-
remote,
9+
ipcRenderer,
1010
versions: process.versions,
1111
};
1212

packages/renderer/src/App.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
<app-bottom-menu></app-bottom-menu>
99
</template>
1010
<script>
11+
import { useStore } from 'vuex';
1112
import AppBottomMenu from './components/app/AppBottomMenu.vue';
1213
import AppSidebar from './components/app/AppSidebar.vue';
1314
1415
export default {
1516
components: { AppBottomMenu, AppSidebar },
1617
setup() {
17-
console.log(window);
18+
const store = useStore();
19+
20+
store.dispatch('retrieve');
1821
},
1922
};
2023
</script>

packages/renderer/src/components/app/AppBottomMenu.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
bottom-0
1010
w-full
1111
left-0
12-
bg-black bg-opacity-25
12+
bg-gray-1000
1313
text-gray-300
14+
border-t
1415
"
1516
>
1617
<v-mdi name="mdi-console" size="20"></v-mdi>

packages/renderer/src/components/app/AppSidebar.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<aside class="w-64 border-r border-white border-opacity-5 p-5">
2+
<aside class="w-64 bg-gray-1000 p-5">
33
<ui-list>
44
<ui-list-item>in the </ui-list-item>
55
</ui-list>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<template>
2+
<div class="flex items-center mb-12">
3+
<h2 class="text-2xl font-semibold">Projects</h2>
4+
<div class="flex-grow"></div>
5+
<ui-input placeholder="Search" prepend-icon="mdi-magnify"></ui-input>
6+
<ui-button icon class="ml-2">
7+
<v-mdi name="mdi-filter-outline"></v-mdi>
8+
</ui-button>
9+
<ui-button variant="primary" class="ml-2" @click="projectModal.show = true">
10+
<v-mdi name="mdi-plus" class="mr-1 -ml-2"></v-mdi>
11+
Project
12+
</ui-button>
13+
<ui-modal v-model="projectModal.show" content-class="max-w-md">
14+
<template #header>
15+
<p>Import Project</p>
16+
</template>
17+
<div class="flex items-end">
18+
<ui-input
19+
label="Select a folder"
20+
readonly
21+
class="flex-1 mr-2"
22+
placeholder="No folder selected"
23+
:model-value="projectModal.path"
24+
@click="selectDirectory"
25+
></ui-input>
26+
<ui-button icon @click="selectDirectory">
27+
<v-mdi name="mdi-folder-open-outline"></v-mdi>
28+
</ui-button>
29+
</div>
30+
<form class="mt-4" @submit.prevent="importProject">
31+
<ui-input
32+
label="Project name"
33+
placeholder="Name"
34+
v-model="projectModal.name"
35+
class="w-full"
36+
></ui-input>
37+
<ui-button
38+
class="w-full mt-12"
39+
variant="primary"
40+
:disabled="!projectModal.name || !projectModal.path"
41+
>
42+
Import Project
43+
</ui-button>
44+
</form>
45+
</ui-modal>
46+
</div>
47+
</template>
48+
<script>
49+
import { shallowReactive } from 'vue';
50+
import { useStore } from 'vuex';
51+
import { useToast } from 'vue-toastification';
52+
53+
export default {
54+
setup() {
55+
const toast = useToast();
56+
const store = useStore();
57+
58+
const projectModal = shallowReactive({
59+
name: '',
60+
path: '',
61+
show: false,
62+
starred: false,
63+
});
64+
const { ipcRenderer } = window.electron;
65+
66+
function selectDirectory() {
67+
ipcRenderer.invoke('select-dir').then(({ canceled, path, config }) => {
68+
if (canceled) return;
69+
70+
projectModal.path = path;
71+
projectModal.name = config.name || path.split('\\').pop();
72+
}).catch((error) => {
73+
toast.error('Can\'t find package.json');
74+
});
75+
}
76+
function importProject() {
77+
if (!projectModal.name || !projectModal.path) return;
78+
79+
const copy = { ...projectModal };
80+
81+
delete copy.show;
82+
83+
store.dispatch('projects/add', copy).then(() => {
84+
projectModal.show = false;
85+
projectModal.name = projectModal.path = '';
86+
}).catch((error) => {
87+
toast.error(error);
88+
});
89+
}
90+
91+
return {
92+
projectModal,
93+
importProject,
94+
selectDirectory,
95+
};
96+
}
97+
};
98+
</script>

packages/renderer/src/components/ui/Input.vue

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div class="inline-block input-ui">
33
<label class="relative">
4-
<span v-if="label" class="text-sm mb-1">{{ label }}</span>
4+
<span v-if="label" class="text-sm text-gray-200 mb-1">{{ label }}</span>
55
<div class="flex items-center">
66
<v-mdi
77
v-if="prependIcon"
@@ -24,7 +24,7 @@
2424
'opacity-75 pointer-events-none': disabled,
2525
'pl-10': prependIcon,
2626
}"
27-
v-bind="{ readonly: disabled || null, placeholder, type }"
27+
v-bind="{ readonly: (disabled || readonly) || null, placeholder, type }"
2828
:value="modelValue"
2929
@input="$emit('update:modelValue', $event.target.value)"
3030
/>
@@ -48,6 +48,10 @@ export default {
4848
type: Boolean,
4949
default: false,
5050
},
51+
readonly: {
52+
type: Boolean,
53+
default: false,
54+
},
5155
modelValue: {
5256
type: String,
5357
default: '',

packages/renderer/src/components/ui/Modal.vue

+4-19
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,20 @@
77
<transition name="modal" mode="out-in">
88
<div
99
v-if="show"
10-
class="
11-
bg-black
12-
p-5
13-
overflow-y-auto
14-
bg-opacity-20
15-
modal-ui__content-container
16-
z-50
17-
flex
18-
justify-center
19-
items-end
20-
md:items-center
21-
"
10+
class="bg-black p-5 overflow-y-auto bg-opacity-40 modal-ui__content-container z-50 flex justify-center items-end md:items-center"
2211
:style="{ 'backdrop-filter': blur && 'blur(2px)' }"
2312
@click.self="closeModal"
2413
>
2514
<slot v-if="customContent"></slot>
26-
<ui-card
27-
v-else
28-
class="modal-ui__content shadow-rounded-lg w-full"
29-
:class="[contentClass]"
30-
>
15+
<ui-card v-else class="modal-ui__content shadow-lg w-full" :class="[contentClass]">
3116
<div class="mb-4">
3217
<div class="flex items-center justify-between">
3318
<span class="content-header">
3419
<slot name="header"></slot>
3520
</span>
3621
<v-mdi
3722
v-show="!persist"
38-
class="text-gray-600 cursor-pointer"
23+
class="text-gray-300 cursor-pointer"
3924
name="mdi-close"
4025
size="20"
4126
@click="closeModal"
@@ -64,7 +49,7 @@ export default {
6449
},
6550
contentClass: {
6651
type: String,
67-
default: 'max-w-rounded-lg',
52+
default: 'max-w-lg',
6853
},
6954
customContent: Boolean,
7055
persist: Boolean,

0 commit comments

Comments
 (0)