feat(lored,user): "pinned" repositories feature on user profile overview

This commit is contained in:
Guz
2025-09-30 21:35:12 -03:00
parent ca2a695a64
commit 6b8ecf8381
5 changed files with 169 additions and 1 deletions

View File

@@ -229,6 +229,31 @@ func prepareUserProfileTabData(ctx *context.Context, profileDbRepo *repo_model.R
} }
} }
// HACK(contact@guz.one): "Pinned" repositories feature.
//
// Pinned user repositories is based with whether or not the owner starred their
// own repository. This method was choose so we don't have any incompatibility
// with upstream's database, since making something similar with GitHub's pinned
// repositories feature would need a new column or table to store said information.
//
// Maybe we could in the future properly implement this feature, if upstream wants
// this feature also, if not, it is not worth it.
repos, count, err = repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: 6,
Page: 0,
},
Actor: ctx.Doer,
OwnerID: ctx.ContextUser.ID,
OrderBy: db.SearchOrderBy(fmt.Sprintf("%s, %s", db.SearchOrderByStarsReverse, db.SearchOrderByRecentUpdated)),
Private: ctx.IsSigned,
StarredByID: ctx.ContextUser.ID,
})
if err != nil {
ctx.ServerError("SearchRepository", err)
return
}
// prepare heatmap data // prepare heatmap data
if setting.Service.EnableUserHeatmap { if setting.Service.EnableUserHeatmap {
data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer) data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)

View File

@@ -0,0 +1,64 @@
<div class="grid-list">
{{range .Repos}}
<div class="grid-item">
<div class="grid-item-main">
<div class="grid-item-header">
<div class="grid-item-title">
<div class="grid-item-leading">
{{if $.ShowRepoOwnerAvatar}}
{{ctx.AvatarUtils.Avatar .Owner 24}}
{{else}}
{{template "repo/icon" .}}
{{end}}
</div>
{{if or (and $.ShowRepoOwnerOnList .Owner) (ne .OwnerID $.ContextUser.ID)}}
<a class="text primary name" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
{{end}}
<a class="text primary name" href="{{.Link}}">{{.Name}}</a>
<span class="label-list">
{{if .IsArchived}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.archived"}}</span>
{{end}}
{{if .IsPrivate}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.private"}}</span>
{{else}}
{{if .Owner.Visibility.IsPrivate}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.internal"}}</span>
{{end}}
{{end}}
{{if .IsTemplate}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.template"}}</span>
{{end}}
{{if eq .ObjectFormatName "sha256"}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.sha256"}}</span>
{{end}}
</span>
</div>
<div class="grid-item-trailing muted-links">
{{if .PrimaryLanguage}}
<a class="flex-text-inline" href="?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
<i class="color-icon tw-mr-2" style="background-color: {{.PrimaryLanguage.Color}}"></i>
{{.PrimaryLanguage.Language}}
</a>
{{end}}
</div>
</div>
{{$description := .DescriptionHTML ctx}}
{{if $description}}
<div class="flex-item-body">{{$description}}</div>
{{end}}
{{if .Topics}}
<div class="label-list">
{{range .Topics}}
{{if ne . ""}}<a class="ui label" href="{{AppSubUrl}}/explore/repos?q={{.}}&topic=1">{{.}}</a>{{end}}
{{end}}
</div>
{{end}}
</div>
</div>
{{else}}
<div>
{{ctx.Locale.Tr "search.no_results"}}
</div>
{{end}}
</div>

View File

@@ -27,7 +27,12 @@
{{template "repo/user_cards" .}} {{template "repo/user_cards" .}}
{{else if eq .TabName "overview"}} {{else if eq .TabName "overview"}}
{{if .HasUserProfileReadme}} {{if .HasUserProfileReadme}}
<div id="readme_profile" class="render-content markup">{{.ProfileReadmeContent}}</div> <div id="readme_profile" class="render-content markup{{if .Repos}} tw-mb-4{{end}}">
{{.ProfileReadmeContent}}
</div>
{{end}}
{{if .Repos}}
{{template "shared/repo/grid" .}}
{{end}} {{end}}
{{if (and (or .HeatmapData .Feeds) (not .ContextUser.KeepActivityPrivate))}} {{if (and (or .HeatmapData .Feeds) (not .ContextUser.KeepActivityPrivate))}}
<div class="divider"></div> <div class="divider"></div>

View File

@@ -29,6 +29,7 @@
@import "./modules/flexcontainer.css"; @import "./modules/flexcontainer.css";
@import "./shared/flex-list.css"; @import "./shared/flex-list.css";
@import "./shared/grid-list.css";
@import "./shared/milestone.css"; @import "./shared/milestone.css";
@import "./shared/repoorg.css"; @import "./shared/repoorg.css";
@import "./shared/settings.css"; @import "./shared/settings.css";

View File

@@ -0,0 +1,73 @@
.grid-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
gap: 1rem;
}
.grid-item {
border-radius: 0.5rem;
padding: 1em;
display: grid;
gap: 8px;
align-items: flex-start;
border: 1px solid var(--color-secondary);
background: var(--color-box-body);
grid-template-rows: 1fr min-content;
}
.grid-item .grid-item-main {
display: grid;
gap: 4px;
}
.grid-item .grid-item-header {
display: flex;
gap: 0.25rem;
justify-content: space-between;
flex-wrap: wrap;
}
.grid-item .grid-item-trailing {
display: flex;
gap: 0.5rem;
align-items: center;
flex-grow: 0;
flex-wrap: wrap;
justify-content: end;
}
.grid-item .grid-item-title {
display: inline-flex;
flex-wrap: wrap;
align-items: center;
gap: 0.25rem;
max-width: 100%;
color: var(--color-text);
font-size: 16px;
font-weight: var(--font-weight-semibold);
word-break: break-word;
min-width: 0;
}
.grid-item .grid-item-body {
font-size: 13px;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.25rem;
color: var(--color-text-light-2);
word-break: break-word;
}
.grid-item .grid-item-footer {
grid-column: 1 / -1;
}
.grid-item .grid-item-trailing {
display: flex;
gap: 0.5rem;
align-items: center;
flex-grow: 0;
flex-wrap: wrap;
justify-content: end;
}