Feature: Platform Filter (#562)

Co-authored-by: MiniDigger <admin@benndorf.dev>
This commit is contained in:
AwesomeDude091 2021-12-08 08:43:21 +01:00 committed by GitHub
parent c574dc19ad
commit 8a175b56aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 97 additions and 14 deletions

View File

@ -48,8 +48,8 @@
<v-row justify="center" align="center" style="position: relative; top: 5px; padding-bottom: 10px">
<v-subheader>Platforms</v-subheader>
</v-row>
<v-list-item-group>
<v-list-item v-for="(platform, i) in platforms" :key="i" active-class="">
<v-list-item-group v-model="filters.platforms" multiple>
<v-list-item v-for="platform in $store.getters.visiblePlatforms" :key="platform.enumName" :value="platform.name">
<v-list-item-icon>
<v-icon v-text="`$vuetify.icons.${platform.name.toLowerCase()}`" />
</v-list-item-icon>
@ -140,6 +140,7 @@ export default class Home extends HangarComponent {
limit: this.options.itemsPerPage,
offset: (this.options.page - 1) * this.options.itemsPerPage,
category: this.filters.categories,
platform: this.filters.platforms,
};
if (this.filters.search != null && this.filters.search.length > 0) {
requestOptions.q = this.filters.search;

View File

@ -102,6 +102,7 @@ export const actions: ActionTree<RootState, RootState> = {
export const getters: GetterTree<RootState, RootState> = {
visibleCategories: (state: RootState) => Array.from(state.projectCategories.values()).filter((value) => value.visible),
visiblePlatforms: (state: RootState) => Array.from(state.platforms.values()).filter((value) => value.visible),
};
function convertToMap<E, T>(values: T[], toStringFunc: (value: T) => string): Map<E, T> {

View File

@ -30,6 +30,7 @@ declare module 'hangar-internal' {
url: string;
tagColor: TagColor;
possibleVersions: string[];
visible: boolean;
}
interface IVisibility {

View File

@ -3,10 +3,7 @@ package io.papermc.hangar.controller.api.v1;
import io.papermc.hangar.HangarComponent;
import io.papermc.hangar.controller.api.v1.interfaces.IProjectsController;
import io.papermc.hangar.controller.extras.pagination.annotations.ApplicableFilters;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectAuthorFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectCategoryFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectQueryFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectTagFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.*;
import io.papermc.hangar.model.api.PaginatedResult;
import io.papermc.hangar.model.api.User;
import io.papermc.hangar.model.api.project.DayProjectStats;
@ -53,7 +50,7 @@ public class ProjectsController extends HangarComponent implements IProjectsCont
}
@Override
@ApplicableFilters({ProjectCategoryFilter.class, ProjectAuthorFilter.class, ProjectQueryFilter.class, ProjectTagFilter.class})
@ApplicableFilters({ProjectCategoryFilter.class, ProjectPlatformFilter.class, ProjectAuthorFilter.class, ProjectQueryFilter.class, ProjectTagFilter.class})
public ResponseEntity<PaginatedResult<Project>> getProjects(String q, ProjectSortingStrategy sort, boolean orderWithRelevance, @NotNull RequestPagination pagination) {
return ResponseEntity.ok(projectsApiService.getProjects(q, sort, orderWithRelevance, pagination));
}

View File

@ -0,0 +1,69 @@
package io.papermc.hangar.controller.extras.pagination.filters.projects;
import io.papermc.hangar.controller.extras.pagination.Filter;
import io.papermc.hangar.model.common.Platform;
import org.jdbi.v3.core.statement.SqlStatement;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.NativeWebRequest;
import java.util.Arrays;
import java.util.Set;
@Component
public class ProjectPlatformFilter implements Filter<ProjectPlatformFilter.ProjectPlatformFilterInstance> {
private final ConversionService conversionService;
@Autowired
public ProjectPlatformFilter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public Set<String> getQueryParamNames() {
return Set.of("platform");
}
@Override
public String getDescription() {
return "A platform to filter for";
}
@NotNull
@Override
public ProjectPlatformFilterInstance create(NativeWebRequest webRequest) {
return new ProjectPlatformFilterInstance(conversionService.convert(webRequest.getParameterValues(getSingleQueryParam()), Platform[].class));
}
static class ProjectPlatformFilterInstance implements FilterInstance {
private final Platform[] platforms;
public ProjectPlatformFilterInstance(Platform[] platforms) {
this.platforms = platforms;
}
@Override
public void createSql(StringBuilder sb, SqlStatement<?> q) {
sb.append("AND v.platform").append(" IN (");
for (int i = 0; i < platforms.length; i++) {
sb.append(":__platform__").append(i);
if (i + 1 != platforms.length) {
sb.append(",");
}
q.bind("__platform__" + i, platforms[i]);
}
sb.append(")");
}
@Override
public String toString() {
return "ProjectCategoryFilterInstance{" +
"platforms=" + Arrays.toString(platforms) +
'}';
}
}
}

View File

@ -69,7 +69,7 @@ public interface ProjectsApiDAO {
Project getProject(String author, String slug, @Define boolean canSeeHidden, @Define @Bind Long requesterId);
@UseStringTemplateEngine
@SqlQuery("SELECT hp.id," +
@SqlQuery("SELECT DISTINCT (hp.id)," +
" hp.created_at," +
" hp.name," +
" hp.owner_name \"owner\"," +
@ -83,6 +83,7 @@ public interface ProjectsApiDAO {
" hp.watchers," +
" hp.category," +
" hp.description," +
" hp.last_updated AS dum," + // need to add this so we can use it in order by, special constraint on distinct queries
" COALESCE(hp.last_updated, hp.created_at) AS last_updated," +
" hp.visibility, " +
" exists(SELECT * FROM project_stars s WHERE s.project_id = p.id AND s.user_id = :requesterId) AS starred, " +
@ -104,7 +105,10 @@ public interface ProjectsApiDAO {
" p.donation_onetime_amounts," +
" p.donation_monthly_amounts" +
" FROM home_projects hp" +
" JOIN projects p ON hp.id = p.id" +
" JOIN projects p on hp.id = p.id" +
" LEFT JOIN project_versions pv on p.id = pv.project_id " +
" LEFT JOIN project_version_platform_dependencies pvpd on pv.id = pvpd.version_id " +
" LEFT JOIN platform_versions v on pvpd.platform_version_id = v.id " +
" WHERE true <filters>" + // Not sure how else to get here a single Where
" <if(!seeHidden)> AND (hp.visibility = 0 <if(requesterId)>OR (:requesterId = ANY(hp.project_members) AND hp.visibility != 4)<endif>) <endif> " +
" ORDER BY <orderBy> " +
@ -119,6 +123,9 @@ public interface ProjectsApiDAO {
@SqlQuery("SELECT count(DISTINCT hp.id) " +
" FROM home_projects hp" +
" JOIN projects p ON hp.id = p.id" +
" LEFT JOIN project_versions pv on p.id = pv.project_id " +
" LEFT JOIN project_version_platform_dependencies pvpd on pv.id = pvpd.version_id " +
" LEFT JOIN platform_versions v on pvpd.platform_version_id = v.id " +
" WHERE true <filters>" + // Not sure how else to get here a single Where
" <if(!seeHidden)> AND (hp.visibility = 0 <if(requesterId)>OR (<requesterId> = ANY(hp.project_members) AND hp.visibility != 4)<endif>) <endif> ")
long countProjects(@Define boolean seeHidden, @Define Long requesterId,

View File

@ -13,22 +13,24 @@ import java.util.stream.Collectors;
public enum Platform {
// NOTE: The order here should always be the order they are displayed whenever there is a list somewhere on the frontend
PAPER("Paper", Category.SERVER, TagColor.PAPER, "https://papermc.io/downloads"),
WATERFALL("Waterfall", Category.PROXY, TagColor.WATERFALL, "https://papermc.io/downloads#Waterfall"),
VELOCITY("Velocity", Category.PROXY, TagColor.VELOCITY, "https://www.velocitypowered.com/downloads");
PAPER("Paper", Category.SERVER, TagColor.PAPER, "https://papermc.io/downloads", true),
WATERFALL("Waterfall", Category.PROXY, TagColor.WATERFALL, "https://papermc.io/downloads#Waterfall", true),
VELOCITY("Velocity", Category.PROXY, TagColor.VELOCITY, "https://www.velocitypowered.com/downloads", true);
private static final Platform[] VALUES = values();
private final String name;
private final Category category;
private final TagColor tagColor;
private final boolean isVisible;
private final String url;
Platform(String name, Category category, TagColor tagColor, String url) {
Platform(String name, Category category, TagColor tagColor, String url, boolean isVisible) {
this.name = name;
this.category = category;
this.tagColor = tagColor;
this.url = url;
this.isVisible = isVisible;
}
public String getName() {
@ -42,6 +44,10 @@ public enum Platform {
return category;
}
public boolean isVisible() {
return isVisible;
}
public TagColor getTagColor() {
return tagColor;
}

View File

@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -75,7 +76,7 @@ public class ProjectsApiService extends HangarComponent {
case RECENT_DOWNLOADS : orderingFirstHalf ="hp.recent_views *"; break;
case RECENT_VIEWS: orderingFirstHalf ="hp.recent_downloads *"; break;
default:
orderingFirstHalf = " "; // Just in case and so that the ide doesnt complain
orderingFirstHalf = " "; // Just in case and so that the ide doesn't complain
}
ordering = orderingFirstHalf + relevance;
}