Skip to content

Commit

Permalink
Backend Version 5.8.5, Application Version 1.1.1
Browse files Browse the repository at this point in the history
- Added the ability to export profiles
  • Loading branch information
nathan-fiscaletti committed Nov 8, 2024
1 parent 22210e2 commit 734a06e
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 14 deletions.
2 changes: 1 addition & 1 deletion application/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "kbs-electron",
"productName": "Keyboard Sounds",
"version": "1.1.0",
"version": "1.1.1",
"description": "https://keyboardsounds.net/",
"main": ".webpack/main",
"repository": {
Expand Down
22 changes: 22 additions & 0 deletions application/src/api/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,28 @@ const kbs = {
return "";
},

selectExportPath: async function(profileToExport) {
if (this.openFileDialogIsOpen) {
return;
}

this.openFileDialogIsOpen = true;
const res = await dialog.showSaveDialog(this.mainWindow, {
title: `Export Profile '${profileToExport}'`,
defaultPath: `${profileToExport}.zip`,
filters: [
{ name: 'Zip Archive', extensions: ['zip'] }
]
});
this.openFileDialogIsOpen = false;
this.mainWindow.show();
this.mainWindow.focus();
if (!res.canceled) {
return res.filePath
}
return "";
},

executeDaemonCommand: async function(command) {
const status = await this.status();
if (status.status !== 'running') {
Expand Down
130 changes: 126 additions & 4 deletions application/src/ui/pages/profiles.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";

import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
Expand All @@ -10,16 +10,19 @@ import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import InputAdornment from '@mui/material/InputAdornment';
import Dialog from '@mui/material/Dialog';

import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined';
import AddIcon from '@mui/icons-material/Add';
import SearchIcon from '@mui/icons-material/Search';
import IosShareIcon from '@mui/icons-material/IosShare';
import FileOpenIcon from '@mui/icons-material/FileOpenOutlined';
import CloseIcon from '@mui/icons-material/Close';
import SaveIcon from '@mui/icons-material/Save';
import { Chip, CircularProgress } from "@mui/material";
import { execute } from "../execute";

function ProfileListItem({ statusLoaded, status, profile: { name, author, description } }) {
function ProfileListItem({ statusLoaded, status, profile: { name, author, description }, onExport }) {
const [isDeleting, setIsDeleting] = useState(false);

return (
Expand All @@ -31,7 +34,7 @@ function ProfileListItem({ statusLoaded, status, profile: { name, author, descri
<Chip sx={{ mr: 1 }} size="small" label="Active" variant="filled" color="success" />
)}
<Tooltip title="Export & Share" placement="top" arrow>
<IconButton color="primary" sx={{ mr: 1 }}>
<IconButton color="primary" sx={{ mr: 1 }} onClick={onExport}>
<IosShareIcon />
</IconButton>
</Tooltip>
Expand Down Expand Up @@ -102,12 +105,123 @@ function ProfileListItem({ statusLoaded, status, profile: { name, author, descri

const Profiles = ({statusLoaded, status, profilesLoaded, profiles}) => {
const [profileSearchValue, setProfileSearchValue] = useState('');
const [exportProfileDialogOpen, setExportProfileDialogOpen] = useState(false);
const [exportPath, setExportPath] = useState("");
const [exportingProfile, setExportingProfile] = useState(false);
const [profileToExport, setProfileToExport] = useState("");

useEffect(() => {
if (!exportProfileDialogOpen) {
setExportPath("");
}
}, [exportProfileDialogOpen]);

const selectExportPath = () => {
execute(`selectExportPath ${profileToExport}`).then((path) => {
if (path) {
setExportPath(path);
}
});
};

const exportProfile = () => {
if (exportPath === "") {
return;
}

setExportingProfile(true);
execute(`export-profile --name "${profileToExport}" --output "${exportPath}"`)
.then(() => {
setExportingProfile(false);
setExportProfileDialogOpen(false);
setProfileToExport("");
});
};

return (
<Box sx={{
ml: 2,
mt: 2,
}}>
<Dialog open={exportProfileDialogOpen} fullWidth>
<Box sx={{
display: 'flex',
flexDirection: 'column',
p: 2,
}}>
<Box sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Typography variant="h6">Export Profile</Typography>
<IconButton onClick={() => setExportProfileDialogOpen(false)}>
<CloseIcon />
</IconButton>
</Box>
<Typography variant="body" sx={{ mt: 2 }}>
Export To
</Typography>
<Box sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
border: '1px solid rgba(255, 255, 255, 0.1)',
borderRadius: 1,
alignItems: 'center',
mt: 1,
p: 1,
}}>
{exportPath !== "" && (
<Tooltip title={exportPath} followCursor>
<Typography variant="body2" color="GrayText" noWrap sx={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
maxWidth: "calc(100vw - 250px)",
}}>
{exportPath}
</Typography>
</Tooltip>
)}
{exportPath === "" && (
<Typography variant="body2" color="GrayText">
Specify an export path...
</Typography>
)}

<Button
startIcon={<FileOpenIcon />}
variant="outlined"
size="small"
sx={{ ml: 1 }}
onClick={selectExportPath}
>
Select
</Button>
</Box>

<Button
fullWidth
variant="contained"
startIcon={
exportingProfile ? (
<CircularProgress size={18} />
) : (
<SaveIcon />
)
}
sx={{ mt: 3, }}
disabled={exportPath === "" || exportingProfile}
onClick={exportProfile}
>
Save
</Button>
</Box>
</Dialog>

<Box
sx={{
display: "flex",
Expand Down Expand Up @@ -164,7 +278,15 @@ const Profiles = ({statusLoaded, status, profilesLoaded, profiles}) => {
},
}}>
{profilesLoaded && profiles.filter(p => profileSearchValue === "" || p.name.toLowerCase().includes(profileSearchValue.toLowerCase())).map((profile) => (
<ProfileListItem statusLoaded={statusLoaded} status={status} key={profile.name} profile={profile} />
<ProfileListItem
statusLoaded={statusLoaded}
status={status}
key={profile.name}
profile={profile}
onExport={() => {
setProfileToExport(profile.name);
setExportProfileDialogOpen(true);
}} />
))}
</List>
</Box>
Expand Down
6 changes: 6 additions & 0 deletions docs/backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ $ kbs stop
## Manage Profiles

```bash
# Create a new profile
$ kbs new -n "My Profile"

# Export an existing profile
$ kbs export-profile -n "My Profile" -o "My Profile.zip"

# List downloadable profiles
$ kbs list-profiles --remote

Expand Down
9 changes: 9 additions & 0 deletions docs/custom-profiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This application supports custom profiles in which you can provide your own WAV
## Index

- [Importing a profile](#importing-a-profile)
- [Exporting an existing profile](#exporting-an-existing-profile)
- [Creating a new Profile](#creating-a-new-profile)
- [Editing a Profile](#editing-a-profile)
- [Compiling a Profile](#compiling-a-profile)
Expand All @@ -18,6 +19,14 @@ Profiles can be imported from a ZIP file using the [`add-profile`](./backend.md#
$ kbs add-profile -z "./my-profile.zip"
```

## Exporting an existing profile

Profiles can be exported from the command line using the [`export-profile`](./backend.md#manage-profiles) action.

```bash
$ kbs export-profile -n my-profile -o "./my-profile.zip"
```

## Creating a new Profile

Create a new profile using the following command:
Expand Down
6 changes: 4 additions & 2 deletions keyboardsounds/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def main():
f" %(prog)s <rp|remove-profile> -n <profile>{os.linesep}"
f" %(prog)s <lp|list-profiles> [-s] [--remote]{os.linesep}"
f" %(prog)s <dp|download-profile> -n <profile>{os.linesep}"
f" %(prog)s <ex|export-profile> -n <profile> -o <zip_file>{os.linesep * 2}"
f" %(prog)s <bp|build-profile> -d <sound_dir> -o <zip_file>{os.linesep * 2}"
)
+ win_messages
Expand Down Expand Up @@ -291,11 +292,12 @@ def main():
Profile.download_profile(args.name)
except Exception as e:
print(e)

elif args.action == "create-profile" or args.action == "new":
Profile.create_profile(args.directory, args.name)
return

elif args.action == "export-profile" or args.action == "ex":
Profile.export_profile(args.name, args.output)
return
# Rules are only available on windows
elif WIN32 and (args.action == "list-rules" or args.action == "lr"):
rules = get_rules()
Expand Down
19 changes: 19 additions & 0 deletions keyboardsounds/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from keyboardsounds.root import ROOT
from keyboardsounds.path_resolver import PathResolver
from keyboardsounds.profile_validation import validate_profile
from keyboardsounds.profile_builder import CliProfileBuilder

PROFILES_REMOTE_URL = "https://api.github.com/repos/nathan-fiscaletti/keyboardsounds/contents/keyboardsounds/profiles?ref=master"
PROFILE_REMOTE_URL = "https://api.github.com/repos/nathan-fiscaletti/keyboardsounds/contents/keyboardsounds/profiles/{name}?ref=master"
Expand Down Expand Up @@ -60,6 +61,10 @@ def metadata(self):
)
return {"name": name, "author": author, "description": description}

def export(self, output: str):
# export profile to zip file
CliProfileBuilder(self.root, output).save()

@classmethod
def list(cls):
names = [
Expand Down Expand Up @@ -220,3 +225,17 @@ def create_profile(cls, path: str, name: str):
print("")
print(f" kbs build-profile -path {path} -o {name}.zip")
print("")

@classmethod
def export_profile(cls, name: str, output: str):
if name is None:
print("Please specify a name for the profile to export.")
return

try:
profile = Profile(name)
except Exception as e:
print(f"Failed to find profile '{name}'. Error: {e}")
return

profile.export(output)
10 changes: 5 additions & 5 deletions keyboardsounds/profile_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ def build(self, output: str):
print(f"Writing '{file}'...")
source = self.get_file_path(file)
destination = intermediate.get_file_path(file)
if not os.path.isfile(destination):
raise ValueError(f"Missing audio file '{file}'.")
if not os.path.isfile(source):
raise ValueError(f"Missing intermediate audio file '{file}'.")
shutil.copy(source, destination)

print(f"Writing archive to '{output}'...")
Expand Down Expand Up @@ -276,7 +276,7 @@ def start(self):
self.__collect_metadata()
else:
if self.__output is not None:
self.__save()
self.save()
else:
self.__open_command_interface()

Expand Down Expand Up @@ -313,7 +313,7 @@ def __process_command(self):
elif self.__cmd == "remove":
return self.__remove()
elif self.__cmd == "save":
return self.__save()
return self.save()
elif self.__cmd == "cancel":
return False

Expand Down Expand Up @@ -546,7 +546,7 @@ def __preview(self) -> bool:
print(self.__builder.preview())
return True

def __save(self) -> bool:
def save(self) -> bool:
if len(self.__args) < 1:
output = self.__output
else:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "keyboardsounds"
version = "5.8.4"
version = "5.8.5"
authors = [
{ name="Nathan Fiscaletti", email="nate.fiscaletti@gmail.com" },
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name="keyboardsounds",
version="5.8.4",
version="5.8.5",
description="Adds the ability to play sounds while typing on any system.",
author="Nathan Fiscaletti",
author_email="nate.fiscaletti@gmail.com",
Expand Down

0 comments on commit 734a06e

Please sign in to comment.