Skip to content

Commit

Permalink
Merge pull request #582 from sickelap/improvements
Browse files Browse the repository at this point in the history
Improvements
  • Loading branch information
derneuere authored Dec 10, 2024
2 parents cb21c8f + f4928f6 commit d7a506e
Show file tree
Hide file tree
Showing 25 changed files with 89 additions and 110 deletions.
2 changes: 0 additions & 2 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
- medium: fix public user list and public photos for authenticated user
- medium: highlight sidebar menu when children menu page is active
- medium: remove the use of useMediaquery
- medium: fix lightbox editor caption
- medium: Fix Modal Person Edit button and hover
- low: fix photo list dropdown filter click area
- low: rework mobile top menu for usability (upload, search, etc.)
- low: fix warnings (in console) related to defaultProps (photo list view, etc.)
- low: select language and "help translating misaligned"
Expand Down
4 changes: 2 additions & 2 deletions src/api_client/albums/people.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const PersonResponseSchema = z.object({
name: z.string(),
face_url: z.string().nullable(),
face_count: z.number(),
face_photo_url: z.string().nullable(),
face_photo_url: z.string(),
video: z.boolean().optional(),
id: z.number(),
newPersonName: z.string().optional(),
Expand Down Expand Up @@ -67,7 +67,7 @@ export const peopleAlbumsApi = api
method: "PATCH",
body: { newPersonName },
}),
transformResponse: (response, meta, query) => {
transformResponse: (_response, _meta, query) => {
notification.renamePerson(query.personName, query.newPersonName);
},
}),
Expand Down
15 changes: 8 additions & 7 deletions src/api_client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
CompletePersonFaceList,
DeleteFacesRequest,
DeleteFacesResponse,
FacesTab,
IncompletePersonFaceListRequest,
IncompletePersonFaceListResponse,
PersonFaceList,
Expand Down Expand Up @@ -171,7 +172,7 @@ export const api = createApi({
[Endpoints.fetchUserSelfDetails]: builder.query<IUser, string>({
query: userId => `/user/${userId}/`,
transformResponse: (response: string) => UserSchema.parse(response),
providesTags: (result, error, id) => [{ type: "UserSelfDetails" as const, id }],
providesTags: (_result, _error, id) => [{ type: "UserSelfDetails" as const, id }],
}),
[Endpoints.fetchUserList]: builder.query<UserList, void>({
query: () => ({
Expand Down Expand Up @@ -232,8 +233,7 @@ export const api = createApi({
});
return newFacesList;
},
providesTags: (result, error, { inferred, method, orderBy }) =>
result ? result.map(({ id }) => ({ type: "Faces", id })) : ["Faces"],
providesTags: result => (result ? result.map(({ id }) => ({ type: "Faces", id })) : ["Faces"]),
}),
[Endpoints.fetchFaces]: builder.query<PersonFaceList, PersonFaceListRequest>({
query: ({ person, page = 0, inferred = false, orderBy = "confidence", method, minConfidence }) => ({
Expand Down Expand Up @@ -271,7 +271,7 @@ export const api = createApi({
)
);
},
providesTags: (result, error, { person }) => [{ type: "Faces", id: person }],
providesTags: (_result, _error, { person }) => [{ type: "Faces", id: person }],
}),
[Endpoints.deleteFaces]: builder.mutation<DeleteFacesResponse, DeleteFacesRequest>({
query: ({ faceIds }) => ({
Expand All @@ -285,17 +285,18 @@ export const api = createApi({
},
async onQueryStarted({ faceIds }, { dispatch, queryFulfilled, getState }) {
const { activeTab, analysisMethod, orderBy } = getState().face;
const incompleteFacesArgs = { inferred: activeTab !== "labeled", method: analysisMethod, orderBy: orderBy };
const incompleteFacesArgs = { inferred: activeTab !== FacesTab.enum.labeled, method: analysisMethod, orderBy };

const patchIncompleteFaces = dispatch(
api.util.updateQueryData(Endpoints.incompleteFaces, incompleteFacesArgs, draft => {
/* draft is wrapper with immer */
/* eslint-disable no-param-reassign */
draft.forEach(personGroup => {
personGroup.faces = personGroup.faces.filter(face => !faceIds.includes(face.id));
});
draft.forEach(personGroup => {
personGroup.face_count = personGroup.faces.length;
});

draft = draft.filter(personGroup => personGroup.faces.length > 0);
})
);
Expand All @@ -315,7 +316,7 @@ export const api = createApi({
}),
transformResponse: response => {
const payload = SetFacesLabelResponse.parse(response);
notification.addFacesToPerson(payload.results[0].person_name, payload.results.length);
notification.addFacesToPerson(payload.results[0].person_name ?? "unknown", payload.results.length);
return payload;
},
// To-Do: Handle optimistic updates by updating the cache. The issue is that there are multiple caches that need to be updated, where we need to remove the faces from the incomplete faces cache and add them to the labeled faces cache.
Expand Down
2 changes: 0 additions & 2 deletions src/components/CustomSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Autocomplete, Avatar, Group, Text } from "@mantine/core";
// import type { AutocompleteItem } from "@mantine/core";
import { useInterval, useViewportSize } from "@mantine/hooks";
import {
IconAlbum as Album,
Expand Down Expand Up @@ -63,7 +62,6 @@ function toPeopleSuggestion(item: Person) {

const SearchSuggestionItem = forwardRef<HTMLDivElement, SearchSuggestion>(
({ icon = <Search />, value, ...rest }: SearchSuggestion, ref) => (
/* eslint-disable react/jsx-props-no-spreading */
<div ref={ref} {...rest}>
<Group wrap="nowrap">
{cloneElement(icon as React.ReactElement, { size: 20 })}
Expand Down
13 changes: 13 additions & 0 deletions src/components/SiteSearch.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.clearSearch {
cursor: pointer;
width: 1rem;
height: 1rem;
}

.person {
transition: transform 0.3s;

&:hover {
transform: scale(1.3);
}
}
12 changes: 6 additions & 6 deletions src/components/SiteSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActionIcon, Combobox, Group, Image, InputBase, Loader, Popover, Text, useCombobox } from "@mantine/core";
import { ActionIcon, Box, Combobox, Group, Image, InputBase, Loader, Popover, Text, useCombobox } from "@mantine/core";
import { useViewportSize } from "@mantine/hooks";
import { IconAlbum, IconMap, IconSearch, IconTag, IconUser, IconX } from "@tabler/icons-react";
import React from "react";
Expand Down Expand Up @@ -101,10 +101,10 @@ export function SiteSearch() {
</Combobox.Option>
))
.concat([
<Combobox.Empty className={classes.people}>
<Combobox.Empty className={classes.people} key="people">
<Group wrap="nowrap">
{searchOptionsPeople.map(person => (
<Popover withArrow opened={showPersonNameToBeSelected(person.value)}>
<Popover key={person.value} withArrow opened={showPersonNameToBeSelected(person.value)}>
<Popover.Target>
<ActionIcon
className={classes.person}
Expand All @@ -128,7 +128,7 @@ export function SiteSearch() {
]);

return (
<div style={{ width: searchWidth }}>
<Box style={{ width: searchWidth }}>
<Combobox store={combobox} withinPortal onOptionSubmit={option => onOptionSubmit(option)}>
<Combobox.Target>
<InputBase
Expand Down Expand Up @@ -157,10 +157,10 @@ export function SiteSearch() {

<Combobox.Dropdown>
<Combobox.Options>
{isLoading ? <Combobox.Empty>{t("loading")}</Combobox.Empty> : searchOptions}
{isLoading ? <Combobox.Empty key="loader">{t("loading")}</Combobox.Empty> : searchOptions}
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
</div>
</Box>
);
}
12 changes: 4 additions & 8 deletions src/components/album/ModalAlbumEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button, Divider, Group, Modal, Stack, Text, TextInput, Title, UnstyledButton } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
import { DateTime } from "luxon";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";

import {
Expand All @@ -27,11 +27,6 @@ export function ModalAlbumEdit(props: Props) {
const { data: albumsUserList = [] } = useFetchUserAlbumsQuery();
const [createUserAlbum] = useCreateUserAlbumMutation();
const [addPhotoToUserAlbum] = useAddPhotoToUserAlbumMutation();
const [filteredUserAlbumList, setFilteredUserAlbumList] = useState(albumsUserList);

useEffect(() => {
setFilteredUserAlbumList(albumsUserList.filter(el => fuzzyMatch(newAlbumTitle, el.title)));
}, [newAlbumTitle, albumsUserList]);

return (
<Modal
Expand Down Expand Up @@ -90,8 +85,9 @@ export function ModalAlbumEdit(props: Props) {
</Group>
<Divider />
<Stack style={{ height: matches ? "50vh" : "25vh", overflowY: "scroll" }}>
{filteredUserAlbumList.length > 0 &&
filteredUserAlbumList.map(item => (
{albumsUserList
.filter(el => fuzzyMatch(newAlbumTitle, el.title))
.map(item => (
<UnstyledButton
key={`ub-${item.id}`}
onClick={() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/facedashboard/FaceTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export function FaceTooltip({ tooltipOpened, probability, timestamp, children =
? t("settings.confidencepercentage", { percentage: (probability * 100).toFixed(1) })
: null;

const dateTimeLabel = DateTime.fromISO(timestamp ? timestamp : "undefined").isValid
? DateTime.fromISO(timestamp ? timestamp : "undefined")
const dateTimeLabel = DateTime.fromISO(timestamp || "undefined").isValid
? DateTime.fromISO(timestamp || "undefined")
.setLocale(i18nResolvedLanguage())
.toLocaleString(DateTime.DATETIME_MED)
: null;
Expand Down
39 changes: 13 additions & 26 deletions src/components/lightbox/PersonDetailComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { ActionIcon, Avatar, Button, Group, Indicator, Text, Tooltip } from "@mantine/core";
import {
IconEdit as Edit,
IconTrash as Trash,
IconUserCheck as UserCheck,
IconUserOff as UserOff,
} from "@tabler/icons-react";
import { IconEdit, IconTrash, IconUserCheck, IconUserOff } from "@tabler/icons-react";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { push } from "redux-first-history";
Expand All @@ -16,36 +11,30 @@ import { useAppDispatch } from "../../store/store";
import { calculateProbabiltyColor } from "../facedashboard/FaceComponent";
import { FaceTooltip } from "../facedashboard/FaceTooltip";

type PersonDetailProps = {
type Props = {
person: any;
isPublic: boolean;
setFaceLocation: (face: any) => void;
onPersonEdit: (faceId: string, faceUrl: string) => void;
notThisPerson: (faceId: string) => void;
notThisPerson: (faceId: number) => void;
};

export const PersonDetail: React.FC<PersonDetailProps> = ({
person,
isPublic,
setFaceLocation,
onPersonEdit,
notThisPerson,
}) => {
export function PersonDetail({ person, isPublic, setFaceLocation, onPersonEdit, notThisPerson }: Props) {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const [tooltipOpened, setTooltipOpened] = useState(false);

return (
<Group
position="center"
spacing="xs"
align="center"
gap="xs"
key={person.name}
onMouseEnter={() => setFaceLocation(person.location)}
onMouseLeave={() => setFaceLocation(null)}
>
<Button
variant="subtle"
leftIcon={
leftSection={
<FaceTooltip tooltipOpened={tooltipOpened} probability={person.probability}>
<Indicator
color={calculateProbabiltyColor(person.probability)}
Expand All @@ -59,9 +48,7 @@ export const PersonDetail: React.FC<PersonDetailProps> = ({
}
onClick={() => !isPublic && dispatch(push(`/search/${person.name}`))}
>
<Text align="center" size="sm">
{person.name}
</Text>
<Text size="sm">{person.name}</Text>
</Button>
{!isPublic && person.type !== "user" && (
<Tooltip label={t("facesdashboard.explanationadding")}>
Expand All @@ -74,21 +61,21 @@ export const PersonDetail: React.FC<PersonDetailProps> = ({
variant="light"
color="green"
>
<UserCheck />
<IconUserCheck />
</ActionIcon>
</Tooltip>
)}
{!isPublic && (
<Tooltip label={t("facesdashboard.explanationadding")}>
<ActionIcon onClick={() => onPersonEdit(person.face_id, person.face_url)} variant="light">
<Edit />
<IconEdit />
</ActionIcon>
</Tooltip>
)}
{!isPublic && (
<Tooltip label={t("facesdashboard.notthisperson")}>
<ActionIcon variant="light" color="orange" onClick={() => notThisPerson(person.face_id)}>
<UserOff />
<IconUserOff />
</ActionIcon>
</Tooltip>
)}
Expand All @@ -102,10 +89,10 @@ export const PersonDetail: React.FC<PersonDetailProps> = ({
notification.deleteFaces(1);
}}
>
<Trash />
<IconTrash />
</ActionIcon>
</Tooltip>
)}
</Group>
);
};
}
6 changes: 3 additions & 3 deletions src/components/lightbox/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ export function Sidebar(props: Props) {
const dispatch = useAppDispatch();
const [personEditOpen, setPersonEditOpen] = useState(false);
const [selectedFaces, setSelectedFaces] = useState<any[]>([]);
const { isPublic, closeSidepanel, setFaceLocation } = props;
const { isPublic, closeSidepanel, setFaceLocation, id } = props;

const photoDetail: PhotoType = useAppSelector(store => store.photoDetails.photoDetails[props.id]);
const photoDetail: PhotoType = useAppSelector(store => store.photoDetails.photoDetails[id]);
const theme = useMantineTheme();
const { colorScheme } = useMantineColorScheme();

const notThisPerson = faceId => {
const notThisPerson = (faceId: number) => {
const ids = [faceId];
dispatch(api.endpoints.setFacesPersonLabel.initiate({ faceIds: ids, personName: "Unknown - Other" }));
notification.removeFacesFromPerson(ids.length);
Expand Down
2 changes: 1 addition & 1 deletion src/components/lightbox/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function Toolbar(props: Props) {
}
}
return (
<ActionIcon className={classes.icon} onClick={() => togglePlay()} variant="transparent">
<ActionIcon onClick={() => togglePlay()} variant="transparent">
{playerLoading && <Loader color="grey" />}
{!playerLoading && playerPlaying ? <PlayerPause /> : <PlayerPlay />}
</ActionIcon>
Expand Down
5 changes: 0 additions & 5 deletions src/components/modals/ModalUserEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,31 +256,27 @@ export function ModalUserEdit(props: Props) {
leftSection={<User />}
placeholder={t("login.usernameplaceholder")}
name="username"
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...form.getInputProps("username")}
/>
<TextInput
label={t("settings.email")}
leftSection={<Mail />}
placeholder={t("settings.emailplaceholder")}
name="email"
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...form.getInputProps("email")}
/>
<TextInput
label={t("settings.firstname")}
leftSection={<User />}
placeholder={t("settings.firstnameplaceholder")}
name="first_name"
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...form.getInputProps("first_name")}
/>
<TextInput
label={t("settings.lastname")}
leftSection={<User />}
placeholder={t("settings.lastnameplaceholder")}
name="last_name"
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...form.getInputProps("last_name")}
/>
</SimpleGrid>
Expand All @@ -304,7 +300,6 @@ export function ModalUserEdit(props: Props) {
required={firstTimeSetup}
placeholder={scanDirectoryPlaceholder}
name="scan_directory"
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...form.getInputProps("scan_directory")}
/>
</Grid.Col>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ GroupHeader.propTypes = {
location: PropTypes.string,
date: PropTypes.string,
}).isRequired,
activeTileUrl: PropTypes.string.isRequired,
activeTileUrl: PropTypes.string,
};

export default GroupHeader;
2 changes: 1 addition & 1 deletion src/components/react-pig/components/Tile/Tile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ Tile.propTypes = {
containerWidth: PropTypes.number.isRequired,
containerOffsetTop: PropTypes.number.isRequired,
getUrl: PropTypes.func.isRequired,
activeTileUrl: PropTypes.string.isRequired,
activeTileUrl: PropTypes.string,
handleClick: PropTypes.func.isRequired,
handleSelection: PropTypes.func.isRequired,
selected: PropTypes.bool.isRequired,
Expand Down
Loading

0 comments on commit d7a506e

Please sign in to comment.