Skip to content

Commit

Permalink
feat: finish project pinning (closes #164)
Browse files Browse the repository at this point in the history
  • Loading branch information
MiniDigger committed Aug 18, 2024
1 parent bd4ca23 commit acb65f7
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,13 @@ public void setProjectWatching(@PathVariable("id") final long projectId, @PathVa
@ResponseStatus(HttpStatus.OK)
@RateLimit(overdraft = 10, refillTokens = 3, refillSeconds = 10)
@PermissionRequired(NamedPermission.EDIT_OWN_USER_SETTINGS)
@VisibilityRequired(type = VisibilityRequired.Type.PROJECT, args = "{#projectId}")
@PostMapping(path = "/project/{id}/pin/{state}")
public void setPinnedStatus(@PathVariable("id") final long projectId, @PathVariable final boolean state) {
@VisibilityRequired(type = VisibilityRequired.Type.PROJECT, args = "{#slug}")
@PostMapping(path = "/project/{slug}/pin/{state}")
public void setPinnedStatus(@PathVariable("slug") final String slug, @PathVariable final boolean state) {
if (state) {
this.pinnedProjectService.addPinnedProject(this.getHangarUserId(), projectId);
this.pinnedProjectService.addPinnedProject(this.getHangarUserId(), slug);
} else {
this.pinnedProjectService.removePinnedProject(this.getHangarUserId(), projectId);
this.pinnedProjectService.removePinnedProject(this.getHangarUserId(), slug);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package io.papermc.hangar.db.dao.internal.table.projects;

import io.papermc.hangar.model.api.project.ProjectCompact;
import io.papermc.hangar.model.db.projects.PinnedProjectTable;
import java.util.List;
import org.jdbi.v3.spring5.JdbiRepository;
import org.jdbi.v3.sqlobject.config.RegisterConstructorMapper;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;

@JdbiRepository
public interface PinnedProjectDAO {

@SqlUpdate("INSERT INTO pinned_user_projects (project_id, user_id) VALUES (:projectId, :userId) ON CONFLICT DO NOTHING")
void insert(@BindBean PinnedProjectTable pinnedProjectVersionTable);
@SqlUpdate("INSERT INTO pinned_user_projects (project_id, user_id) VALUES ((SELECT id FROM projects WHERE lower(slug) = lower(:slug)), :userId) ON CONFLICT DO NOTHING")
void insert(long userId, String slug);

@SqlUpdate("DELETE FROM pinned_user_projects WHERE project_id = :projectId AND user_id = :userId")
void delete(long projectId, long userId);
@SqlUpdate("DELETE FROM pinned_user_projects WHERE project_id = (SELECT id FROM projects WHERE lower(slug) = lower(:slug)) AND user_id = :userId")
void delete(long userId, String slug);

@SqlQuery("SELECT * FROM pinned_projects WHERE user_id = :userId")
@RegisterConstructorMapper(ProjectCompact.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public interface UsersApiDAO {
hp.last_updated,
hp.visibility,
hp.avatar,
hp.avatar_fallback
hp.avatar_fallback,
hp.description
FROM users u
JOIN project_stars ps ON u.id = ps.user_id
JOIN home_projects hp ON ps.project_id = hp.id
Expand Down Expand Up @@ -79,7 +80,8 @@ SELECT count(*)
hp.last_updated,
hp.visibility,
hp.avatar,
hp.avatar_fallback
hp.avatar_fallback,
hp.description
FROM users u
JOIN project_watchers pw ON u.id = pw.user_id
JOIN home_projects hp ON pw.project_id = hp.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.papermc.hangar.model.api.project.settings.ProjectSettings;
import io.papermc.hangar.model.common.projects.Category;
import io.papermc.hangar.model.common.projects.Visibility;
import io.papermc.hangar.util.AvatarUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.OffsetDateTime;
import org.jdbi.v3.core.enums.EnumByOrdinal;
Expand All @@ -12,34 +11,25 @@

public class Project extends ProjectCompact {

@Schema(description = "The short description of the project")
private final String description;
@Schema(description = "Information about your interactions with the project")
private final UserActions userActions;
@Schema(description = "The settings of the project")
private final ProjectSettings settings;

@JdbiConstructor
public Project(final OffsetDateTime createdAt, final long id, final String name, @Nested final ProjectNamespace namespace, @Nested final ProjectStats stats, @EnumByOrdinal final Category category, final String description, final OffsetDateTime lastUpdated, @EnumByOrdinal final Visibility visibility, @Nested final UserActions userActions, @Nested final ProjectSettings settings, final String avatar, final String avatarFallback) {
super(createdAt, id, name, namespace, stats, category, lastUpdated, visibility);
this.description = description;
super(createdAt, id, name, namespace, description, stats, category, lastUpdated, visibility, avatar, avatarFallback);
this.userActions = userActions;
this.settings = settings;
this.setAvatarUrl(AvatarUtil.avatarUrl(avatar, avatarFallback));
}

public Project(final Project other) {
super(other.createdAt, other.id, other.name, other.namespace, other.stats, other.category, other.lastUpdated, other.visibility);
this.description = other.description;
super(other.createdAt, other.id, other.name, other.namespace, other.description, other.stats, other.category, other.lastUpdated, other.visibility, null, null);
this.userActions = other.userActions;
this.settings = other.settings;
this.avatarUrl = other.avatarUrl;
}

public String getDescription() {
return this.description;
}

public UserActions getUserActions() {
return this.userActions;
}
Expand All @@ -51,7 +41,6 @@ public ProjectSettings getSettings() {
@Override
public String toString() {
return "Project{" +
"description='" + this.description + '\'' +
", lastUpdated=" + this.lastUpdated +
", userActions=" + this.userActions +
", settings=" + this.settings +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import io.papermc.hangar.model.Visible;
import io.papermc.hangar.model.common.projects.Category;
import io.papermc.hangar.model.common.projects.Visibility;
import io.papermc.hangar.util.AvatarUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.OffsetDateTime;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.mapper.Nested;

Expand All @@ -29,16 +31,20 @@ public class ProjectCompact extends Model implements Named, Visible {
protected final Visibility visibility;
@Schema(description = "The url to the project's icon")
protected String avatarUrl;
@Schema(description = "The short description of the project")
protected final String description;

public ProjectCompact(final OffsetDateTime createdAt, final long id, final String name, @Nested final ProjectNamespace namespace, @Nested final ProjectStats stats, @EnumByOrdinal final Category category, final OffsetDateTime lastUpdated, @EnumByOrdinal final Visibility visibility) {
public ProjectCompact(final OffsetDateTime createdAt, final long id, final String name, @Nested final ProjectNamespace namespace, final String description, @Nested final ProjectStats stats, @EnumByOrdinal final Category category, final OffsetDateTime lastUpdated, @EnumByOrdinal final Visibility visibility, @Nullable final String avatar, @Nullable final String avatarFallback) {
super(createdAt);
this.id = id;
this.name = name;
this.namespace = namespace;
this.description = description;
this.stats = stats;
this.category = category;
this.visibility = visibility;
this.lastUpdated = lastUpdated;
this.setAvatarUrl(AvatarUtil.avatarUrl(avatar, avatarFallback));
}

@Override
Expand All @@ -58,6 +64,10 @@ public Category getCategory() {
return this.category;
}

public String getDescription() {
return this.description;
}

public OffsetDateTime getLastUpdated() {
return this.lastUpdated;
}
Expand Down Expand Up @@ -85,6 +95,7 @@ public String toString() {
return "ProjectCompact{" +
"name='" + this.name + '\'' +
", namespace=" + this.namespace +
", description=" + this.description +
", stats=" + this.stats +
", category=" + this.category +
", visibility=" + this.visibility +
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,6 @@ public void supplyAvatarUrl(final User user) {
}

public List<ProjectCompact> getUserPinned(final String userName) {
final List<ProjectCompact> pinnedVersions = this.pinnedProjectService.getPinnedVersions(this.getUserRequired(userName, this.usersDAO::getUser, HangarUser.class).getId());
// TODO rewrite avatar fetching
pinnedVersions.forEach(p -> p.setAvatarUrl(this.avatarService.getProjectAvatarUrl(p.getId(), p.getNamespace().getOwner())));
return pinnedVersions;
return this.pinnedProjectService.getPinnedProjects(this.getUserRequired(userName, this.usersDAO::getUser, HangarUser.class).getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.papermc.hangar.HangarComponent;
import io.papermc.hangar.db.dao.internal.table.projects.PinnedProjectDAO;
import io.papermc.hangar.model.api.project.ProjectCompact;
import io.papermc.hangar.model.db.projects.PinnedProjectTable;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -18,15 +17,15 @@ public PinnedProjectService(final PinnedProjectDAO pinnedProjectDAO) {
this.pinnedProjectDAO = pinnedProjectDAO;
}

public void addPinnedProject(final long userId, final long projectId) {
this.pinnedProjectDAO.insert(new PinnedProjectTable(projectId, userId));
public void addPinnedProject(final long userId, final String slug) {
this.pinnedProjectDAO.insert(userId, slug);
}

public void removePinnedProject(final long userId, final long projectId) {
this.pinnedProjectDAO.delete(projectId, userId);
public void removePinnedProject(final long userId, final String slug) {
this.pinnedProjectDAO.delete(userId, slug);
}

public List<ProjectCompact> getPinnedVersions(final long userid) {
public List<ProjectCompact> getPinnedProjects(final long userid) {
return this.pinnedProjectDAO.pinnedProjects(userid);
}
}
15 changes: 11 additions & 4 deletions backend/src/main/resources/db/migration/R__04_pinned_views.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
DROP VIEW IF EXISTS pinned_versions;

DROP VIEW IF EXISTS pinned_projects;

CREATE OR REPLACE VIEW pinned_versions AS
Expand All @@ -19,7 +20,7 @@ CREATE OR REPLACE VIEW pinned_versions AS
JOIN platform_versions plv ON plv.id = pvpd.platform_version_id
WHERE pvpd.version_id = pv.id
ORDER BY plv.platform
) AS platforms,
) AS platforms,
'version' AS type,
pv.project_id
FROM pinned_project_versions ppv
Expand All @@ -34,7 +35,7 @@ CREATE OR REPLACE VIEW pinned_versions AS
JOIN platform_versions plv ON plv.id = pvpd.platform_version_id
WHERE pvpd.version_id = pv.id
ORDER BY plv.platform
) AS platforms,
) AS platforms,
'channel' AS type,
pv.project_id
FROM project_channels pc
Expand Down Expand Up @@ -62,7 +63,10 @@ CREATE OR REPLACE VIEW pinned_projects AS
name,
created_at,
license_type,
last_updated
description,
last_updated,
avatar,
avatar_fallback
FROM (SELECT pp.id,
pp.user_id,
pp.project_id,
Expand All @@ -81,7 +85,10 @@ CREATE OR REPLACE VIEW pinned_projects AS
p.name,
p.created_at,
p.license_type,
hp.last_updated
p.description,
hp.last_updated,
hp.avatar,
hp.avatar_fallback
FROM pinned_user_projects pp
JOIN home_projects hp ON hp.id = pp.project_id
JOIN projects p ON pp.project_id = p.id) AS pvs;
21 changes: 17 additions & 4 deletions frontend/src/components/projects/ProjectCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@
import { type Project, type ProjectCompact, Tag, Visibility } from "~/types/backend";
const i18n = useI18n();
const router = useRouter();
defineProps<{
const props = defineProps<{
project: Project | ProjectCompact;
canEdit?: boolean;
pinned?: boolean;
}>();
async function togglePin() {
await useInternalApi(`/projects/project/${props.project.namespace.slug}/pin/${!props.pinned}`, "POST").catch(handleRequestError);
router.go(0); // I am lazy
}
</script>

<template>
Expand All @@ -14,7 +22,7 @@ defineProps<{
:class="{
'!border-red-500 border-1px': project.visibility === Visibility.SoftDelete,
'!border-gray-300 !dark:border-gray-700 border-1px': project.visibility === Visibility.Public,
'hover:background-card': true,
'hover:background-card group': true,
}"
>
<div class="flex space-x-4">
Expand All @@ -34,11 +42,16 @@ defineProps<{
</object>
</span>
</h3>
<IconMdiCancel v-show="project.visibility === Visibility.SoftDelete" />
<IconMdiEyeOff v-show="project.visibility !== Visibility.Public" />
<IconMdiCancel v-if="project.visibility === Visibility.SoftDelete" />
<IconMdiEyeOff v-if="project.visibility !== Visibility.Public" />
<button v-if="canEdit" @click.prevent="togglePin" :title="'Toggle pinned status for project ' + project.namespace.slug">
<IconMdiPinOff class="hidden group-hover:block" v-if="pinned" />
<IconMdiPin v-else class="hidden group-hover:block" />
</button>
</div>

<div v-if="'description' in project && project.description" class="mb-1">{{ project.description }}</div>
<div v-else />
<div class="inline-flex items-center text-gray-500 dark:text-gray-400 lt-sm:hidden">
<CategoryLogo :category="project.category" :size="16" class="mr-1" />
{{ i18n.t("project.category." + project.category) }}
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/projects/ProjectList.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script setup lang="ts">
import type { PaginatedResultProject } from "~/types/backend";
import type { PaginatedResultProject, ProjectCompact } from "~/types/backend";
const i18n = useI18n();
defineProps<{
projects?: PaginatedResultProject;
resetAnchor?: Element;
loading?: boolean;
canEdit?: boolean;
pinned?: ProjectCompact[];
}>();
const emit = defineEmits<{
Expand All @@ -32,7 +34,7 @@ defineExpose({ updatePage });
@update:page="emitPageUpdate"
>
<template #default="{ item }">
<ProjectCard :project="item" />
<ProjectCard :project="item" :can-edit :pinned="pinned?.some((p) => p.namespace.slug === item.namespace.slug)" />
</template>
</Pagination>
<div v-if="projects?.result?.length === 0">{{ i18n.t("hangar.projectSearch.noProjects") }}</div>
Expand Down
Loading

0 comments on commit acb65f7

Please sign in to comment.