feat: begin of gui stuff, loading repository in gui works

This commit is contained in:
overflowerror 2021-10-07 18:27:09 +02:00
parent 7ed8d49f55
commit 05bf42c2e3
15 changed files with 189 additions and 18 deletions

View file

@ -1,9 +1,14 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import RepositoryProvider from "./components/RepositoryProvider";
import Home from "./views/Home";
function render() {
ReactDOM.render(
<h2>Hello from React!</h2>, document.body
<RepositoryProvider>
<Home />
</RepositoryProvider>,
document.getElementById("root")
);
}

View file

@ -0,0 +1,12 @@
import React, {FunctionComponent} from "react";
export type MediaListProps = {
}
const MediaList: FunctionComponent<MediaListProps> = () => {
return (
<div></div>
)
}
export default MediaList

View file

@ -0,0 +1,42 @@
import React, {FunctionComponent, useContext, useEffect, useState} from "react";
import Repository from "../../repository";
import {ipcRenderer} from 'electron';
const OPEN_REPOSITORY_MESSAGE_NAME = "MSG_OPEN_REPO"
const RepositoryContext = React.createContext<Repository|null>(null)
export const useRepository = () => useContext(RepositoryContext)
type RepositoryProviderProps = {
}
const RepositoryProvider: FunctionComponent<RepositoryProviderProps> = ({children}) => {
const [repository, setRepository] = useState<Repository|null>(null)
useEffect(() => {
const listener = (event: any, data: any) => {
console.log(data)
const _path = data as string
if (_path) {
setRepository(new Repository(_path))
} else {
setRepository(null)
}
}
ipcRenderer.on(OPEN_REPOSITORY_MESSAGE_NAME, listener)
return () => {
// cleanup
ipcRenderer.removeListener(OPEN_REPOSITORY_MESSAGE_NAME, listener)
}
}, [setRepository])
return (
<RepositoryContext.Provider value={repository}>
{children}
</RepositoryContext.Provider>
)
}
export default RepositoryProvider

View file

@ -0,0 +1,37 @@
import React, {FunctionComponent, useEffect, useState} from "react";
import {useRepository} from "../RepositoryProvider";
import {CountedTag} from "../../database/query";
export type SidebarProps = {
}
const Sidebar: FunctionComponent<SidebarProps> = () => {
const repository = useRepository()
const [tags, setTags] = useState<CountedTag[]>([])
useEffect(() => {
console.log("sidebar use effect")
if (repository) {
repository.get().then(d => {
console.log(d)
setTags(d.query().countedTags())
}).catch(console.log)
}
}, [repository])
return (
<div>
<ul>
{
tags.map(t => (
<li>{t.name} ({t.count})</li>
))
}
</ul>
</div>
)
}
export default Sidebar

View file

@ -1,5 +1,5 @@
const KEY_DIRECTORIES = "directories"
const KEY_FILES = "files"
export const KEY_DIRECTORIES = "directories"
export const KEY_FILES = "files"
const KEY_FILE_PATH = "path"
const KEY_FILE_TAGS = "tags"
export const KEY_FILE_PATH = "path"
export const KEY_FILE_TAGS = "tags"

View file

@ -1,4 +1,9 @@
class Database {
import {KEY_DIRECTORIES, KEY_FILES} from "./config-keys";
import {Directory} from "./directory";
import {MediaFile} from "./file";
import {Query} from "./query";
export class Database {
private directories: Directory[]
private files: MediaFile[]
@ -70,4 +75,4 @@ class Database {
return obj
}
}
}

View file

@ -1,4 +1,4 @@
class Directory {
export class Directory {
private readonly path: string
constructor(path: string) {

View file

@ -1,10 +1,10 @@
enum MediaFileType {
export enum MediaFileType {
unknown,
video,
image
}
function FileTypeFromExtension(name: string): MediaFileType {
export function FileTypeFromExtension(name: string): MediaFileType {
while (name.indexOf(".") >= 0) {
name = name.substring(name.indexOf(".") + 1)
}

View file

@ -1,4 +1,7 @@
class MediaFile {
import {FileTypeFromExtension, MediaFileType} from "./file-type";
import {KEY_FILE_PATH, KEY_FILE_TAGS} from "./config-keys";
export class MediaFile {
private readonly path: string
private readonly type: MediaFileType
private tags: string[]

View file

@ -1,10 +1,12 @@
type CountedTag = {
import {MediaFile} from "./file";
import {MediaFileType} from "./file-type";
export type CountedTag = {
name: string,
count: number
}
class Query {
export class Query {
private readonly files: MediaFile[]
public constructor(files: MediaFile[]) {

View file

@ -5,5 +5,7 @@
<title>Tagify</title>
</head>
<body>
<div id="root">
</div>
</body>
</html>

View file

@ -1,9 +1,11 @@
import { app, BrowserWindow } from 'electron';
import {app, BrowserWindow, dialog, Menu} from 'electron';
// This allows TypeScript to pick up the magic constant that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
export const OPEN_REPOSITORY_MESSAGE_NAME = "MSG_OPEN_REPO"
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
@ -13,7 +15,11 @@ const createWindow = (): void => {
// Create the browser window.
const mainWindow = new BrowserWindow({
height: 600,
width: 800,
width: 1300,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
// and load the index.html of the app.
@ -21,6 +27,35 @@ const createWindow = (): void => {
// Open the DevTools.
mainWindow.webContents.openDevTools();
const menu = Menu.buildFromTemplate([
{
label: 'File',
submenu: [
{
label: 'Open Repository',
click: menuItem => {
dialog.showOpenDialog({
title: "Open Repository",
properties: [
"openDirectory",
]
}).then(result => {
if (result.canceled || result.filePaths.length != 1) {
return
}
mainWindow.webContents.send(OPEN_REPOSITORY_MESSAGE_NAME, result.filePaths[0])
})
}
},
{
label: 'Exit',
click: menuItem => app.quit()
}
]
}
])
Menu.setApplicationMenu(menu);
};
// This method will be called when Electron has finished

View file

@ -1,6 +1,8 @@
import * as fsPromises from "fs/promises";
import {Database} from "../database/database";
import {FileTypeFromExtension, MediaFileType} from "../database/file-type";
const DB_FILE = ".tagdb.json"
const DB_FILE = ".tagdb"
class Repository {
private readonly path: string
@ -57,4 +59,6 @@ class Repository {
return untagged.filter((v, i, s) => s.indexOf(v) == i)
}
}
}
export default Repository

23
src/views/Home/index.tsx Normal file
View file

@ -0,0 +1,23 @@
import React, {FunctionComponent, useState} from "react";
import Sidebar from "../../components/Sidebar";
import MediaList from "../../components/MediaList";
import {MediaFile} from "../../database/file";
export type HomeProps = {
}
const Home: FunctionComponent<HomeProps> = () => {
const [selected, setSelected] = useState<MediaFile|null>(null)
return (
<div>
<Sidebar />
{ !selected &&
<MediaList />
}
</div>
)
}
export default Home

View file

@ -14,4 +14,5 @@ module.exports = {
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css']
},
target: 'electron-renderer'
};