Skip to content

Commit

Permalink
Merge pull request #387 from allusion-app/rc6-fixes
Browse files Browse the repository at this point in the history
RC6 fixes
  • Loading branch information
RvanderLaan authored Dec 5, 2021
2 parents 3601dd1 + db61682 commit f6f08fa
Show file tree
Hide file tree
Showing 20 changed files with 268 additions and 59 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "allusion",
"productName": "Allusion",
"version": "1.0.0-rc6",
"version": "1.0.0-rc6.1",
"description": "A tool for managing your visual library",
"main": "build/main.bundle.js",
"scripts": {
Expand Down Expand Up @@ -90,7 +90,7 @@
"@typescript-eslint/eslint-plugin": "^4.2.0",
"@typescript-eslint/parser": "^4.2.0",
"css-loader": "^5.2.0",
"electron": "15.0.0",
"electron": "^15.3.3",
"electron-builder": "^22.10.5",
"eslint": "^7.18.0",
"eslint-config-prettier": "^8.1.0",
Expand Down
15 changes: 14 additions & 1 deletion resources/style/inspector.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
///////////////////////////////// Inspector /////////////////////////////////
#inspector {
background-color: var(--background-color);
overflow: hidden;
overflow: hidden auto; // show a Y scrollbar if inspector is too small in height to fit min-height of its content

display: flex;
flex-direction: column;

> * {
padding: 0.25rem 0.5rem;

// Tags section should shrink to fit its parent if the inspector is small in height
&:last-child {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
// so that the tags section remains visible when inspector is very small in height: inspector should overflow with a scrollbar
min-height: 6rem;
}
}

header {
Expand Down
4 changes: 3 additions & 1 deletion resources/style/remake/outliner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@

.tree-content-label {
display: flex;
flex: 1 1 auto;
align-items: center;
height: 1.5rem;
width: 100%;
overflow: hidden;
white-space: nowrap;

span {
Expand Down Expand Up @@ -148,10 +150,10 @@
}

> .label-text {
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: clip visible;
width: 100%;
line-height: 1;
padding-bottom: 0px;
}
Expand Down
22 changes: 18 additions & 4 deletions resources/style/remake/tag-editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,25 @@
overflow: auto;
resize: vertical;

// Intended layout behavior:
// - Input element has a static height
// - The tag checklist grows to fit the available space, until a max-height is reached
// - The applied tags section has a static height of 1.5 rows of tags,
// and grows when the tag check-list has reached max-height
// TODO: preferably: this section first fits to its content up to 3.5 rows (so only 1 row if there is 1 row of tags)
// could figure out the CSS: we need something like min-height: calc(max(1.5rem, min(fit-content, 3.5rem)))
// maybe with the fit-content() property coming soon? https://developer.mozilla.org/en-US/docs/Web/CSS/min-height#:~:text=5.0-,fit%2Dcontent(),-Experimental

> input {
margin: 0.5rem;
width: auto;
height: auto;
min-height: fit-content; // prevents weird slight resize when checklist shrinks when resizing parent
}

// Checklist of all available tags
[role='grid'] {
flex: 1;
flex: 0 1 calc(16rem + 1px); // expand to fill available space, don't shrink unless absolutaly necessary
border-top: 0.0625rem solid var(--border-color);
border-bottom: 0.0625rem solid var(--border-color);
max-width: unset;
Expand All @@ -34,14 +45,17 @@
}
}

// The tags applied to the selected images
> div:last-child {
flex: 1 1 auto; // shrink to make room for other elements, but grow if there is space available (other other elements reached their max height)
margin: 0.5rem;
min-height: 1.375rem;
max-height: 3.5rem;
height: 3.375rem;
min-height: 2.375rem;
// preferably: min-height calc(max(min(3.75rem, fit-content), 1.375rem)), but doesn't work
overflow: auto;
display: flex;
flex-wrap: wrap;
align-items: center;
align-content: flex-start;
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/Messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const TOGGLE_DEV_TOOLS = 'TOGGLE_DEV_TOOLS';
const RELOAD = 'RELOAD';
const OPEN_DIALOG = 'OPEN_DIALOG';
const GET_PATH = 'GET_PATH';
const TRASH_FILE = 'TRASH_FILE';
const SET_FULL_SCREEN = 'SET_FULL_SCREEN';
const IS_FULL_SCREEN = 'IS_FULL_SCREEN';
const FULL_SCREEN_CHANGED = 'FULL_SCREEN_CHANGED';
Expand Down Expand Up @@ -137,6 +138,9 @@ export class RendererMessenger {

static getPath = (name: SYSTEM_PATHS): Promise<string> => ipcRenderer.invoke(GET_PATH, name);

static trashFile = (absolutePath: string): Promise<Error | undefined> =>
ipcRenderer.invoke(TRASH_FILE, absolutePath);

static setFullScreen = (isFullScreen: boolean) =>
ipcRenderer.invoke(SET_FULL_SCREEN, isFullScreen);

Expand Down Expand Up @@ -235,6 +239,15 @@ export class MainMessenger {
static onGetPath = (cb: (name: SYSTEM_PATHS) => string) =>
ipcMain.handle(GET_PATH, (_, name) => cb(name));

static onTrashFile = (cb: (absolutePath: string) => Promise<void>) =>
ipcMain.handle(TRASH_FILE, async (_, absolutePath) => {
try {
await cb(absolutePath);
} catch (e) {
return e;
}
});

static onSetFullScreen = (cb: (isFullScreen: boolean) => void) =>
ipcMain.handle(SET_FULL_SCREEN, (_, isFullScreen) => cb(isFullScreen));

Expand Down
50 changes: 42 additions & 8 deletions src/frontend/components/FileTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { IconSet } from 'widgets/Icons';
import { Row } from 'widgets';
import { useStore } from '../contexts/StoreContext';
import { TagSelector } from './TagSelector';
import useContextMenu from '../hooks/useContextMenu';
import { FileTagMenuItems } from '../containers/ContentView/menu-items';
import { ClientTag } from 'src/entities/Tag';
import { ContextMenu, Menu } from 'widgets/menus';

interface IFileTagProp {
file: ClientFile;
Expand All @@ -13,6 +17,8 @@ interface IFileTagProp {
const FileTags = observer(({ file }: IFileTagProp) => {
const { tagStore } = useStore();

const [contextState, { show, hide }] = useContextMenu();

const renderCreateOption = useCallback(
(tagName: string, resetTextBox: () => void) => (
<Row
Expand All @@ -30,15 +36,43 @@ const FileTags = observer(({ file }: IFileTagProp) => {
[file, tagStore],
);

const handleTagContextMenu = useCallback(
(event: React.MouseEvent<HTMLElement>, tag: ClientTag) => {
event.stopPropagation();
show(event.clientX, event.clientY, [
<React.Fragment key="file-tag-context-menu">
<FileTagMenuItems file={file} tag={tag} />
</React.Fragment>,
]);
},
[file, show],
);

return (
<TagSelector
disabled={file.isBroken}
selection={Array.from(file.tags)}
onClear={file.clearTags}
onDeselect={file.removeTag}
onSelect={file.addTag}
renderCreateOption={renderCreateOption}
/>
<>
<TagSelector
disabled={file.isBroken}
selection={Array.from(file.tags)}
onClear={file.clearTags}
onDeselect={file.removeTag}
onSelect={file.addTag}
renderCreateOption={renderCreateOption}
showTagContextMenu={handleTagContextMenu}
multiline
/>

{/* TODO: probably not the right place for the ContextMenu component.
Why not a single one at the root element that can be interacted with through a Context? */}
<ContextMenu
isOpen={contextState.open}
x={contextState.x}
y={contextState.y}
close={hide}
usePortal
>
<Menu>{contextState.menu}</Menu>
</ContextMenu>
</>
);
});

Expand Down
24 changes: 19 additions & 5 deletions src/frontend/components/RemovalAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { ClientTag } from 'src/entities/Tag';
import { useStore } from 'src/frontend/contexts/StoreContext';
import { Tag, IconSet } from 'widgets';
import { Alert, DialogButton } from 'widgets/popovers';
import { shell } from 'electron';
import { AppToaster } from './Toaster';
import { RendererMessenger } from 'src/Messaging';

interface IRemovalProps<T> {
object: T;
Expand Down Expand Up @@ -124,14 +125,27 @@ export const MoveFilesToTrashBin = observer(() => {
uiStore.closeMoveFilesToTrash();
const files = [];
for (const file of selection) {
try {
await shell.trashItem(file.absolutePath);
// File deletion used to be possible in renderer process, not in new electron version
// await shell.trashItem(file.absolutePath);
// https://github.com/electron/electron/issues/29598
const error = await RendererMessenger.trashFile(file.absolutePath);
if (!error) {
files.push(file);
} catch (error) {
console.warn('Could not move file to trash', file.absolutePath);
} else {
console.warn('Could not move file to trash', file.absolutePath, error);
}
}
fileStore.deleteFiles(files);
if (files.length !== selection.size) {
AppToaster.show({
message: 'Some files could not be deleted.',
clickAction: {
onClick: () => RendererMessenger.toggleDevTools(),
label: 'More info',
},
timeout: 8000,
});
}
});

const isMulti = selection.size > 1;
Expand Down
21 changes: 18 additions & 3 deletions src/frontend/components/TagSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface TagSelectorProps {
inputText: string,
resetTextBox: () => void,
) => ReactElement<RowProps> | ReactElement<RowProps>[];
multiline?: boolean;
showTagContextMenu?: (e: React.MouseEvent<HTMLElement>, tag: ClientTag) => void;
}

const TagSelector = (props: TagSelectorProps) => {
Expand All @@ -29,10 +31,12 @@ const TagSelector = (props: TagSelectorProps) => {
onSelect,
onDeselect,
onTagClick,
showTagContextMenu,
onClear,
disabled,
extraIconButtons,
renderCreateOption,
multiline,
} = props;
const gridId = useRef(generateWidgetId('__suggestions')).current;
const inputRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -86,6 +90,8 @@ const TagSelector = (props: TagSelectorProps) => {

const handleFocus = useRef(() => setIsOpen(true)).current;

const handleBackgroundClick = useCallback(() => inputRef.current?.focus(), []);

const resetTextBox = useRef(() => {
inputRef.current?.focus();
setQuery('');
Expand All @@ -109,8 +115,9 @@ const TagSelector = (props: TagSelectorProps) => {
aria-expanded={isOpen}
aria-haspopup="grid"
aria-owns={gridId}
className="input multiautocomplete tag-selector"
className={`input multiautocomplete tag-selector ${multiline ? 'multiline' : ''}`}
onBlur={handleBlur}
onClick={handleBackgroundClick}
>
<Flyout
isOpen={isOpen}
Expand All @@ -121,7 +128,13 @@ const TagSelector = (props: TagSelectorProps) => {
<div className="multiautocomplete-input">
<div className="input-wrapper">
{selection.map((t) => (
<SelectedTag key={t.id} tag={t} onDeselect={onDeselect} onTagClick={onTagClick} />
<SelectedTag
key={t.id}
tag={t}
onDeselect={onDeselect}
onTagClick={onTagClick}
showContextMenu={showTagContextMenu}
/>
))}
<input
disabled={disabled}
Expand Down Expand Up @@ -161,16 +174,18 @@ interface SelectedTagProps {
tag: ClientTag;
onDeselect: (item: ClientTag) => void;
onTagClick?: (item: ClientTag) => void;
showContextMenu?: (e: React.MouseEvent<HTMLElement>, item: ClientTag) => void;
}

const SelectedTag = observer((props: SelectedTagProps) => {
const { tag, onDeselect, onTagClick } = props;
const { tag, onDeselect, onTagClick, showContextMenu } = props;
return (
<Tag
text={tag.name}
color={tag.viewColor}
onRemove={() => onDeselect(tag)}
onClick={onTagClick !== undefined ? () => onTagClick(tag) : undefined}
onContextMenu={showContextMenu !== undefined ? (e) => showContextMenu(e, tag) : undefined}
/>
);
});
Expand Down
29 changes: 26 additions & 3 deletions src/frontend/containers/AppToolbar/FileTagEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,29 @@ const TagEditor = () => {
inputRef.current?.focus();
});

const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Backspace') {
// Prevent backspace from navigating back to main view when having an image open
e.stopPropagation();
}

if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
// If shift key is pressed with arrow keys left/right,
// stop those key events from propagating to the gallery,
// so that the cursor in the text input can be moved without selecting the prev/next image
// Kind of an ugly work-around, but better than not being able to move the cursor at all
if (e.shiftKey) {
e.stopPropagation(); // move text cursor as expected (and select text because shift is pressed)
} else {
e.preventDefault(); // don't do anything here: let the event propagate to the gallery
}
}
handleGridFocus(e);
},
[handleGridFocus],
);

return (
<div
ref={panelRef}
Expand All @@ -126,7 +149,7 @@ const TagEditor = () => {
value={inputText}
aria-autocomplete="list"
onChange={handleInput}
onKeyDown={handleGridFocus}
onKeyDown={handleKeyDown}
className="input"
aria-controls={POPUP_ID}
aria-activedescendant={activeDescendant}
Expand Down Expand Up @@ -279,7 +302,7 @@ const FloatingPanel = observer(({ children }: { children: ReactNode }) => {
}
}).current;

const handleClose = useRef((e: React.KeyboardEvent) => {
const handleKeyDown = useRef((e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
e.stopPropagation();
uiStore.closeToolbarTagPopover();
Expand All @@ -294,7 +317,7 @@ const FloatingPanel = observer(({ children }: { children: ReactNode }) => {
data-open={uiStore.isToolbarTagPopoverOpen}
className="floating-dialog"
onBlur={handleBlur}
onKeyDown={handleClose}
onKeyDown={handleKeyDown}
>
{uiStore.isToolbarTagPopoverOpen ? children : null}
</div>
Expand Down
Loading

0 comments on commit f6f08fa

Please sign in to comment.