Gitfed
bastien-mrq/gitfed / cmd / gitfed-web / handlers_home.go
package main

import (
	"bytes"
	"html/template"
	"net/http"
	"sort"
	"strings"

	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/i18n"
	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/store"
)

var exploreTpl = newTpl("explore", `
{{.Flash}}
<div class="gf-page-head"><h1>{{t .Lang "explore.title"}}</h1><span class="count">{{len .Repos}} {{t .Lang "explore.repo_count"}}</span></div>
<p class="gf-page-sub">{{t .Lang "explore.subtitle"}}</p>

<form class="gf-search" method="get" action="/explore">
  {{if .Topic}}<input type="hidden" name="topic" value="{{.Topic}}">{{end}}
  {{icon "search"}}
  <input type="search" name="q" value="{{.Query}}" placeholder="{{t .Lang "explore.search_placeholder"}}">
</form>

{{if .AllTopics}}
<div class="gf-topics-row">
  <span class="label">{{t .Lang "explore.topics_label"}}</span>
  {{range .AllTopics}}
    {{if eq . $.Topic}}
    <a class="chip active" href="/explore{{if $.Query}}?q={{$.Query}}{{end}}">{{.}} {{icon "close"}}</a>
    {{else}}
    <a class="chip" href="/explore?topic={{.}}{{if $.Query}}&q={{$.Query}}{{end}}">{{.}}</a>
    {{end}}
  {{end}}
</div>
{{end}}

<div class="gf-card">
{{if .Repos}}
  <div class="gf-repo-list">
  {{range .Repos}}
    <div class="gf-repo-row">
      <div class="gf-repo-icon">{{icon "folder"}}</div>
      <div class="gf-repo-main">
        <div class="name"><a href="/r/{{.Name}}">{{.Name}}</a> <span class="owner">— {{if localUser .Owner $.Domain}}<a href="/u/{{localUser .Owner $.Domain}}">{{.Owner}}</a>{{else}}{{.Owner}}{{end}}</span></div>
        {{if .Topics}}<div class="topics">{{range .Topics}}<a href="/explore?topic={{.}}">{{.}}</a>{{end}}</div>{{end}}
      </div>
      <span class="badge trusted">{{t $.Lang "common.public"}}</span>
    </div>
  {{end}}
  </div>
{{else}}
  <div class="gf-empty">
    {{icon "search"}}
    <p><strong>{{t .Lang "explore.empty_title"}}</strong></p>
    <p class="muted">{{t .Lang "explore.empty_hint"}}</p>
    <a class="clear" href="/explore">{{t .Lang "explore.clear"}} →</a>
  </div>
{{end}}
</div>
`)

func (s *server) handleExplore(w http.ResponseWriter, r *http.Request) {
	topic := r.URL.Query().Get("topic")
	query := strings.TrimSpace(r.URL.Query().Get("q"))
	lang := s.lang(r)

	repos, err := s.ops.ListRepos()
	if err != nil {
		s.serverError(w, r, err)
		return
	}

	topicSet := map[string]bool{}
	var visible []store.Repo
	needle := strings.ToLower(query)
	for _, repo := range repos {
		if !repo.Public {
			continue
		}
		for _, t := range repo.Topics {
			topicSet[t] = true
		}
		if topic != "" && !containsTopic(repo.Topics, topic) {
			continue
		}
		if needle != "" && !strings.Contains(strings.ToLower(repo.Name), needle) {
			continue
		}
		visible = append(visible, repo)
	}
	allTopics := make([]string, 0, len(topicSet))
	for t := range topicSet {
		allTopics = append(allTopics, t)
	}
	sort.Strings(allTopics)

	var buf bytes.Buffer
	_ = exploreTpl.Execute(&buf, struct {
		Repos                      []store.Repo
		AllTopics                  []string
		Topic, Query, Lang, Domain string
		Flash                      template.HTML
	}{visible, allTopics, topic, query, string(lang), s.domain, flash(r)})
	s.render(w, r, i18n.T(lang, "explore.title"), "explore", template.HTML(buf.String()))
}

func containsTopic(topics []string, topic string) bool {
	for _, t := range topics {
		if t == topic {
			return true
		}
	}
	return false
}