Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements #582

Merged
merged 16 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading