feat: add timeline filter support to replay dispatcher and clean up unused dependencies
This commit is contained in:
+884
@@ -0,0 +1,884 @@
|
||||
|
||||
> fe_admin_history_web@2.2.3 lint
|
||||
> eslint .
|
||||
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/jsvectormap.d.ts
|
||||
2:24 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/(full-width-pages)/(auth)/layout.tsx
|
||||
1:8 warning 'GridShape' is defined but never used @typescript-eslint/no-unused-vars
|
||||
5:8 warning 'Image' is defined but never used @typescript-eslint/no-unused-vars
|
||||
6:8 warning 'Link' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/(full-width-pages)/(auth)/reset-password/page.tsx
|
||||
48:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/editor/[id]/page.tsx
|
||||
6:20 warning 'MapFeaturePayload' is defined but never used @typescript-eslint/no-unused-vars
|
||||
13:8 warning 'ReplayPreviewOverlay' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:8 warning 'ReplayPreviewLayerPanel' is defined but never used @typescript-eslint/no-unused-vars
|
||||
16:8 warning 'PublicWikiSidebar' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:8 warning 'PresentPlaceSearch' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:35 warning 'HistoricalGeometryFocusPayload' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:72 warning 'PresentPlaceSelection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
26:10 warning 'fetchWikiById' is defined but never used @typescript-eslint/no-unused-vars
|
||||
53:5 warning 'persistBackgroundLayerVisibility' is defined but never used @typescript-eslint/no-unused-vars
|
||||
89:10 warning 'fitMapToFeatureCollection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
119:6 warning 'PreviewLinkEntityPopupState' is defined but never used @typescript-eslint/no-unused-vars
|
||||
545:11 warning 'handleViewerPreviewTimelineYearChange' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
556:11 warning 'handleViewerPreviewTimelineFilterChange' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
583:37 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
616:23 warning 'replayPreviewActiveCursor' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
618:22 warning 'replayPreviewSidebarOpen' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
664:11 warning 'replayPreviewActiveEntity' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1199:57 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1203:62 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
2420:8 warning React Hook useCallback has a missing dependency: 'setSelectedFeatureIds'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
3047:10 warning 'extractWikiBlockquoteText' is defined but never used @typescript-eslint/no-unused-vars
|
||||
3083:10 warning 'computeFixedPopupPosition' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/user/account/applications/page.tsx
|
||||
42:9 warning 'config' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
62:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
101:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
170:25 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/user/account/page.tsx
|
||||
15:10 warning 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
31:6 warning React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/user/layout.tsx
|
||||
4:8 warning 'AppHeader' is defined but never used @typescript-eslint/no-unused-vars
|
||||
30:6 warning React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/user/projects/[id]/page.tsx
|
||||
10:8 warning 'PageBreadcrumb' is defined but never used @typescript-eslint/no-unused-vars
|
||||
31:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
36:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
51:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
60:6 warning React Hook useEffect has a missing dependency: 'fetchProject'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
68:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
107:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
121:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
129:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
138:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
168:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
204:24 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
402:74 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
427:25 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
|
||||
529:51 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/app/user/role-upgrade/page.tsx
|
||||
3:8 warning 'PageBreadcrumb' is defined but never used @typescript-eslint/no-unused-vars
|
||||
180:9 warning 'handleItemClick' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/auth/tokenStore.ts
|
||||
48:59 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
65:11 warning 'refresh' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/auth/SignInForm.tsx
|
||||
82:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/auth/SignUpForm.tsx
|
||||
75:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
109:13 warning 'signupRes' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/calendar/Calendar.tsx
|
||||
44:5 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/calendar/Calendar.tsx:44:5
|
||||
42 | useEffect(() => {
|
||||
43 | // Initialize with some events
|
||||
> 44 | setEvents([
|
||||
| ^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
45 | {
|
||||
46 | id: "1",
|
||||
47 | title: "Event Conf.", react-hooks/set-state-in-effect
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/ecommerce/StatisticsChart.tsx
|
||||
157:23 error `'` can be escaped with `'`, `‘`, `'`, `’` react/no-unescaped-entities
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/form/role-upgrade/Editor.tsx
|
||||
10:12 error Component definition is missing display name react/display-name
|
||||
10:67 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
10:88 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
36:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
82:6 warning React Hook useEffect has a missing dependency: 'onEditorReady'. Either include it or remove the dependency array. If 'onEditorReady' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/header/UserDropdown.tsx
|
||||
7:10 warning 'fullDataUser' is defined but never used @typescript-eslint/no-unused-vars
|
||||
14:9 warning 'router' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
18:10 warning 'loading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationDetailModal.tsx
|
||||
91:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx
|
||||
182:33 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx:182:33
|
||||
180 | onClick={() => onSort("status")}
|
||||
181 | >
|
||||
> 182 | Trạng thái <SortIcon column="status" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
183 | </div>
|
||||
184 | </TableCell>
|
||||
185 | <TableCell
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx:48:20
|
||||
46 | };
|
||||
47 |
|
||||
> 48 | const SortIcon = ({ column }: { column: AppSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 49 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 50 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 79 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 80 | };
|
||||
| ^^^^ The component is created during render here
|
||||
81 |
|
||||
82 | const getStatusBadge = (status: string | number) => {
|
||||
83 | const s = status?.toString(); react-hooks/static-components
|
||||
193:31 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx:193:31
|
||||
191 | onClick={() => onSort("created_at")}
|
||||
192 | >
|
||||
> 193 | Ngày nộp <SortIcon column="created_at" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
194 | </div>
|
||||
195 | </TableCell>
|
||||
196 | <TableCell
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx:48:20
|
||||
46 | };
|
||||
47 |
|
||||
> 48 | const SortIcon = ({ column }: { column: AppSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 49 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 50 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 79 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 80 | };
|
||||
| ^^^^ The component is created during render here
|
||||
81 |
|
||||
82 | const getStatusBadge = (status: string | number) => {
|
||||
83 | const s = status?.toString(); react-hooks/static-components
|
||||
204:31 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx:204:31
|
||||
202 | onClick={() => onSort("reviewed_at")}
|
||||
203 | >
|
||||
> 204 | Cập nhật <SortIcon column="reviewed_at" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
205 | </div>
|
||||
206 | </TableCell>
|
||||
207 | <TableCell
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/ApplicationTable.tsx:48:20
|
||||
46 | };
|
||||
47 |
|
||||
> 48 | const SortIcon = ({ column }: { column: AppSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 49 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 50 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 79 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 80 | };
|
||||
| ^^^^ The component is created during render here
|
||||
81 |
|
||||
82 | const getStatusBadge = (status: string | number) => {
|
||||
83 | const s = status?.toString(); react-hooks/static-components
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx
|
||||
101:33 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:101:33
|
||||
99 | onClick={() => onSort("display_name")}
|
||||
100 | >
|
||||
> 101 | Người dùng <SortIcon column="display_name" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
102 | </div>
|
||||
103 | </TableCell>
|
||||
104 |
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:52:20
|
||||
50 | };
|
||||
51 |
|
||||
> 52 | const SortIcon = ({ column }: { column: SortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 53 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 54 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 83 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 84 | };
|
||||
| ^^^^ The component is created during render here
|
||||
85 |
|
||||
86 | return (
|
||||
87 | <div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]"> react-hooks/static-components
|
||||
113:28 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:113:28
|
||||
111 | onClick={() => onSort("email")}
|
||||
112 | >
|
||||
> 113 | Email <SortIcon column="email" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
114 | </div>
|
||||
115 | </TableCell>
|
||||
116 |
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:52:20
|
||||
50 | };
|
||||
51 |
|
||||
> 52 | const SortIcon = ({ column }: { column: SortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 53 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 54 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 83 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 84 | };
|
||||
| ^^^^ The component is created during render here
|
||||
85 |
|
||||
86 | return (
|
||||
87 | <div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]"> react-hooks/static-components
|
||||
165:36 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:165:36
|
||||
163 | onClick={() => onSort("created_at")}
|
||||
164 | >
|
||||
> 165 | Ngày tham gia <SortIcon column="created_at" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
166 | </div>
|
||||
167 | </TableCell>
|
||||
168 |
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:52:20
|
||||
50 | };
|
||||
51 |
|
||||
> 52 | const SortIcon = ({ column }: { column: SortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 53 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 54 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 83 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 84 | };
|
||||
| ^^^^ The component is created during render here
|
||||
85 |
|
||||
86 | return (
|
||||
87 | <div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]"> react-hooks/static-components
|
||||
177:31 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:177:31
|
||||
175 | onClick={() => onSort("updated_at")}
|
||||
176 | >
|
||||
> 177 | Cập nhật <SortIcon column="updated_at" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
178 | </div>
|
||||
179 | </TableCell>
|
||||
180 |
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/BasicTableOne.tsx:52:20
|
||||
50 | };
|
||||
51 |
|
||||
> 52 | const SortIcon = ({ column }: { column: SortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 53 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 54 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 83 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 84 | };
|
||||
| ^^^^ The component is created during render here
|
||||
85 |
|
||||
86 | return (
|
||||
87 | <div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]"> react-hooks/static-components
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx
|
||||
141:30 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:141:30
|
||||
139 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
140 | <div className="flex items-center cursor-pointer select-none" onClick={() => onSort("original_name")}>
|
||||
> 141 | Tên tệp <SortIcon column="original_name" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
142 | </div>
|
||||
143 | </TableCell>
|
||||
144 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:76:20
|
||||
74 | };
|
||||
75 |
|
||||
> 76 | const SortIcon = ({ column }: { column: MediaSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 77 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 78 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 97 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 98 | };
|
||||
| ^^^^ The component is created during render here
|
||||
99 |
|
||||
100 | const getMimeTypeBadge = (mimeType: string) => {
|
||||
101 | if (mimeType.includes("image")) { react-hooks/static-components
|
||||
146:32 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:146:32
|
||||
144 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
145 | <div className="flex items-center cursor-pointer select-none" onClick={() => onSort("mime_type")}>
|
||||
> 146 | Định dạng <SortIcon column="mime_type" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
147 | </div>
|
||||
148 | </TableCell>
|
||||
149 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:76:20
|
||||
74 | };
|
||||
75 |
|
||||
> 76 | const SortIcon = ({ column }: { column: MediaSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 77 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 78 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 97 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 98 | };
|
||||
| ^^^^ The component is created during render here
|
||||
99 |
|
||||
100 | const getMimeTypeBadge = (mimeType: string) => {
|
||||
101 | if (mimeType.includes("image")) { react-hooks/static-components
|
||||
151:33 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:151:33
|
||||
149 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
150 | <div className="flex items-center cursor-pointer select-none" onClick={() => onSort("size")}>
|
||||
> 151 | Kích thước <SortIcon column="size" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
152 | </div>
|
||||
153 | </TableCell>
|
||||
154 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:76:20
|
||||
74 | };
|
||||
75 |
|
||||
> 76 | const SortIcon = ({ column }: { column: MediaSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 77 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 78 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 97 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 98 | };
|
||||
| ^^^^ The component is created during render here
|
||||
99 |
|
||||
100 | const getMimeTypeBadge = (mimeType: string) => {
|
||||
101 | if (mimeType.includes("image")) { react-hooks/static-components
|
||||
156:35 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:156:35
|
||||
154 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
155 | <div className="flex items-center cursor-pointer select-none" onClick={() => onSort("created_at")}>
|
||||
> 156 | Ngày tải lên <SortIcon column="created_at" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
157 | </div>
|
||||
158 | </TableCell>
|
||||
159 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:76:20
|
||||
74 | };
|
||||
75 |
|
||||
> 76 | const SortIcon = ({ column }: { column: MediaSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 77 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 78 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 97 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 98 | };
|
||||
| ^^^^ The component is created during render here
|
||||
99 |
|
||||
100 | const getMimeTypeBadge = (mimeType: string) => {
|
||||
101 | if (mimeType.includes("image")) { react-hooks/static-components
|
||||
161:31 error Error: Cannot create components during render
|
||||
|
||||
Components created during render will reset their state each time they are created. Declare components outside of render.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:161:31
|
||||
159 | <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
|
||||
160 | <div className="flex items-center cursor-pointer select-none" onClick={() => onSort("updated_at")}>
|
||||
> 161 | Cập nhật <SortIcon column="updated_at" />
|
||||
| ^^^^^^^^ This component is created during render
|
||||
162 | </div>
|
||||
163 | </TableCell>
|
||||
164 | <TableCell isHeader className="px-5 py-3 font-medium text-center text-gray-500 text-theme-xs dark:text-gray-400 min-w-[120px]">
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/MediaTable.tsx:76:20
|
||||
74 | };
|
||||
75 |
|
||||
> 76 | const SortIcon = ({ column }: { column: MediaSortColumn }) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 77 | const isActive = sortBy === column;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 78 | return (
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 97 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 98 | };
|
||||
| ^^^^ The component is created during render here
|
||||
99 |
|
||||
100 | const getMimeTypeBadge = (mimeType: string) => {
|
||||
101 | if (mimeType.includes("image")) { react-hooks/static-components
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/tables/UserDetailModal.tsx
|
||||
39:32 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/ui/table/index.tsx
|
||||
58:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/AccountDetails.tsx
|
||||
31:7 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/AccountDetails.tsx:31:7
|
||||
29 | useEffect(() => {
|
||||
30 | if (!isOpen) {
|
||||
> 31 | setFormValues({
|
||||
| ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
32 | old_password: "",
|
||||
33 | new_password: "",
|
||||
34 | confirm_password: "", react-hooks/set-state-in-effect
|
||||
71:44 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
76:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/ApplicationList.tsx
|
||||
142:19 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/Media.tsx
|
||||
33:5 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/Media.tsx:33:5
|
||||
31 |
|
||||
32 | useEffect(() => {
|
||||
> 33 | setLocalMedia(data?.data || []);
|
||||
| ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
34 | }, [data]);
|
||||
35 |
|
||||
36 | const isImageFile = (file: MediaItem) => { react-hooks/set-state-in-effect
|
||||
117:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
159:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars
|
||||
210:13 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/UserInfoCard.tsx
|
||||
28:7 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/UserInfoCard.tsx:28:7
|
||||
26 | useEffect(() => {
|
||||
27 | if (data?.data?.profile) {
|
||||
> 28 | setFormData({
|
||||
| ^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
29 | display_name: data.data.profile.display_name || "",
|
||||
30 | phone: data.data.profile.phone || "",
|
||||
31 | bio: data.data.profile.bio || "", react-hooks/set-state-in-effect
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/components/user-profile/UserMetaCard.tsx
|
||||
19:9 warning 'router' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/config/config.ts
|
||||
25:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
31:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
39:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
44:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
82:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
99:43 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
110:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
128:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
142:24 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/context/ThemeContext.tsx
|
||||
26:5 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/context/ThemeContext.tsx:26:5
|
||||
24 | const initialTheme = savedTheme || "light"; // Default to light theme
|
||||
25 |
|
||||
> 26 | setTheme(initialTheme);
|
||||
| ^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
27 | setIsInitialized(true);
|
||||
28 | }, []);
|
||||
29 | react-hooks/set-state-in-effect
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/interface/common.ts
|
||||
1:37 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
5:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
18:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
28:12 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/layout/AppHeader.tsx
|
||||
2:10 warning 'ThemeToggleButton' is defined but never used @typescript-eslint/no-unused-vars
|
||||
3:8 warning 'NotificationDropdown' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/service/mediaService.ts
|
||||
54:9 warning 'res' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/store/StoreProvider.tsx
|
||||
13:29 error Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/store/StoreProvider.tsx:13:29
|
||||
11 | }) {
|
||||
12 | const storeRef = useRef(store);
|
||||
> 13 | return <Provider store={storeRef.current}>{children}</Provider>;
|
||||
| ^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
14 | } react-hooks/refs
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/store/features/userSlice.ts
|
||||
15:26 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
32:63 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/config.ts
|
||||
6:7 warning 'GOONG_PROXY_BASE_PATH' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/http.ts
|
||||
47:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
52:14 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
67:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
104:29 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
104:78 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
106:52 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/editor/Panel.tsx
|
||||
12:5 warning 'badge' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/editor/ReplayEffectsSidebar.tsx
|
||||
22:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
111:7 warning 'uiInputOptionValues' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
165:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
183:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
212:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
213:16 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
221:64 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
226:49 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
316:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
761:11 warning 'safeYear' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
822:11 warning 'firstId' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
1167:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1180:41 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1190:13 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/editor/ReplayEffectsSidebar.tsx:1190:13
|
||||
1188 | if (actions.length > 0) {
|
||||
1189 | const first = actions[0];
|
||||
> 1190 | setComposerFunctionName(first.function_name);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
1191 | const def = definitions[first.function_name];
|
||||
1192 | if (def) {
|
||||
1193 | setComposerDraftValues(def.deserialize(first.params)); react-hooks/set-state-in-effect
|
||||
1354:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1380:53 warning Compilation Skipped: `this` is not supported syntax
|
||||
|
||||
React Compiler does not support compiling functions that use `this`.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/editor/ReplayEffectsSidebar.tsx:1380:53
|
||||
1378 | ],
|
||||
1379 | handlers: {
|
||||
> 1380 | link: function (this: { quill?: any }) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ `this` was used here
|
||||
1381 | onLinkClick?.(this?.quill);
|
||||
1382 | },
|
||||
1383 | }, react-hooks/unsupported-syntax
|
||||
1380:69 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1541:10 warning 'toInputNumber' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1554:10 warning 'toNumberOr' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1566:10 warning 'emptyToNull' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1570:10 warning 'emptyToUndefined' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1574:10 warning 'compactTrailingUndefined' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1582:10 warning 'normalizeSelectValue' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx
|
||||
5:10 warning 'GEO_TYPE_KEYS' is defined but never used @typescript-eslint/no-unused-vars
|
||||
168:7 warning 'buttonClassName' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/editor/ReplayPreviewOverlay.tsx
|
||||
112:25 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/mapUtils.ts
|
||||
321:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
487:46 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1065:44 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1179:36 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/useMapInstance.ts
|
||||
65:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/useMapLayers.ts
|
||||
12:10 warning 'FeatureCollection' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/useMapSync.ts
|
||||
5:10 warning 'EMPTY_FEATURE_COLLECTION' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PinnedWikiPopup.tsx
|
||||
24:5 warning 'featureId' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx
|
||||
4:8 warning 'EditorMap' is defined but never used @typescript-eslint/no-unused-vars
|
||||
10:10 warning 'useReplayPreview' is defined but never used @typescript-eslint/no-unused-vars
|
||||
20:10 warning 'persistBackgroundLayerVisibility' is defined but never used @typescript-eslint/no-unused-vars
|
||||
28:10 warning 'deepClone' is defined but never used @typescript-eslint/no-unused-vars
|
||||
55:20 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
81:23 error Component definition is missing display name react/display-name
|
||||
81:34 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
90:5 warning 'entityWikiLinks' is defined but never used @typescript-eslint/no-unused-vars
|
||||
96:5 warning 'onViewModeChange' is defined but never used @typescript-eslint/no-unused-vars
|
||||
98:5 warning 'isGlobalLoading' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
101:5 warning 'selectedStageId' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
102:5 warning 'selectedStepIndex' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
106:5 warning 'mapHandleRef' is defined but never used @typescript-eslint/no-unused-vars
|
||||
110:5 warning 'previewEntityFocusToken' is defined but never used @typescript-eslint/no-unused-vars
|
||||
114:5 warning 'isLargeScreen' is defined but never used @typescript-eslint/no-unused-vars
|
||||
115:5 warning 'setIsLargeScreen' is defined but never used @typescript-eslint/no-unused-vars
|
||||
143:9 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx:143:9
|
||||
141 | useEffect(() => {
|
||||
142 | setPreviewWikiCache({});
|
||||
> 143 | setPreviewWikiError(null);
|
||||
| ^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
144 | setIsPreviewWikiLoading(false);
|
||||
145 | setPreviewPinnedWikiPopupAnchor(null);
|
||||
146 | setPreviewActiveEntityId(null); react-hooks/set-state-in-effect
|
||||
209:13 error Error: Calling setState synchronously within an effect can trigger cascading renders
|
||||
|
||||
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
|
||||
* Update external systems with the latest state from React.
|
||||
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
|
||||
|
||||
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx:209:13
|
||||
207 | useEffect(() => {
|
||||
208 | if (!mode || !replayPreviewSidebarOpen) {
|
||||
> 209 | setPreviewWikiError(null);
|
||||
| ^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
||||
210 | setIsPreviewWikiLoading(false);
|
||||
211 | return;
|
||||
212 | } react-hooks/set-state-in-effect
|
||||
261:8 warning React Hook useEffect has a missing dependency: 'setPreviewWikiCache'. Either include it or remove the dependency array. If 'setPreviewWikiCache' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
305:11 warning 'replayPreviewActiveEntityGeometries' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
315:51 error Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `setPreviewActiveEntityId`, but the source dependencies were [openReplayPreviewWikiPanelById, previewRelations.entitiesById, previewRelations.entityWikisById, setSelectedFeatureIds]. Inferred different dependency than source.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx:315:51
|
||||
313 |
|
||||
314 | // Handle replay preview entity selection
|
||||
> 315 | const selectReplayPreviewEntity = useCallback((
|
||||
| ^
|
||||
> 316 | entityId: string,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 317 | options?: {
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 352 | }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 353 | }, [
|
||||
| ^^^^^^ Could not preserve existing manual memoization
|
||||
354 | openReplayPreviewWikiPanelById,
|
||||
355 | previewRelations.entitiesById,
|
||||
356 | previewRelations.entityWikisById, react-hooks/preserve-manual-memoization
|
||||
315:51 error Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `setPreviewEntityFocusToken`, but the source dependencies were [openReplayPreviewWikiPanelById, previewRelations.entitiesById, previewRelations.entityWikisById, setSelectedFeatureIds]. Inferred different dependency than source.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx:315:51
|
||||
313 |
|
||||
314 | // Handle replay preview entity selection
|
||||
> 315 | const selectReplayPreviewEntity = useCallback((
|
||||
| ^
|
||||
> 316 | entityId: string,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 317 | options?: {
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 352 | }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 353 | }, [
|
||||
| ^^^^^^ Could not preserve existing manual memoization
|
||||
354 | openReplayPreviewWikiPanelById,
|
||||
355 | previewRelations.entitiesById,
|
||||
356 | previewRelations.entityWikisById, react-hooks/preserve-manual-memoization
|
||||
353:8 warning React Hook useCallback has missing dependencies: 'setPreviewActiveEntityId' and 'setPreviewEntityFocusToken'. Either include them or remove the dependency array. If 'setPreviewActiveEntityId' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
361:51 error Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `setPreviewActiveEntityId`, but the source dependencies were [closeReplayPreviewWikiPanel, setSelectedFeatureIds]. Inferred different dependency than source.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx:361:51
|
||||
359 |
|
||||
360 | // Handle close sidebar
|
||||
> 361 | const closeReplayPreviewSidebar = useCallback(() => {
|
||||
| ^^^^^^^
|
||||
> 362 | closeReplayPreviewWikiPanel();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 363 | setPreviewActiveEntityId(null);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 364 | setIsPreviewEntitySidebarOpen(false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 365 | setPreviewWikiError(null);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 366 | setPreviewLinkEntityPopup(null);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 367 | setSelectedFeatureIds([]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 368 | }, [closeReplayPreviewWikiPanel, setSelectedFeatureIds]);
|
||||
| ^^^^^^ Could not preserve existing manual memoization
|
||||
369 |
|
||||
370 | // Play selected battle replay
|
||||
371 | const handlePlaySelectedReplay = useCallback((replay: BattleReplay) => { react-hooks/preserve-manual-memoization
|
||||
368:8 warning React Hook useCallback has a missing dependency: 'setPreviewActiveEntityId'. Either include it or remove the dependency array. If 'setPreviewActiveEntityId' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
517:11 warning 'mapRenderDraft' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
575:11 warning 'mapLabelContextDraft' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
580:11 warning 'viewerPreviewSelectedReplay' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
600:55 error Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `setPreviewEntityFocusToken`, but the source dependencies were [previewRelations.geometryEntityIds, selectReplayPreviewEntity]. Inferred different dependency than source.
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/preview/PreviewLayout.tsx:600:55
|
||||
598 | }, []);
|
||||
599 |
|
||||
> 600 | const handleFocusHistoricalGeometry = useCallback((payload: HistoricalGeometryFocusPayload) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 601 | setFocusedPresentPlace(null);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 602 | setSelectedFeatureIds([payload.geometry.id]);
|
||||
…
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 612 | }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 613 | }, [previewRelations.geometryEntityIds, selectReplayPreviewEntity]);
|
||||
| ^^^^^^ Could not preserve existing manual memoization
|
||||
614 |
|
||||
615 | const effectiveGeometryVisibility = useMemo(() => {
|
||||
616 | return geometryVisibility; react-hooks/preserve-manual-memoization
|
||||
613:8 warning React Hook useCallback has a missing dependency: 'setPreviewEntityFocusToken'. Either include it or remove the dependency array. If 'setPreviewEntityFocusToken' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
|
||||
619:11 warning 'handleSetMode' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
884:10 warning 'buildPreviewRelationIndex' is defined but never used @typescript-eslint/no-unused-vars
|
||||
909:11 warning 'wikiMap' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/lib/editor/snapshot/editorSnapshot.ts
|
||||
1229:46 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1230:44 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1231:49 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1232:53 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
1281:27 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/lib/map/styles/shared/pointStyle.ts
|
||||
26:6 warning 'PointIconVariant' is defined but never used @typescript-eslint/no-unused-vars
|
||||
45:7 warning 'DRAFT_FILL' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
46:7 warning 'DRAFT_RIM' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/lib/replay/replayDispatcher.ts
|
||||
4:5 warning 'GeoFunctionName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
5:5 warning 'MapFunctionName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
6:5 warning 'NarrativeFunctionName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
8:5 warning 'UIOptionName' is defined but never used @typescript-eslint/no-unused-vars
|
||||
22:14 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
50:29 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
196:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
196:59 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
||||
|
||||
/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/lib/replay/useReplayPreview.ts
|
||||
258:8 warning React Hook useEffect has a missing dependency: 'setDialogWithRef'. Either include it or remove the dependency array react-hooks/exhaustive-deps
|
||||
|
||||
✖ 204 problems (99 errors, 105 warnings)
|
||||
|
||||
@@ -3,27 +3,23 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, type SetStateAction } from "react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import Map, { type MapFeaturePayload, type MapHandle } from "@/uhm/components/Map";
|
||||
import Map, { type MapHandle } from "@/uhm/components/Map";
|
||||
import Editor from "@/uhm/components/Editor";
|
||||
import BackgroundLayersPanel from "@/uhm/components/editor/BackgroundLayersPanel";
|
||||
import TimelineBar from "@/uhm/components/ui/TimelineBar";
|
||||
import SelectedGeometryPanel from "@/uhm/components/editor/SelectedGeometryPanel";
|
||||
import ReplayTimelineSidebar from "@/uhm/components/editor/ReplayTimelineSidebar";
|
||||
import ReplayEffectsSidebar from "@/uhm/components/editor/ReplayEffectsSidebar";
|
||||
import ReplayPreviewOverlay from "@/uhm/components/editor/ReplayPreviewOverlay";
|
||||
import ReplayPreviewLayerPanel from "@/uhm/components/editor/ReplayPreviewLayerPanel";
|
||||
import PreviewLayout from "@/uhm/components/preview/PreviewLayout";
|
||||
import PublicWikiSidebar from "@/uhm/components/wiki/PublicWikiSidebar";
|
||||
import WikiSidebarPanel from "@/uhm/components/wiki/WikiSidebarPanel";
|
||||
import ProjectEntityRefsPanel from "@/uhm/components/editor/ProjectEntityRefsPanel";
|
||||
import EntityWikiBindingsPanel from "@/uhm/components/editor/EntityWikiBindingsPanel";
|
||||
import GeometryBindingPanel from "@/uhm/components/editor/GeometryBindingPanel";
|
||||
import ImageOverlayPanel from "@/uhm/components/editor/ImageOverlayPanel";
|
||||
import PresentPlaceSearch, { type HistoricalGeometryFocusPayload, type PresentPlaceSelection } from "@/uhm/components/editor/PresentPlaceSearch";
|
||||
import { Entity, fetchEntities, searchEntitiesByName } from "@/uhm/api/entities";
|
||||
import { ApiError } from "@/uhm/api/http";
|
||||
import { fetchCurrentUser } from "@/uhm/api/auth";
|
||||
import { fetchWikiById, searchWikisByTitle, type Wiki } from "@/uhm/api/wikis";
|
||||
import { searchWikisByTitle, type Wiki } from "@/uhm/api/wikis";
|
||||
import { searchGeometriesByEntityName, fetchGeometriesByBBox, type EntityGeometriesSearchItem, type EntityGeometrySearchGeo } from "@/uhm/api/geometries";
|
||||
import { WORLD_BBOX } from "@/uhm/lib/map/geo/constants";
|
||||
import {
|
||||
@@ -50,7 +46,6 @@ import { buildFeatureEntityPatch } from "@/uhm/lib/editor/entity/entityBinding";
|
||||
import { newId } from "@/uhm/lib/utils/id";
|
||||
import {
|
||||
loadBackgroundLayerVisibilityFromStorage,
|
||||
persistBackgroundLayerVisibility,
|
||||
} from "@/uhm/lib/editor/background/backgroundVisibilityStorage";
|
||||
import { deepClone } from "@/uhm/lib/editor/draft/draftDiff";
|
||||
import { useProjectCommands } from "@/uhm/lib/editor/project/useProjectCommands";
|
||||
@@ -86,7 +81,6 @@ import {
|
||||
normalizeReplaysForCompare,
|
||||
normalizeWikisForCompare,
|
||||
} from "@/uhm/lib/editor/editorPageUtils";
|
||||
import { fitMapToFeatureCollection } from "@/uhm/components/map/mapUtils";
|
||||
|
||||
const CURRENT_YEAR = new Date().getUTCFullYear();
|
||||
const DEFAULT_EDITOR_USER_ID = "local-editor";
|
||||
@@ -116,12 +110,6 @@ type PreviewRelationIndex = {
|
||||
wikiBySlug: Record<string, Wiki>;
|
||||
};
|
||||
|
||||
type PreviewLinkEntityPopupState = {
|
||||
slug: string;
|
||||
entities: Entity[];
|
||||
top: number;
|
||||
left: number;
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
@@ -542,27 +530,6 @@ function EditorPageContent() {
|
||||
setTimelineDraftYear(clampYearToFixedRange(Math.trunc(nextYear)));
|
||||
}, [setTimelineDraftYear]);
|
||||
|
||||
const handleViewerPreviewTimelineYearChange = useCallback((nextYear: number) => {
|
||||
setPreviewSession((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
timelineYear: clampYearToFixedRange(Math.trunc(nextYear)),
|
||||
}
|
||||
: prev
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleViewerPreviewTimelineFilterChange = useCallback((enabled: boolean) => {
|
||||
setPreviewSession((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
timelineFilterEnabled: enabled,
|
||||
}
|
||||
: prev
|
||||
);
|
||||
}, []);
|
||||
|
||||
// Preview specific UI states
|
||||
const [previewActiveEntityId, setPreviewActiveEntityId] = useState<string | null>(null);
|
||||
@@ -613,9 +580,7 @@ function EditorPageContent() {
|
||||
hiddenGeometryIds: replayPreviewHiddenGeometryIds,
|
||||
timelineYear: replayPreviewTimelineYear,
|
||||
timelineFilterEnabled: replayPreviewTimelineFilterEnabled,
|
||||
activeCursor: replayPreviewActiveCursor,
|
||||
activeWikiId: replayPreviewActiveWikiId,
|
||||
sidebarOpen: replayPreviewSidebarOpen,
|
||||
} = replayPreview;
|
||||
|
||||
// Draft hiển thị trong preview có thể ẩn bớt geometry theo action replay.
|
||||
@@ -661,12 +626,6 @@ function EditorPageContent() {
|
||||
return activeWikiEntityIds[0] || previewActiveEntityId;
|
||||
}, [previewActiveEntityId, previewRelations.wikiEntityIdsById, replayPreviewActiveWikiId]);
|
||||
|
||||
const replayPreviewActiveEntity = useMemo(() => {
|
||||
return replayPreviewActiveEntityId
|
||||
? previewRelations.entitiesById[replayPreviewActiveEntityId] || null
|
||||
: null;
|
||||
}, [replayPreviewActiveEntityId, previewRelations.entitiesById]);
|
||||
|
||||
const replayPreviewActiveEntityGeometries = useMemo(() => {
|
||||
return replayPreviewActiveEntityId
|
||||
? previewRelations.entityGeometriesById[replayPreviewActiveEntityId] || EMPTY_FEATURE_COLLECTION
|
||||
@@ -3044,25 +3003,6 @@ function snapshotWikiToWiki(snapshot: WikiSnapshot, wikiCache: Record<string, Wi
|
||||
};
|
||||
}
|
||||
|
||||
function extractWikiBlockquoteText(content: string | null | undefined): string {
|
||||
if (!content) return "";
|
||||
|
||||
const blockquoteMatch = content.match(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/i);
|
||||
const rawText = blockquoteMatch?.[1]?.trim() || "";
|
||||
if (!rawText) return "";
|
||||
|
||||
return rawText
|
||||
.replace(/<[^>]*>/g, "")
|
||||
.replace(/ /gi, " ")
|
||||
.replace(/\u00a0/g, " ")
|
||||
.replace(/&/gi, "&")
|
||||
.replace(/</gi, "<")
|
||||
.replace(/>/gi, ">")
|
||||
.replace(/"/gi, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function pushUniqueString(target: Record<string, string[]>, key: string, value: string) {
|
||||
if (!target[key]) {
|
||||
@@ -3080,20 +3020,6 @@ function normalizeRelationArrays(target: Record<string, string[]>) {
|
||||
}
|
||||
}
|
||||
|
||||
function computeFixedPopupPosition(rect: DOMRect, width: number, height: number) {
|
||||
const margin = 12;
|
||||
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 1440;
|
||||
const viewportHeight = typeof window !== "undefined" ? window.innerHeight : 900;
|
||||
const preferredLeft = rect.right + margin;
|
||||
const maxLeft = Math.max(margin, viewportWidth - width - margin);
|
||||
const left = Math.min(preferredLeft, maxLeft);
|
||||
|
||||
const preferredTop = rect.top;
|
||||
const maxTop = Math.max(margin, viewportHeight - height - margin);
|
||||
const top = Math.max(margin, Math.min(preferredTop, maxTop));
|
||||
|
||||
return { top, left };
|
||||
}
|
||||
|
||||
function isTypingTarget(target: EventTarget | null): boolean {
|
||||
if (!(target instanceof HTMLElement)) return false;
|
||||
|
||||
@@ -41,7 +41,23 @@ export function Panel({
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<span>{title}</span>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
||||
<span>{title}</span>
|
||||
{badge && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
background: "#374151",
|
||||
color: "#cbd5e1",
|
||||
padding: "2px 6px",
|
||||
borderRadius: 4,
|
||||
}}
|
||||
>
|
||||
{badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
width: 22,
|
||||
|
||||
@@ -108,11 +108,6 @@ const uiSimpleOptionValues: UIOptionName[] = [
|
||||
"zoom_panel",
|
||||
];
|
||||
|
||||
const uiInputOptionValues: UIOptionName[] = [
|
||||
"wiki",
|
||||
"toast",
|
||||
];
|
||||
|
||||
const mapCameraOptionChoices: Array<{ label: string; value: MapCameraOptionName }> = [
|
||||
{ label: "LngLat", value: "center" },
|
||||
{ label: "Zoom", value: "zoom" },
|
||||
@@ -758,8 +753,6 @@ function MapFunctionShortcutPanel({
|
||||
currentTimelineYear: number;
|
||||
onAppendActions: (actions: ReplayAction<MapFunctionName>[], label: string) => void;
|
||||
}) {
|
||||
const safeYear = Math.trunc(currentTimelineYear);
|
||||
|
||||
return (
|
||||
<Panel title="Map Functions" defaultOpen>
|
||||
<div style={{ display: "grid", gap: 10 }}>
|
||||
@@ -819,7 +812,6 @@ function GeoFunctionShortcutPanel({
|
||||
}) {
|
||||
const selectedIds = selectedGeometries.map((item) => item.id);
|
||||
const selectedCount = selectedIds.length;
|
||||
const firstId = selectedIds[0] || "";
|
||||
const hasSelection = selectedCount > 0;
|
||||
|
||||
return (
|
||||
@@ -1538,24 +1530,6 @@ function asString(value: unknown) {
|
||||
return typeof value === "string" ? value : value == null ? "" : String(value);
|
||||
}
|
||||
|
||||
function toInputNumber(value: unknown, fallback: string) {
|
||||
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
||||
if (typeof value === "string" && value.trim().length) return value;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function toOptionalNumber(value: unknown) {
|
||||
const raw = asString(value).trim();
|
||||
if (!raw.length) return undefined;
|
||||
const parsed = Number(raw);
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
function toNumberOr(value: unknown, fallback: number) {
|
||||
const parsed = toOptionalNumber(value);
|
||||
return parsed == null ? fallback : parsed;
|
||||
}
|
||||
|
||||
function toStringArray(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value
|
||||
@@ -1563,26 +1537,6 @@ function toStringArray(value: unknown): string[] {
|
||||
.filter((item) => item.length > 0);
|
||||
}
|
||||
|
||||
function emptyToNull(value: string) {
|
||||
return value.trim().length ? value : null;
|
||||
}
|
||||
|
||||
function emptyToUndefined(value: string) {
|
||||
return value.trim().length ? value : undefined;
|
||||
}
|
||||
|
||||
function compactTrailingUndefined(values: unknown[]) {
|
||||
const next = [...values];
|
||||
while (next.length > 0 && next[next.length - 1] === undefined) {
|
||||
next.pop();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function normalizeSelectValue(value: string, fallback: string) {
|
||||
return value.trim().length ? value : fallback;
|
||||
}
|
||||
|
||||
function buildUiEffectsDraftState(actions: ReplayAction<UIOptionName>[]): UiEffectsDraftState {
|
||||
const selected = buildEmptyUiOptionSelection();
|
||||
const visible = buildDefaultUiVisibilityState();
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from "react";
|
||||
import EditorMap, { type MapFeaturePayload, type MapHandle } from "@/uhm/components/Map";
|
||||
import { type MapFeaturePayload, type MapHandle } from "@/uhm/components/Map";
|
||||
import PresentPlaceSearch, { type HistoricalGeometryFocusPayload, type PresentPlaceSelection } from "@/uhm/components/editor/PresentPlaceSearch";
|
||||
import ReplayPreviewOverlay from "@/uhm/components/editor/ReplayPreviewOverlay";
|
||||
import ReplayPreviewLayerPanel from "@/uhm/components/editor/ReplayPreviewLayerPanel";
|
||||
import PublicWikiSidebar from "@/uhm/components/wiki/PublicWikiSidebar";
|
||||
import TimelineBar from "@/uhm/components/ui/TimelineBar";
|
||||
import { useReplayPreview } from "@/uhm/lib/replay/useReplayPreview";
|
||||
import RelatedEntityPopup from "./RelatedEntityPopup";
|
||||
import PinnedWikiPopup from "./PinnedWikiPopup";
|
||||
|
||||
import { fetchWikiById, type Wiki } from "@/uhm/api/wikis";
|
||||
import { type Wiki } from "@/uhm/api/wikis";
|
||||
import type { Entity } from "@/uhm/api/entities";
|
||||
import type { Feature, FeatureCollection } from "@/uhm/types/geo";
|
||||
import type { BattleReplay, EntityWikiLinkSnapshot } from "@/uhm/types/projects";
|
||||
import type { FeatureCollection } from "@/uhm/types/geo";
|
||||
import type { BattleReplay } from "@/uhm/types/projects";
|
||||
import type { WikiSnapshot } from "@/uhm/types/wiki";
|
||||
import { type BackgroundLayerVisibility } from "@/uhm/lib/map/styles/backgroundLayers";
|
||||
import { persistBackgroundLayerVisibility } from "@/uhm/lib/editor/background/backgroundVisibilityStorage";
|
||||
import { EMPTY_FEATURE_COLLECTION } from "@/uhm/lib/map/geo/constants";
|
||||
import {
|
||||
clampNumber,
|
||||
isFeatureVisibleAtYear,
|
||||
} from "@/uhm/lib/editor/editorPageUtils";
|
||||
import { normalizeFeatureEntityIds } from "@/uhm/lib/editor/snapshot/editorSnapshot";
|
||||
import { normalizeTimelineYearValue } from "@/uhm/lib/utils/timeline";
|
||||
import { deepClone } from "@/uhm/lib/editor/draft/draftDiff";
|
||||
import type { EditorMode } from "@/uhm/lib/editor/session/sessionTypes";
|
||||
|
||||
type Props = {
|
||||
projectId: string;
|
||||
@@ -37,32 +28,20 @@ type Props = {
|
||||
replays: BattleReplay[];
|
||||
entities: Entity[];
|
||||
wikis: WikiSnapshot[];
|
||||
entityWikiLinks: EntityWikiLinkSnapshot[];
|
||||
backgroundVisibility: BackgroundLayerVisibility;
|
||||
onBackgroundVisibilityChange: (vis: BackgroundLayerVisibility) => void;
|
||||
geometryVisibility: Record<string, boolean>;
|
||||
onGeometryVisibilityChange: (vis: Record<string, boolean>) => void;
|
||||
viewMode: "local" | "global";
|
||||
onViewModeChange?: (mode: "local" | "global") => void;
|
||||
globalGeometries?: FeatureCollection;
|
||||
isGlobalLoading?: boolean;
|
||||
baseline?: FeatureCollection;
|
||||
activeReplay?: BattleReplay | null;
|
||||
selectedStageId?: number | null;
|
||||
selectedStepIndex?: number | null;
|
||||
autoplayMode?: "start" | "selection" | null;
|
||||
|
||||
replayPreview: any;
|
||||
mapHandleRef: React.RefObject<MapHandle | null>;
|
||||
previewRelations: PreviewRelationIndex;
|
||||
previewActiveEntityId: string | null;
|
||||
setPreviewActiveEntityId: (id: string | null) => void;
|
||||
previewEntityFocusToken: number;
|
||||
setPreviewEntityFocusToken: React.Dispatch<React.SetStateAction<number>>;
|
||||
previewSidebarWidth: number;
|
||||
setPreviewSidebarWidth: React.Dispatch<React.SetStateAction<number>>;
|
||||
isLargeScreen: boolean;
|
||||
setIsLargeScreen: (isLarge: boolean) => void;
|
||||
previewWikiCache: Record<string, Wiki>;
|
||||
setPreviewWikiCache: React.Dispatch<React.SetStateAction<Record<string, Wiki>>>;
|
||||
};
|
||||
@@ -83,36 +62,21 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
mode,
|
||||
onModeChange,
|
||||
onExitPreview,
|
||||
draft,
|
||||
replays,
|
||||
entities,
|
||||
wikis,
|
||||
entityWikiLinks,
|
||||
backgroundVisibility,
|
||||
onBackgroundVisibilityChange,
|
||||
geometryVisibility,
|
||||
onGeometryVisibilityChange,
|
||||
viewMode,
|
||||
onViewModeChange,
|
||||
globalGeometries = EMPTY_FEATURE_COLLECTION,
|
||||
isGlobalLoading = false,
|
||||
baseline = EMPTY_FEATURE_COLLECTION,
|
||||
activeReplay,
|
||||
selectedStageId = null,
|
||||
selectedStepIndex = null,
|
||||
autoplayMode = null,
|
||||
|
||||
replayPreview,
|
||||
mapHandleRef,
|
||||
previewRelations,
|
||||
previewActiveEntityId,
|
||||
setPreviewActiveEntityId,
|
||||
previewEntityFocusToken,
|
||||
setPreviewEntityFocusToken,
|
||||
previewSidebarWidth,
|
||||
setPreviewSidebarWidth,
|
||||
isLargeScreen,
|
||||
setIsLargeScreen,
|
||||
previewWikiCache,
|
||||
setPreviewWikiCache,
|
||||
}: Props, ref) => {
|
||||
@@ -167,9 +131,7 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
}, [autoplayMode, isReplayPreviewMode, currentActiveReplay, replayPreview]);
|
||||
|
||||
const {
|
||||
hiddenGeometryIds: replayPreviewHiddenGeometryIds,
|
||||
timelineYear: replayPreviewTimelineYear,
|
||||
timelineFilterEnabled: replayPreviewTimelineFilterEnabled,
|
||||
resetPreview: resetReplayPreview,
|
||||
playbackSpeed: replayPreviewPlaybackSpeed,
|
||||
activeCursor: replayPreviewActiveCursor,
|
||||
@@ -181,7 +143,6 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
|
||||
// Timeline bar parameters
|
||||
const activeTimelineYear = isReplayPreviewMode ? replayPreviewTimelineYear : replayPreviewTimelineYear;
|
||||
const activeTimelineFilterEnabled = isReplayPreviewMode ? replayPreviewTimelineFilterEnabled : true;
|
||||
|
||||
// Timeline bar visibility
|
||||
const timelineBarVisible = !isReplayPreviewMode || replayPreview.timelineVisible;
|
||||
@@ -302,15 +263,9 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
? previewRelations.entitiesById[replayPreviewActiveEntityId] || null
|
||||
: null;
|
||||
|
||||
const replayPreviewActiveEntityGeometries = replayPreviewActiveEntityId
|
||||
? previewRelations.entityGeometriesById[replayPreviewActiveEntityId] || EMPTY_FEATURE_COLLECTION
|
||||
: EMPTY_FEATURE_COLLECTION;
|
||||
|
||||
const isReplayPreviewWikiSidebarOpen = mode && (replayPreviewSidebarOpen || isPreviewEntitySidebarOpen);
|
||||
|
||||
// Selected feature ids
|
||||
const [selectedFeatureIds, setSelectedFeatureIds] = useState<(string | number)[]>([]);
|
||||
|
||||
// Handle replay preview entity selection
|
||||
const selectReplayPreviewEntity = useCallback((
|
||||
entityId: string,
|
||||
@@ -344,9 +299,6 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
if (options?.focusMap === true) {
|
||||
setPreviewEntityFocusToken((prev) => (prev ?? 0) + 1);
|
||||
}
|
||||
if (options?.selectGeometry && options.sourceFeatureId != null) {
|
||||
setSelectedFeatureIds([options.sourceFeatureId]);
|
||||
}
|
||||
if (nextWiki) {
|
||||
openReplayPreviewWikiPanelById(nextWiki.id);
|
||||
}
|
||||
@@ -354,7 +306,8 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
openReplayPreviewWikiPanelById,
|
||||
previewRelations.entitiesById,
|
||||
previewRelations.entityWikisById,
|
||||
setSelectedFeatureIds,
|
||||
setPreviewActiveEntityId,
|
||||
setPreviewEntityFocusToken,
|
||||
]);
|
||||
|
||||
// Handle close sidebar
|
||||
@@ -364,8 +317,7 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
setIsPreviewEntitySidebarOpen(false);
|
||||
setPreviewWikiError(null);
|
||||
setPreviewLinkEntityPopup(null);
|
||||
setSelectedFeatureIds([]);
|
||||
}, [closeReplayPreviewWikiPanel, setSelectedFeatureIds]);
|
||||
}, [closeReplayPreviewWikiPanel, setPreviewActiveEntityId]);
|
||||
|
||||
// Play selected battle replay
|
||||
const handlePlaySelectedReplay = useCallback((replay: BattleReplay) => {
|
||||
@@ -499,94 +451,7 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
wikis,
|
||||
]);
|
||||
|
||||
// Render Draft geometries builder
|
||||
const replayPreviewDraft = useMemo(() => {
|
||||
const sourceDraft = draft;
|
||||
if (!isReplayPreviewMode || replayPreviewHiddenGeometryIds.length === 0) {
|
||||
return sourceDraft;
|
||||
}
|
||||
const hiddenIds = new Set(replayPreviewHiddenGeometryIds);
|
||||
return {
|
||||
...sourceDraft,
|
||||
features: sourceDraft.features.filter(
|
||||
(feature) => !hiddenIds.has(String(feature.properties.id))
|
||||
),
|
||||
};
|
||||
}, [isReplayPreviewMode, draft, replayPreviewHiddenGeometryIds]);
|
||||
|
||||
const mapRenderDraft = useMemo(() => {
|
||||
if (isReplayPreviewMode) {
|
||||
return replayPreviewDraft;
|
||||
}
|
||||
|
||||
const sourceDraft = draft;
|
||||
if (!activeTimelineFilterEnabled) {
|
||||
return sourceDraft;
|
||||
}
|
||||
|
||||
return {
|
||||
...sourceDraft,
|
||||
features: sourceDraft.features.filter((feature) =>
|
||||
isFeatureVisibleAtYear(feature, activeTimelineYear)
|
||||
),
|
||||
};
|
||||
}, [
|
||||
activeTimelineFilterEnabled,
|
||||
activeTimelineYear,
|
||||
draft,
|
||||
isReplayPreviewMode,
|
||||
replayPreviewDraft,
|
||||
]);
|
||||
|
||||
// Build label context
|
||||
const labelContextBaseDraft = useMemo(() => {
|
||||
if (viewMode === "local") {
|
||||
return draft;
|
||||
}
|
||||
|
||||
const localFeatureIds = new Set<string>();
|
||||
for (const f of draft.features) {
|
||||
if (f.properties?.id != null) {
|
||||
localFeatureIds.add(String(f.properties.id));
|
||||
}
|
||||
}
|
||||
if (baseline && baseline.features) {
|
||||
for (const f of baseline.features) {
|
||||
if (f.properties?.id != null) {
|
||||
localFeatureIds.add(String(f.properties.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mergedFeatures = [...draft.features];
|
||||
for (const globalFeature of globalGeometries.features) {
|
||||
const globalId = globalFeature.properties?.id != null ? String(globalFeature.properties.id) : null;
|
||||
if (globalId === null || !localFeatureIds.has(globalId)) {
|
||||
mergedFeatures.push(globalFeature);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...draft,
|
||||
features: mergedFeatures,
|
||||
};
|
||||
}, [viewMode, draft, baseline, globalGeometries.features]);
|
||||
|
||||
const mapLabelContextDraft = useMemo(() => {
|
||||
return buildEntityLabelContextDraft(labelContextBaseDraft, entities);
|
||||
}, [entities, labelContextBaseDraft]);
|
||||
|
||||
// Replay matching the selected feature
|
||||
const viewerPreviewSelectedReplay = useMemo(() => {
|
||||
if (isReplayPreviewMode || !selectedFeatureIds.length) return null;
|
||||
const selectedGeometryId = String(selectedFeatureIds[0] ?? "").trim();
|
||||
if (!selectedGeometryId.length) return null;
|
||||
return replays.find(
|
||||
(r) =>
|
||||
String(r?.geometry_id || "").trim() === selectedGeometryId &&
|
||||
hasPlayableReplaySteps(r)
|
||||
) || null;
|
||||
}, [isReplayPreviewMode, replays, selectedFeatureIds]);
|
||||
|
||||
// Search and focus place
|
||||
const handleFocusPresentPlace = useCallback((place: PresentPlaceSelection) => {
|
||||
@@ -599,7 +464,6 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
|
||||
const handleFocusHistoricalGeometry = useCallback((payload: HistoricalGeometryFocusPayload) => {
|
||||
setFocusedPresentPlace(null);
|
||||
setSelectedFeatureIds([payload.geometry.id]);
|
||||
setPreviewEntityFocusToken((prev) => (prev ?? 0) + 1);
|
||||
|
||||
const linkedEntityIds = previewRelations.geometryEntityIds[String(payload.geometry.id)] || [];
|
||||
@@ -610,17 +474,13 @@ const PreviewLayout = forwardRef<any, Props>(({
|
||||
selectGeometry: false,
|
||||
});
|
||||
}
|
||||
}, [previewRelations.geometryEntityIds, selectReplayPreviewEntity]);
|
||||
}, [previewRelations.geometryEntityIds, selectReplayPreviewEntity, setPreviewEntityFocusToken]);
|
||||
|
||||
const effectiveGeometryVisibility = useMemo(() => {
|
||||
return geometryVisibility;
|
||||
}, [geometryVisibility]);
|
||||
|
||||
const handleSetMode = useCallback((m: EditorMode) => {
|
||||
if (m === "preview" || m === "replay_preview") {
|
||||
onModeChange(m);
|
||||
}
|
||||
}, [onModeChange]);
|
||||
|
||||
|
||||
// Popup PinnedWikiPopup rows
|
||||
const previewPinnedWikiPopupRows = useMemo(() => {
|
||||
@@ -810,26 +670,6 @@ export default PreviewLayout;
|
||||
// Helper functions
|
||||
// ==========================================
|
||||
|
||||
function snapshotWikiToWiki(snapshot: WikiSnapshot, wikiCache: Record<string, Wiki>, projectId: string): Wiki {
|
||||
if (typeof snapshot.doc === "string") {
|
||||
return {
|
||||
id: snapshot.id,
|
||||
project_id: projectId,
|
||||
title: snapshot.title,
|
||||
slug: snapshot.slug ?? null,
|
||||
content: snapshot.doc || "",
|
||||
};
|
||||
}
|
||||
|
||||
return wikiCache[snapshot.id] || {
|
||||
id: snapshot.id,
|
||||
project_id: projectId,
|
||||
title: snapshot.title,
|
||||
slug: snapshot.slug ?? null,
|
||||
content: "",
|
||||
};
|
||||
}
|
||||
|
||||
function extractWikiBlockquoteText(content: string | null | undefined): string {
|
||||
if (!content) return "";
|
||||
|
||||
@@ -850,22 +690,6 @@ function extractWikiBlockquoteText(content: string | null | undefined): string {
|
||||
.trim();
|
||||
}
|
||||
|
||||
function pushUniqueString(target: Record<string, string[]>, key: string, value: string) {
|
||||
if (!target[key]) {
|
||||
target[key] = [value];
|
||||
return;
|
||||
}
|
||||
if (!target[key].includes(value)) {
|
||||
target[key].push(value);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRelationArrays(target: Record<string, string[]>) {
|
||||
for (const key of Object.keys(target)) {
|
||||
target[key] = Array.from(new Set(target[key]));
|
||||
}
|
||||
}
|
||||
|
||||
function computeFixedPopupPosition(rect: DOMRect, width: number, height: number) {
|
||||
const margin = 12;
|
||||
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 1440;
|
||||
@@ -880,127 +704,3 @@ function computeFixedPopupPosition(rect: DOMRect, width: number, height: number)
|
||||
|
||||
return { top, left };
|
||||
}
|
||||
|
||||
function buildPreviewRelationIndex(options: {
|
||||
draft: FeatureCollection;
|
||||
entities: Entity[];
|
||||
wikis: WikiSnapshot[];
|
||||
entityWikiLinks: EntityWikiLinkSnapshot[];
|
||||
wikiCache: Record<string, Wiki>;
|
||||
projectId: string;
|
||||
}): PreviewRelationIndex {
|
||||
const next: PreviewRelationIndex = {
|
||||
entitiesById: {},
|
||||
entityGeometriesById: {},
|
||||
entityWikisById: {},
|
||||
geometryEntityIds: {},
|
||||
wikiEntityIdsById: {},
|
||||
wikiEntityIdsBySlug: {},
|
||||
wikiById: {},
|
||||
wikiBySlug: {},
|
||||
};
|
||||
|
||||
for (const entity of options.entities || []) {
|
||||
const id = String(entity?.id || "").trim();
|
||||
if (!id) continue;
|
||||
next.entitiesById[id] = entity;
|
||||
}
|
||||
|
||||
const wikiMap = new Map<string, Wiki>();
|
||||
for (const wikiSnapshot of options.wikis || []) {
|
||||
if (!wikiSnapshot || wikiSnapshot.operation === "delete") continue;
|
||||
const wiki = snapshotWikiToWiki(wikiSnapshot, options.wikiCache, options.projectId);
|
||||
if (!wiki?.id) continue;
|
||||
next.wikiById[wiki.id] = wiki;
|
||||
const slug = String(wiki.slug || "").trim();
|
||||
if (slug) next.wikiBySlug[slug] = wiki;
|
||||
}
|
||||
|
||||
for (const feature of options.draft.features || []) {
|
||||
const geometryId = String(feature.properties.id);
|
||||
for (const entityId of normalizeFeatureEntityIds(feature)) {
|
||||
if (!next.entitiesById[entityId]) {
|
||||
next.entitiesById[entityId] = { id: entityId, name: entityId };
|
||||
}
|
||||
pushUniqueString(next.geometryEntityIds, geometryId, entityId);
|
||||
if (!next.entityGeometriesById[entityId]) {
|
||||
next.entityGeometriesById[entityId] = { type: "FeatureCollection", features: [] };
|
||||
}
|
||||
if (!next.entityGeometriesById[entityId].features.some((item) => String(item.properties.id) === geometryId)) {
|
||||
next.entityGeometriesById[entityId].features.push(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const link of options.entityWikiLinks || []) {
|
||||
if (!link || link.operation === "delete") continue;
|
||||
const entityId = String(link.entity_id || "").trim();
|
||||
const wikiId = String(link.wiki_id || "").trim();
|
||||
const entity = next.entitiesById[entityId] || null;
|
||||
const wiki = next.wikiById[wikiId] || null;
|
||||
if (!entity || !wiki) continue;
|
||||
|
||||
if (!next.entityWikisById[entityId]) next.entityWikisById[entityId] = [];
|
||||
if (!next.entityWikisById[entityId].some((item) => item.id === wiki.id)) {
|
||||
next.entityWikisById[entityId].push(wiki);
|
||||
}
|
||||
|
||||
pushUniqueString(next.wikiEntityIdsById, wiki.id, entityId);
|
||||
const slug = String(wiki.slug || "").trim();
|
||||
if (slug) pushUniqueString(next.wikiEntityIdsBySlug, slug, entityId);
|
||||
}
|
||||
|
||||
normalizeRelationArrays(next.geometryEntityIds);
|
||||
normalizeRelationArrays(next.wikiEntityIdsById);
|
||||
normalizeRelationArrays(next.wikiEntityIdsBySlug);
|
||||
return next;
|
||||
}
|
||||
|
||||
function hasPlayableReplaySteps(replay: BattleReplay | null | undefined) {
|
||||
return Boolean(
|
||||
replay?.detail?.some((stage) => Array.isArray(stage?.steps) && stage.steps.length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
function buildEntityLabelContextDraft(draft: FeatureCollection, entities: Entity[]): FeatureCollection {
|
||||
if (!draft.features.length) return draft;
|
||||
|
||||
const entityById = new globalThis.Map<string, Entity>();
|
||||
for (const entity of entities || []) {
|
||||
const id = String(entity?.id || "").trim();
|
||||
if (!id) continue;
|
||||
entityById.set(id, entity);
|
||||
}
|
||||
|
||||
return {
|
||||
...draft,
|
||||
features: draft.features.map((feature) => {
|
||||
const entityIds = normalizeFeatureEntityIds(feature);
|
||||
if (!entityIds.length) return feature;
|
||||
|
||||
const candidates = entityIds.map((id) => {
|
||||
const entity = entityById.get(id) || null;
|
||||
const name = String(entity?.name || id).trim();
|
||||
if (!name) return null;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
time_start: normalizeTimelineYearValue(entity?.time_start),
|
||||
time_end: normalizeTimelineYearValue(entity?.time_end),
|
||||
};
|
||||
}).filter((candidate) => candidate !== null);
|
||||
|
||||
return {
|
||||
...feature,
|
||||
properties: {
|
||||
...feature.properties,
|
||||
entity_id: entityIds[0] || null,
|
||||
entity_ids: entityIds,
|
||||
entity_name: candidates[0]?.name || null,
|
||||
entity_names: candidates.map((candidate) => candidate.name),
|
||||
entity_label_candidates: candidates,
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ export const POINT_GEOTYPE_ICON_PATHS: Partial<Record<PointGeotypeId, string>> =
|
||||
port: "/images/mapIcon/point/port.png",
|
||||
};
|
||||
|
||||
type PointIconVariant = "default" | "draft";
|
||||
|
||||
type PointLayerOptions = {
|
||||
iconScale?: number;
|
||||
@@ -42,8 +41,6 @@ const TYPE_MATCH_EXPR: maplibregl.ExpressionSpecification = ["coalesce", ["get",
|
||||
const SELECTED_EXPR: maplibregl.ExpressionSpecification = ["boolean", ["feature-state", "selected"], false];
|
||||
|
||||
const ICON_CANVAS_SIZE = 48;
|
||||
const DRAFT_FILL = "#ef4444";
|
||||
const DRAFT_RIM = "#7f1d1d";
|
||||
const POINT_GEOMETRY_FILTER: maplibregl.ExpressionSpecification = [
|
||||
"any",
|
||||
["==", ["geometry-type"], "Point"],
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { UIOptionName } from "@/uhm/types/projects";
|
||||
|
||||
export const REPLAY_UI_OPTIONS = [
|
||||
"timeline",
|
||||
"layer_panel",
|
||||
"zoom_panel",
|
||||
"wiki",
|
||||
"toast",
|
||||
] as const satisfies UIOptionName[];
|
||||
|
||||
export function normalizeReplayUiOption(value: unknown): UIOptionName | null {
|
||||
return REPLAY_UI_OPTIONS.includes(value as UIOptionName)
|
||||
? value as UIOptionName
|
||||
: null;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import type maplibregl from "maplibre-gl";
|
||||
import type { FeatureCollection } from "@/uhm/types/geo";
|
||||
import type {
|
||||
GeoFunctionName,
|
||||
MapFunctionName,
|
||||
NarrativeFunctionName,
|
||||
ReplayAction,
|
||||
UIOptionName,
|
||||
DialogState,
|
||||
} from "@/uhm/types/projects";
|
||||
import { mapActions } from "./mapActions";
|
||||
@@ -64,6 +60,9 @@ export const dispatchReplayAction = (
|
||||
case "set_labels_visible":
|
||||
mapActions.set_labels_visible(map, asBooleanValue(params[0], true));
|
||||
return;
|
||||
case "set_timeline_filter":
|
||||
controllers.setTimelineFilterEnabled(asBooleanValue(params[0], true));
|
||||
return;
|
||||
case "fly_to_geometries":
|
||||
mapActions.fly_to_geometries(
|
||||
map,
|
||||
|
||||
Reference in New Issue
Block a user