Skip to content

Commit

Permalink
Fix links being in the wrong order when no tags are selected
Browse files Browse the repository at this point in the history
  • Loading branch information
s-thom committed Apr 24, 2022
1 parent 827c001 commit 19dbcc9
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 24 deletions.
24 changes: 24 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",

"configurations": [
{
"command": "npm run dev",
"name": "Run npm run dev",
"request": "launch",
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"name": "Attach by Process ID",
"processId": "${command:PickProcess}",
"request": "attach",
"skipFiles": ["<node_internals>/**"],
"outFiles": ["${workspaceFolder}/build/**/*.js", "!**/node_modules/**"],
"type": "pwa-node"
}
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Sorting when there are no tags selected now uses the creation date properly.
- Any left-over input in the Add link's tag input is treated properly.
- This fixes an issue where you could add invalid tags by submitting the form with an invalid value in the input.
- Removed "0" appearing in the common tags of the search form when using the app for the first time.
Expand Down
42 changes: 21 additions & 21 deletions app/models/link.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface LinksByTagResult extends Link {
tags: string[];
}

async function getUserLinkIDsByTags({
export async function searchUserLinks({
userId,
tags,
limit = LINK_QUERY_RESULTS_LIMIT,
Expand All @@ -18,49 +18,49 @@ async function getUserLinkIDsByTags({
tags: string[];
limit?: number;
}) {
const trueLimit = Math.max(limit, LINK_QUERY_RESULTS_LIMIT);

// Since this is a bit more of a complicated query, it's split into a couple of sections.
// It involved re-querying data and sorting in-memory, which is why there is a limit in place.
const results = await prisma.$queryRaw<LinksByTagResult[]>`
SELECT
DISTINCT l.*,
count(t.id) AS tag_count
FROM "Link" l
JOIN "_LinkToTag" ltt ON ltt."A" = l.id
JOIN "Tag" t ON t.id = ltt."B"
LEFT JOIN "Tag" t ON
t.id = ltt."B" AND
${Prisma.sql([tags.length ? "true" : "false"])}
WHERE
l."userId" = ${userId}
${Prisma.sql`AND ${
l."userId" = ${userId} AND
${Prisma.sql`${
tags.length
? Prisma.sql`t."name" IN (${Prisma.join(tags)})`
: Prisma.sql`true`
}`}
GROUP BY l.id
ORDER BY tag_count DESC, l."createdAt" DESC
LIMIT ${Math.max(limit, LINK_QUERY_RESULTS_LIMIT)}
LIMIT ${trueLimit}
OFFSET 0
`;
const sortedLinkIds = results.map((link) => link.id);

return results.map((link) => link.id);
}

export async function getUserLinksByTags({
userId,
tags,
limit,
}: {
userId: User["id"];
tags: string[];
limit?: number;
}) {
const linkIds = await getUserLinkIDsByTags({ userId, tags, limit });

// Now we have the IDs, get the objects for real.
// Yes, this is effectively querying the same information again.
const links = await prisma.link.findMany({
include: { tags: true },
where: {
userId,
id: { in: linkIds },
id: { in: sortedLinkIds },
},
});

return links.sort((a, b) => linkIds.indexOf(a.id) - linkIds.indexOf(b.id));
// The list from the findMany is not in the correct order, so it must be sorted in-memory.
links.sort(
(a, b) => sortedLinkIds.indexOf(a.id) - sortedLinkIds.indexOf(b.id)
);

return links;
}

export function getSingleLink({ id }: { id: Link["id"] }) {
Expand Down
6 changes: 3 additions & 3 deletions app/routes/__layout/links/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import React from "react";
import LinkDisplay from "~/components/LinkDisplay";
import SearchForm from "~/components/SearchForm";
import { searchParamsToFormValues } from "~/util/useSearchFormState";
import { getUserLinksByTags } from "~/models/link.server";
import { searchUserLinks } from "~/models/link.server";
import { getUserCommonTags } from "~/models/tag.server";
import { requireUserId } from "~/session.server";

export const handle = { hydrate: true };

type LoaderData = {
links: Awaited<ReturnType<typeof getUserLinksByTags>>;
links: Awaited<ReturnType<typeof searchUserLinks>>;
commonTags: string[];
};

Expand All @@ -24,7 +24,7 @@ export const loader: LoaderFunction = async ({ request }) => {

const [commonTags, links] = await Promise.all([
getUserCommonTags({ userId, exclude: tags }),
getUserLinksByTags({ userId, tags }),
searchUserLinks({ userId, tags }),
]);

return json<LoaderData>({
Expand Down

0 comments on commit 19dbcc9

Please sign in to comment.