From 9bd0960eccb601d6ac48f5f427840c2d87cafdf1 Mon Sep 17 00:00:00 2001 From: Oleksandr Holub Date: Thu, 30 Nov 2023 20:24:31 -0600 Subject: [PATCH] added updating of link --- .../ActionButtons/DeleteLinkActionButton.tsx | 20 ++++--- .../ActionButtons/LikeLinkActionButton.tsx | 8 +-- .../ActionButtons/SaveLinkActionButton.tsx | 8 +-- .../LinkListItem}/AddTagButton.tsx | 4 +- .../Contents/YoutubeVideoContent.tsx | 2 +- .../components/LinkListItem/LinkListItem.tsx | 52 +++++++++++++++---- .../LinkListItem/LinkListItemActionPanel.tsx | 52 +++++++++++++++++++ .../{components => }/LinkListItemAuthor.tsx | 2 +- .../{components => }/LinkListItemContent.tsx | 4 +- .../LinkListItem/LinkListItemTags.tsx | 39 ++++++++++++++ .../LinkListItem/LinkListItemTitle.tsx | 47 +++++++++++++++++ .../{ => LinkListItem}/TagSearchInput.tsx | 10 ++-- .../components/LinkListItemActionButtons.tsx | 22 -------- .../components/LinkListItemTags.tsx | 18 ------- .../components/LinkListItemTitle.tsx | 12 ----- .../LinkListToolbar/LinkListToolbar.tsx | 6 ++- .../LinkListToolbar/components/TagSearch.tsx | 9 ---- .../AddLink/components/AddLinkForm.tsx | 19 ++----- .../screens/AddLink/components/TitleInput.tsx | 47 ----------------- client/src/stores/LinkStore.ts | 42 +++++++++++++-- client/src/utils/errors.ts | 1 - .../UpdateLinkHandler/UpdateLinkHandler.cs | 4 +- 22 files changed, 260 insertions(+), 168 deletions(-) rename client/src/components/LinkListItem/{components => }/ActionButtons/DeleteLinkActionButton.tsx (67%) rename client/src/components/LinkListItem/{components => }/ActionButtons/LikeLinkActionButton.tsx (89%) rename client/src/components/LinkListItem/{components => }/ActionButtons/SaveLinkActionButton.tsx (83%) rename client/src/{screens/AddLink/components => components/LinkListItem}/AddTagButton.tsx (90%) rename client/src/components/LinkListItem/{components => }/Contents/YoutubeVideoContent.tsx (85%) create mode 100644 client/src/components/LinkListItem/LinkListItemActionPanel.tsx rename client/src/components/LinkListItem/{components => }/LinkListItemAuthor.tsx (88%) rename client/src/components/LinkListItem/{components => }/LinkListItemContent.tsx (76%) create mode 100644 client/src/components/LinkListItem/LinkListItemTags.tsx create mode 100644 client/src/components/LinkListItem/LinkListItemTitle.tsx rename client/src/components/{ => LinkListItem}/TagSearchInput.tsx (93%) delete mode 100644 client/src/components/LinkListItem/components/LinkListItemActionButtons.tsx delete mode 100644 client/src/components/LinkListItem/components/LinkListItemTags.tsx delete mode 100644 client/src/components/LinkListItem/components/LinkListItemTitle.tsx delete mode 100644 client/src/components/LinkListToolbar/components/TagSearch.tsx delete mode 100644 client/src/screens/AddLink/components/TitleInput.tsx diff --git a/client/src/components/LinkListItem/components/ActionButtons/DeleteLinkActionButton.tsx b/client/src/components/LinkListItem/ActionButtons/DeleteLinkActionButton.tsx similarity index 67% rename from client/src/components/LinkListItem/components/ActionButtons/DeleteLinkActionButton.tsx rename to client/src/components/LinkListItem/ActionButtons/DeleteLinkActionButton.tsx index 1c77ad0..1fe066c 100644 --- a/client/src/components/LinkListItem/components/ActionButtons/DeleteLinkActionButton.tsx +++ b/client/src/components/LinkListItem/ActionButtons/DeleteLinkActionButton.tsx @@ -1,9 +1,9 @@ -import Link from '../../../../models/Link.ts'; -import {useLinkStore} from '../../../../contexts/AppContext.tsx'; -import useAsyncAction from '../../../../hooks/useAsyncAction.ts'; -import Button from '../../../Button.tsx'; +import Link from '../../../models/Link.ts'; +import {useLinkStore} from '../../../contexts/AppContext.tsx'; +import useAsyncAction from '../../../hooks/useAsyncAction.ts'; +import Button from '../../Button.tsx'; import {useState} from 'react'; -import Modal from '../../../Modal.tsx'; +import Modal from '../../Modal.tsx'; const DeleteLinkActionButton = ({ id }: Pick) => { const { deleteLink } = useLinkStore(); @@ -16,12 +16,10 @@ const DeleteLinkActionButton = ({ id }: Pick) => { return ( <>
-
{ showConfirmModal && ( diff --git a/client/src/components/LinkListItem/components/ActionButtons/LikeLinkActionButton.tsx b/client/src/components/LinkListItem/ActionButtons/LikeLinkActionButton.tsx similarity index 89% rename from client/src/components/LinkListItem/components/ActionButtons/LikeLinkActionButton.tsx rename to client/src/components/LinkListItem/ActionButtons/LikeLinkActionButton.tsx index 148a84d..a1f77d0 100644 --- a/client/src/components/LinkListItem/components/ActionButtons/LikeLinkActionButton.tsx +++ b/client/src/components/LinkListItem/ActionButtons/LikeLinkActionButton.tsx @@ -1,7 +1,7 @@ -import Link from '../../../../models/Link.ts'; -import {useLinkStore} from '../../../../contexts/AppContext.tsx'; -import useAsyncAction from '../../../../hooks/useAsyncAction.ts'; -import Button from '../../../Button.tsx'; +import Link from '../../../models/Link.ts'; +import {useLinkStore} from '../../../contexts/AppContext.tsx'; +import useAsyncAction from '../../../hooks/useAsyncAction.ts'; +import Button from '../../Button.tsx'; const shortLikes = (likes: number) => { if (likes < 1000) { diff --git a/client/src/components/LinkListItem/components/ActionButtons/SaveLinkActionButton.tsx b/client/src/components/LinkListItem/ActionButtons/SaveLinkActionButton.tsx similarity index 83% rename from client/src/components/LinkListItem/components/ActionButtons/SaveLinkActionButton.tsx rename to client/src/components/LinkListItem/ActionButtons/SaveLinkActionButton.tsx index 6dbfc3c..b3515ee 100644 --- a/client/src/components/LinkListItem/components/ActionButtons/SaveLinkActionButton.tsx +++ b/client/src/components/LinkListItem/ActionButtons/SaveLinkActionButton.tsx @@ -1,7 +1,7 @@ -import Link from '../../../../models/Link.ts'; -import {useLinkStore} from '../../../../contexts/AppContext.tsx'; -import useAsyncAction from '../../../../hooks/useAsyncAction.ts'; -import Button from '../../../Button.tsx'; +import Link from '../../../models/Link.ts'; +import {useLinkStore} from '../../../contexts/AppContext.tsx'; +import useAsyncAction from '../../../hooks/useAsyncAction.ts'; +import Button from '../../Button.tsx'; const SaveLinkActionButton = ({ isSaved, id }: Pick) => { diff --git a/client/src/screens/AddLink/components/AddTagButton.tsx b/client/src/components/LinkListItem/AddTagButton.tsx similarity index 90% rename from client/src/screens/AddLink/components/AddTagButton.tsx rename to client/src/components/LinkListItem/AddTagButton.tsx index 195ed59..d165f32 100644 --- a/client/src/screens/AddLink/components/AddTagButton.tsx +++ b/client/src/components/LinkListItem/AddTagButton.tsx @@ -1,6 +1,6 @@ import {useState} from 'react'; -import TagSearchInput from '../../../components/TagSearchInput.tsx'; -import Button from '../../../components/Button.tsx'; +import TagSearchInput from './TagSearchInput.tsx'; +import Button from '../Button.tsx'; type TagInputProps = { onAdd: (tag: string) => void; diff --git a/client/src/components/LinkListItem/components/Contents/YoutubeVideoContent.tsx b/client/src/components/LinkListItem/Contents/YoutubeVideoContent.tsx similarity index 85% rename from client/src/components/LinkListItem/components/Contents/YoutubeVideoContent.tsx rename to client/src/components/LinkListItem/Contents/YoutubeVideoContent.tsx index 98a34cb..d18ba80 100644 --- a/client/src/components/LinkListItem/components/Contents/YoutubeVideoContent.tsx +++ b/client/src/components/LinkListItem/Contents/YoutubeVideoContent.tsx @@ -1,4 +1,4 @@ -import {YoutubeVideo} from '../../../../models/Link.ts'; +import {YoutubeVideo} from '../../../models/Link.ts'; const YoutubeVideoContent = ({ videoId }: YoutubeVideo) => { return ( diff --git a/client/src/components/LinkListItem/LinkListItem.tsx b/client/src/components/LinkListItem/LinkListItem.tsx index 6e45e3f..36e625c 100644 --- a/client/src/components/LinkListItem/LinkListItem.tsx +++ b/client/src/components/LinkListItem/LinkListItem.tsx @@ -1,20 +1,54 @@ - -import LinkListItemContent from './components/LinkListItemContent.tsx'; -import LinkListItemTitle from './components/LinkListItemTitle.tsx'; -import LinkListItemAuthor from './components/LinkListItemAuthor.tsx'; -import LinkListItemTags from './components/LinkListItemTags.tsx'; -import LinkListItemActionButtons from './components/LinkListItemActionButtons.tsx'; +import LinkListItemContent from './LinkListItemContent.tsx'; +import LinkListItemTitle from './LinkListItemTitle.tsx'; +import LinkListItemAuthor from './LinkListItemAuthor.tsx'; +import LinkListItemTags from './LinkListItemTags.tsx'; import Link from '../../models/Link.ts'; +import {useState} from 'react'; +import {useLinkStore} from '../../contexts/AppContext.tsx'; +import LinkListItemActionPanel from './LinkListItemActionPanel.tsx'; +import useAsyncAction from '../../hooks/useAsyncAction.ts'; const LinkListItem = ({ link }: { link: Link }) => { + const [updating, setUpdating] = useState(false); + const { updateLink } = useLinkStore(); + const [updateState, setUpdateState] = useState>({ title: link.title, tags: link.tags }); + const {loading, execute} = useAsyncAction(() => updateLink(link.id, updateState)); + + const handleOnEditClick = () => { + setUpdateState({ title: link.title, tags: link.tags }); + setUpdating(true); + }; + + const handleOnApplyClick = async () => { + await execute(); + setUpdating(false); + }; + + const handleOnRemoveTag = (tag: string) => { + setUpdateState((prevState) => ({ ...prevState, tags: prevState.tags.filter((t) => t !== tag) })); + }; + + const handleOnAddTag = (tag: string) => { + setUpdateState((prevState) => ({ ...prevState, tags: [...prevState.tags, tag] })); + }; + + const handleOnTitleUpdate = (title: string) => { + setUpdateState((prevState) => ({ ...prevState, title })); + } + return ( <>
- + setUpdating(false)} + onEditClick={handleOnEditClick} />
- - + + ); diff --git a/client/src/components/LinkListItem/LinkListItemActionPanel.tsx b/client/src/components/LinkListItem/LinkListItemActionPanel.tsx new file mode 100644 index 0000000..20207ca --- /dev/null +++ b/client/src/components/LinkListItem/LinkListItemActionPanel.tsx @@ -0,0 +1,52 @@ +import LikeLinkActionButton from './ActionButtons/LikeLinkActionButton.tsx'; +import SaveLinkActionButton from './ActionButtons/SaveLinkActionButton.tsx'; +import DeleteLinkActionButton from './ActionButtons/DeleteLinkActionButton.tsx'; +import Link from '../../models/Link.ts'; +import Button from '../Button.tsx'; + +type LinkListItemActionPanelProps = { + disabled?: boolean; + onEditClick: () => void; + onCancelClick: () => void; + onApplyClick: () => void; + link: Link; + updating: boolean; +}; + +const LinkListItemActionPanel = ({ link, disabled, updating, onApplyClick, onCancelClick, onEditClick}: LinkListItemActionPanelProps) => { + return ( +
+
+ + +
+ { !updating && link.editable && ( +
+ + +
+ )} + { updating && ( +
+ + +
+ )} +
+ ); +}; + +export default LinkListItemActionPanel; \ No newline at end of file diff --git a/client/src/components/LinkListItem/components/LinkListItemAuthor.tsx b/client/src/components/LinkListItem/LinkListItemAuthor.tsx similarity index 88% rename from client/src/components/LinkListItem/components/LinkListItemAuthor.tsx rename to client/src/components/LinkListItem/LinkListItemAuthor.tsx index d69e8ac..bdead66 100644 --- a/client/src/components/LinkListItem/components/LinkListItemAuthor.tsx +++ b/client/src/components/LinkListItem/LinkListItemAuthor.tsx @@ -1,4 +1,4 @@ -import Link from '../../../models/Link.ts'; +import Link from '../../models/Link.ts'; const LinkListItemAuthor = ({ user, createdAt }: Pick) => (

diff --git a/client/src/components/LinkListItem/components/LinkListItemContent.tsx b/client/src/components/LinkListItem/LinkListItemContent.tsx similarity index 76% rename from client/src/components/LinkListItem/components/LinkListItemContent.tsx rename to client/src/components/LinkListItem/LinkListItemContent.tsx index 3828809..6cfbf69 100644 --- a/client/src/components/LinkListItem/components/LinkListItemContent.tsx +++ b/client/src/components/LinkListItem/LinkListItemContent.tsx @@ -1,6 +1,6 @@ import YoutubeVideoContent from './Contents/YoutubeVideoContent.tsx'; -import Link from '../../../models/Link.ts'; -import LinkType from '../../../models/LinkType.ts'; +import Link from '../../models/Link.ts'; +import LinkType from '../../models/LinkType.ts'; const LinkListItemContent = ({ type, youtube }: Pick) => { if (type === LinkType.Youtube) { diff --git a/client/src/components/LinkListItem/LinkListItemTags.tsx b/client/src/components/LinkListItem/LinkListItemTags.tsx new file mode 100644 index 0000000..a1498be --- /dev/null +++ b/client/src/components/LinkListItem/LinkListItemTags.tsx @@ -0,0 +1,39 @@ +import {useStore} from '../../contexts/AppContext.tsx'; +import LinkStore from '../../stores/LinkStore.ts'; +import {observer} from 'mobx-react-lite'; +import TagBadge from '../TagBadge.tsx'; +import {MaxTags} from '../../constants/preferences.ts'; +import AddTagButton from './AddTagButton.tsx'; + +type LinkListItemTagsProps = { + tags: string[]; + editable?: boolean; + error?: string; + onAdd?: (tag: string) => void; + onRemove?: (tag: string) => void; +}; + +const LinkListItemTags = observer(({ tags, editable, error, onAdd, onRemove }: LinkListItemTagsProps) => { + const { toggleTagFilter } = useStore(LinkStore); + const handleOnClick = (tag: string) => { + if (!editable) { + toggleTagFilter(tag); + } else if (onRemove) { + onRemove(tag); + } + }; + + return ( + <> +

+ {tags.map((tag) => ( + handleOnClick(tag)} name={tag} removable={editable} active /> + ))} + { editable && tags.length < MaxTags && onAdd && onAdd(tag)} />} +
+ { editable && error && ({error}) } + + ); +}); + +export default LinkListItemTags; \ No newline at end of file diff --git a/client/src/components/LinkListItem/LinkListItemTitle.tsx b/client/src/components/LinkListItem/LinkListItemTitle.tsx new file mode 100644 index 0000000..e03274b --- /dev/null +++ b/client/src/components/LinkListItem/LinkListItemTitle.tsx @@ -0,0 +1,47 @@ +import { ChangeEvent, useState } from 'react'; +import {MaxTitleLength} from '../../constants/preferences.ts'; + +type LinkListItemTitleProps = { + editable?: boolean; + title: string; + onUpdate?: (title: string) => void; + error?: string; +}; + +const LinkListItemTitle = ({ title, onUpdate, editable, error }: LinkListItemTitleProps) => { + const [value, setValue] = useState(title); + + const handleInputChange = (event: ChangeEvent) => { + setValue(event.target.value); + }; + + const handleEditEnd = () => { + if (value !== title && onUpdate) { + onUpdate(value); + } + }; + + return ( + <> + { editable && ( + + )} + { !editable && ( +
+ { value.length > MaxTitleLength ? `${value.slice(0, MaxTitleLength)}` : value } +
+ )} + { error && ({error}) } + + ); +} + +export default LinkListItemTitle; \ No newline at end of file diff --git a/client/src/components/TagSearchInput.tsx b/client/src/components/LinkListItem/TagSearchInput.tsx similarity index 93% rename from client/src/components/TagSearchInput.tsx rename to client/src/components/LinkListItem/TagSearchInput.tsx index 6f40632..6b1ecd5 100644 --- a/client/src/components/TagSearchInput.tsx +++ b/client/src/components/LinkListItem/TagSearchInput.tsx @@ -1,10 +1,10 @@ import React, {useEffect, useState} from 'react'; -import useClickOutsideHandler from '../hooks/useClickOutsideHandler.ts'; +import useClickOutsideHandler from '../../hooks/useClickOutsideHandler.ts'; import {observer} from 'mobx-react-lite'; -import {useLinkStore} from '../contexts/AppContext.tsx'; -import TagBadge from '../components/TagBadge.tsx'; -import SearchIcon from '../components/SearchIcon.tsx'; -import Button from './Button.tsx'; +import {useLinkStore} from '../../contexts/AppContext.tsx'; +import TagBadge from '../TagBadge.tsx'; +import SearchIcon from '../SearchIcon.tsx'; +import Button from '../Button.tsx'; type TagSearchProps = { showSuggestionsInitial?: boolean; diff --git a/client/src/components/LinkListItem/components/LinkListItemActionButtons.tsx b/client/src/components/LinkListItem/components/LinkListItemActionButtons.tsx deleted file mode 100644 index 91a702e..0000000 --- a/client/src/components/LinkListItem/components/LinkListItemActionButtons.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import LikeLinkActionButton from './ActionButtons/LikeLinkActionButton.tsx'; -import SaveLinkActionButton from './ActionButtons/SaveLinkActionButton.tsx'; -import DeleteLinkActionButton from './ActionButtons/DeleteLinkActionButton.tsx'; -import Link from '../../../models/Link.ts'; - -const LinkListItemActionButtons = (link: Link) => { - return ( -
-
- - -
- { link.editable && ( -
- -
- )} -
- ); -}; - -export default LinkListItemActionButtons; \ No newline at end of file diff --git a/client/src/components/LinkListItem/components/LinkListItemTags.tsx b/client/src/components/LinkListItem/components/LinkListItemTags.tsx deleted file mode 100644 index 5907778..0000000 --- a/client/src/components/LinkListItem/components/LinkListItemTags.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import {useStore} from '../../../contexts/AppContext.tsx'; -import LinkStore from '../../../stores/LinkStore.ts'; -import {observer} from 'mobx-react-lite'; -import TagBadge from '../../TagBadge.tsx'; - -const LinkListItemTags = observer(({ tags }: { tags: string[]}) => { - const { toggleTagFilter } = useStore(LinkStore); - - return ( -
- {tags.map((tag) => ( - toggleTagFilter(tag)} active /> - ))} -
- ); -}); - -export default LinkListItemTags; \ No newline at end of file diff --git a/client/src/components/LinkListItem/components/LinkListItemTitle.tsx b/client/src/components/LinkListItem/components/LinkListItemTitle.tsx deleted file mode 100644 index 8a43aa6..0000000 --- a/client/src/components/LinkListItem/components/LinkListItemTitle.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import {MaxTitleLength} from '../../../constants/preferences.ts'; - -const LinkListItemTitle = ({ title }: { title: string}) => { - return ( -
- { title?.length > MaxTitleLength ? `${title?.slice(0, MaxTitleLength - 3)}...` : title } -
- ); -} - -export default LinkListItemTitle; \ No newline at end of file diff --git a/client/src/components/LinkListToolbar/LinkListToolbar.tsx b/client/src/components/LinkListToolbar/LinkListToolbar.tsx index 18ea23e..3c30eea 100644 --- a/client/src/components/LinkListToolbar/LinkListToolbar.tsx +++ b/client/src/components/LinkListToolbar/LinkListToolbar.tsx @@ -1,14 +1,16 @@ import TopTagsList from './components/TopTagsList.tsx'; import SearchByTitleInput from './components/SearchByTitleInput.tsx'; -import TagSearch from './components/TagSearch.tsx'; import UserInteractionsFilter from './components/UserInteractionsFilter.tsx'; +import {useLinkStore} from '../../contexts/AppContext.tsx'; +import TagSearchInput from '../LinkListItem/TagSearchInput.tsx'; const LinkListToolbar = () => { + const { toggleTagFilter } = useLinkStore(); return (
- +
diff --git a/client/src/components/LinkListToolbar/components/TagSearch.tsx b/client/src/components/LinkListToolbar/components/TagSearch.tsx deleted file mode 100644 index 1fa6c2d..0000000 --- a/client/src/components/LinkListToolbar/components/TagSearch.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import {useLinkStore} from '../../../contexts/AppContext.tsx'; -import TagSearchInput from '../../TagSearchInput.tsx'; - -const TagSearch = () => { - const { toggleTagFilter } = useLinkStore(); - return ; -}; - -export default TagSearch; \ No newline at end of file diff --git a/client/src/screens/AddLink/components/AddLinkForm.tsx b/client/src/screens/AddLink/components/AddLinkForm.tsx index d8ec3e9..f47fd08 100644 --- a/client/src/screens/AddLink/components/AddLinkForm.tsx +++ b/client/src/screens/AddLink/components/AddLinkForm.tsx @@ -1,14 +1,12 @@ import {useLinkStore} from '../../../contexts/AppContext.tsx'; -import TagBadge from '../../../components/TagBadge.tsx'; -import AddTagButton from './AddTagButton.tsx'; -import {MaxTags} from '../../../constants/preferences.ts'; -import TitleInput from './TitleInput.tsx'; import SubmitButton from '../../../components/SubmitButton.tsx'; import ErrorAlert from '../../../components/ErrorAlert.tsx'; import useSimpleReducer from '../../../hooks/useSimpleReducer.ts'; import {handleError} from '../../../utils/errors.ts'; import PreviewLink from '../../../models/PreviewLink.ts'; -import LinkListItemContent from '../../../components/LinkListItem/components/LinkListItemContent.tsx'; +import LinkListItemContent from '../../../components/LinkListItem/LinkListItemContent.tsx'; +import LinkListItemTitle from '../../../components/LinkListItem/LinkListItemTitle.tsx'; +import LinkListItemTags from '../../../components/LinkListItem/LinkListItemTags.tsx'; type LocalState = { isSubmitting: boolean; @@ -78,15 +76,8 @@ const AddLinkForm = ({ onSuccess, link }: AddLinkFormProps) => {
- - { state.titleError && ({state.titleError}) } -
- {link.tags.map((tag) => ( - removeTag(tag)} name={tag} removable active /> - ))} - { link.tags.length < MaxTags && } -
- { state.tagsError && ({state.tagsError}) } + +
Submit diff --git a/client/src/screens/AddLink/components/TitleInput.tsx b/client/src/screens/AddLink/components/TitleInput.tsx deleted file mode 100644 index 6c798da..0000000 --- a/client/src/screens/AddLink/components/TitleInput.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { ChangeEvent, useState } from 'react'; -import {MaxTitleLength} from '../../../constants/preferences.ts'; - -type TitleInputProps = { - initialTitle: string; - onUpdate: (title: string) => void; -}; - -const TitleInput = ({ initialTitle, onUpdate }: TitleInputProps) => { - const [isEditing, setIsEditing] = useState(false); - const [title, setTitle] = useState(initialTitle); - - const handleEditStart = () => { - setIsEditing(true); - }; - - const handleInputChange = (event: ChangeEvent) => { - setTitle(event.target.value); - }; - - const handleEditEnd = () => { - setIsEditing(false); - if (title !== initialTitle) { - onUpdate(title); - } - }; - - return isEditing || initialTitle.length === 0 ? ( - - ) : ( -
- { title.length > MaxTitleLength ? `${title.slice(0, MaxTitleLength)}...` : title } -
- ); -} - -export default TitleInput; \ No newline at end of file diff --git a/client/src/stores/LinkStore.ts b/client/src/stores/LinkStore.ts index 2aa0b90..d89ede6 100644 --- a/client/src/stores/LinkStore.ts +++ b/client/src/stores/LinkStore.ts @@ -91,9 +91,7 @@ class LinkStore { }; public loadMore = async () => { - console.log('loadMore') if (this.state.paginationState.hasNextPage) { - console.log('loadMore2') const response = await this.linkService.getList({ ...this.paginationParams, ...this.state.filter, @@ -212,7 +210,7 @@ class LinkStore { return {}; }; - public updatePreviewLink = (updates: Partial) => { + public updatePreviewLink = (updates: Partial>) => { if (!this.state.preview.link) { throw new Error('Preview Link not found'); } @@ -223,6 +221,44 @@ class LinkStore { }; }; + public updateLink = async (linkId: string, update: Pick) => { + await this.linkService.update({ linkId, ...update }); + + const linkIndex = this.state.links.findIndex(link => link.id === linkId); + if (linkIndex === -1) { + throw new Error('Link not found'); + } + + const link = this.state.links[linkIndex]; + const removedTags = link.tags.filter(tag => !update.tags.includes(tag)); + const addedTags = update.tags.filter(tag => !link.tags.includes(tag)); + const updatedTags = this.state.tags.map(tag => { + if (removedTags.includes(tag.name)) { + return { + ...tag, + count: tag.count - 1 + }; + } + + if (addedTags.includes(tag.name)) { + return { + ...tag, + count: tag.count + 1 + }; + } + + return tag; + }).filter(x => x.count > 0); + runInAction(() => { + this.state.links[linkIndex] = { + ...link, + ...update, + }; + this.state.tags = updatedTags; + }); + + } + public submitLink = async (): Promise => { if (!this.state.preview.link) { throw new Error('Preview Link not found'); diff --git a/client/src/utils/errors.ts b/client/src/utils/errors.ts index bce293d..1a78618 100644 --- a/client/src/utils/errors.ts +++ b/client/src/utils/errors.ts @@ -3,7 +3,6 @@ import FetchHttpResponseError from '../services/HttpClient/errors/FetchHttpRespo import FetchHttpResponseValidationError from '../services/HttpClient/errors/FetchHttpResponseValidationError.ts'; export const handleError = (error: unknown): string | undefined => { - console.log(error) if (error instanceof FetchHttpResponseBusinessError) { return error.message; } diff --git a/server/src/ShareLink.Application/UpdateLinkHandler/UpdateLinkHandler.cs b/server/src/ShareLink.Application/UpdateLinkHandler/UpdateLinkHandler.cs index 822291b..7d458d8 100644 --- a/server/src/ShareLink.Application/UpdateLinkHandler/UpdateLinkHandler.cs +++ b/server/src/ShareLink.Application/UpdateLinkHandler/UpdateLinkHandler.cs @@ -17,7 +17,9 @@ public async Task Handle(UpdateLinkRequest request, CancellationToken cancellati throw new UserUnauthorizedException(); } - var link = await context.Links.SingleOrDefaultAsync(x => x.Id == request.LinkId && x.UserId == userId, cancellationToken); + var link = await context.Links + .Include(x => x.Tags) + .SingleOrDefaultAsync(x => x.Id == request.LinkId && x.UserId == userId, cancellationToken); if (link is null) { throw new BusinessException(ErrorCodes.LinkNotFound);