Skip to content

Commit

Permalink
#9433: use paginated table for mod version history
Browse files Browse the repository at this point in the history
  • Loading branch information
twschiller committed Nov 15, 2024
1 parent f3613fd commit 41a94db
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ interface TableProps<
actions?: Actions;
initialPageSize?: number;
rowProps?: (row: UnknownObject) => RowProps;
showSearchFilter: boolean;
showSearchFilter?: boolean;

/**
* Force the table to show a specific record. This can be a function that returns true for the record to show, or
Expand All @@ -57,6 +57,11 @@ interface TableProps<
* tables mounted, this will cause an infinite history loop.
*/
syncURL?: boolean;

/**
* Message to display when there are no records to show.
*/
emptyMessage?: string;
}

const SearchFilter: React.FunctionComponent<{
Expand Down Expand Up @@ -143,7 +148,8 @@ function PaginatedTable<
initialPageSize = 10,
syncURL = false,
rowProps,
showSearchFilter,
emptyMessage = "No records found.",
showSearchFilter = true,
forceShowRecord,
}: TableProps<Row, Actions>): React.ReactElement {
const history = useHistory();
Expand Down Expand Up @@ -299,7 +305,7 @@ function PaginatedTable<
{rows.length === 0 && (
<tr>
<td colSpan={5} className="text-muted">
No records found.
{emptyMessage}
</td>
</tr>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import React, { useMemo } from "react";
import { useSelector } from "react-redux";
import { selectActiveModId } from "@/pageEditor/store/editor/editorSelectors";
import { assertNotNullish } from "@/utils/nullishUtils";
import { Card, Container, Table } from "react-bootstrap";
import { Card, Container } from "react-bootstrap";
import styles from "@/pageEditor/tabs/modVariablesDefinition/ModVariablesDefinitionEditor.module.scss";
import ErrorBoundary from "@/components/ErrorBoundary";
import {
Expand All @@ -33,10 +33,53 @@ import AsyncStateGate from "@/components/AsyncStateGate";
import { dateFormat } from "@/utils/stringUtils";
import { mergeAsyncState, valueToAsyncState } from "@/utils/asyncStateUtils";
import { isInternalRegistryId } from "@/utils/registryUtils";
import type { Column } from "react-table";
import PaginatedTable from "@/components/paginatedTable/PaginatedTable";
import { MemoryRouter } from "react-router";

function useModPackageVersionsQuery(
modId: RegistryId,
): AsyncState<PackageVersionDeprecated[] | string> {
type TableColumn = Column<PackageVersionDeprecated>;

const COLUMNS: TableColumn[] = [
{
Header: "Version",
accessor: "version",
},
{
Header: "Timestamp",
accessor: "updated_at",
Cell({ value }) {
return <>{dateFormat.format(Date.parse(value))}</>;
},
},
{
Header: "Updated By",
accessor: "updated_by",
Cell({ value }) {
const { email } = value ?? {};
return email ? (
<a href={`mailto:${email}`}>{email}</a>
) : (
<span className="text-muted">Unknown</span>
);
},
},
{
Header: "Message",
accessor: "message",
Cell({ value }) {
return value ? (
<>{value}</>
) : (
<span className="text-muted">No message provided</span>
);
},
},
];

function useModPackageVersionsQuery(modId: RegistryId): AsyncState<{
data: PackageVersionDeprecated[];
message: string | undefined;
}> {
// Lookup the surrogate key for the package
const editablePackagesQuery = useGetEditablePackagesQuery(undefined, {
skip: isInternalRegistryId(modId),
Expand All @@ -57,45 +100,29 @@ function useModPackageVersionsQuery(

return useMemo(() => {
if (isInternalRegistryId(modId)) {
return valueToAsyncState("Version History unavailable for unsaved mods");
return valueToAsyncState({
data: [],
message: "Version History unavailable for unsaved mods",
});
}

if (editablePackage) {
return packageVersionsQuery;
return mergeAsyncState(
packageVersionsQuery,
(data: PackageVersionDeprecated[]) => ({
data,
message: undefined,
}),
);
}

return mergeAsyncState(
editablePackagesQuery,
() => "Version History requires mod write permission",
);
return mergeAsyncState(editablePackagesQuery, () => ({
data: [],
message: "Viewing Version History requires mod write permission",
}));
}, [modId, editablePackage, packageVersionsQuery, editablePackagesQuery]);
}

const PackageVersionRow: React.VFC<{ version: PackageVersionDeprecated }> = ({
version,
}) => {
const email = version.updated_by?.email;

return (
<tr>
<td>{version.version}</td>
<td>{dateFormat.format(Date.parse(version.updated_at))}</td>
<td>
{email ? (
<a href={`mailto:${email}`}>{email}</a>
) : (
<span className="text-muted">Unknown</span>
)}
</td>
<td>
{version.message ?? (
<span className="text-muted">No message provided</span>
)}
</td>
</tr>
);
};

const ModVersionHistory: React.FC = () => {
const modId = useSelector(selectActiveModId);

Expand All @@ -108,34 +135,23 @@ const ModVersionHistory: React.FC = () => {
<ErrorBoundary>
<Card>
<Card.Header>Version History</Card.Header>
<Card.Body>
<AsyncStateGate state={packageVersionsQuery}>
{({ data }) => (
<Table>
<thead>
<tr>
<th>Version</th>
<th>Timestamp</th>
<th>Updated By</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{typeof data === "object" ? (
data.map((version) => (
<PackageVersionRow key={version.id} version={version} />
))
) : (
<tr>
<td colSpan={4} className="text-muted">
{data}
</td>
</tr>
)}
</tbody>
</Table>
)}
</AsyncStateGate>
<Card.Body className="p-0">
<MemoryRouter>
<AsyncStateGate state={packageVersionsQuery}>
{({ data: { data, message } }) => (
// PaginatedTable includes a useLocation call because it supports syncing the page number with the URL
// We're not using that feature here, but still need to ensure it's wrapped in a Router so the
// useLocation call doesn't error
<MemoryRouter>
<PaginatedTable
columns={COLUMNS}
data={data}
emptyMessage={message}
/>
</MemoryRouter>
)}
</AsyncStateGate>
</MemoryRouter>
</Card.Body>
</Card>
</ErrorBoundary>
Expand Down

0 comments on commit 41a94db

Please sign in to comment.