diff --git a/components/common/sidebar/sidebar.js b/components/common/sidebar/sidebar.js index ddecc496..e66203eb 100644 --- a/components/common/sidebar/sidebar.js +++ b/components/common/sidebar/sidebar.js @@ -1,6 +1,6 @@ (() => { const mobile = matchMedia("(max-width: 767.98px)"); - for (const page of document.querySelectorAll("[data-theme-sidebar]")) { + const init = page => { let collapsed = mobile.matches; try { if (!mobile.matches) collapsed = localStorage.getItem(page.dataset.themeSidebar) === "true"; @@ -31,5 +31,13 @@ update(); }); update(); - } + }; + for (const page of document.querySelectorAll("[data-theme-sidebar]")) init(page); + // Forgejo replaces the whole notifications page element after an action; set the new one up too. + if (typeof MutationObserver === "undefined" || !document.body) return; + new MutationObserver(records => { + for (const record of records) + for (const node of record.addedNodes) + if (node instanceof Element && node.matches("[data-theme-sidebar]")) init(node); + }).observe(document.body, { childList: true, subtree: true }); })(); diff --git a/public/assets/js/issue-list.js b/public/assets/js/issue-list.js index 4f21ee3e..fc19f7c1 100644 --- a/public/assets/js/issue-list.js +++ b/public/assets/js/issue-list.js @@ -5,8 +5,11 @@ const form = page.querySelector(".issue-list-search"); const input = form?.querySelector('input[name="q"]'); if (!input) return; - input.setAttribute("aria-label", isPullList ? "Search pull requests" : "Search Issues"); - input.placeholder = isPullList ? "Search all pull requests" : "Search Issues"; + // The dashboard's milestone list reuses this layout and keeps its own translated placeholder. + if (!page.classList.contains("github-milestones")) { + input.setAttribute("aria-label", isPullList ? "Search pull requests" : "Search Issues"); + input.placeholder = isPullList ? "Search all pull requests" : "Search Issues"; + } const toolbar = page.querySelector("#issue-filters"); const filterRow = toolbar?.querySelector(".issue-list-toolbar-right"); if (filterRow) { diff --git a/scripts/assembleForgejo.ts b/scripts/assembleForgejo.ts index af9af703..616ded81 100644 --- a/scripts/assembleForgejo.ts +++ b/scripts/assembleForgejo.ts @@ -43,7 +43,7 @@ for (const directory of ["common", "repository"]) { } } -for (const file of ["organization.css", "profile-subpages.css", "user-profile.css"]) { +for (const file of ["dashboard-lists.css", "organization.css", "profile-subpages.css", "user-profile.css"]) { cpSync(join(root, "styles", "pages", file), join(css, "components", "pages", file)); } for (const file of readdirSync(join(root, "public"))) { diff --git a/styles/pages/dashboard-lists.css b/styles/pages/dashboard-lists.css new file mode 100644 index 00000000..40705017 --- /dev/null +++ b/styles/pages/dashboard-lists.css @@ -0,0 +1,144 @@ +/* The dashboard's own lists: issues, pull requests, milestones and notifications. */ + +/* The context switcher sits at the top of the sidebar instead of in a bar across the page. */ +.dashboard-list-context .secondary-nav { + padding: 0; + border: 0 !important; + background: transparent; +} + +.dashboard-list-context .ui.secondary.menu { + flex-direction: column; + align-items: stretch; + min-height: 0; + margin: 0; + padding: 0 8px; +} + +.dashboard-list-context .ui.secondary.menu > .item { + margin: 0; + padding: 6px 8px; +} + +/* Counts sit at the right edge of a sidebar entry, as in GitHub's sidebars. */ +:is(.dashboard-issue-list, .github-notifications) .theme-sidebar nav a > .theme-counter { + margin-left: auto; +} + +:is(.dashboard-issue-list, .github-notifications) .theme-sidebar nav a > span:not(.theme-counter) { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Milestones: the cards form one bordered list under the Open/Closed bar. */ +.github-milestones .milestone-list { + margin: 0; + padding: 0; + border: 1px solid var(--color-light-border); + border-top: 0; + border-radius: 0 0 var(--border-radius) var(--border-radius); + list-style: none; +} + +.github-milestones .milestone-list > .milestone-card { + margin: 0; + padding: 12px 16px; + border: 0; + border-radius: 0; +} + +.github-milestones .milestone-list > .milestone-card + .milestone-card { + border-top: 1px solid var(--color-light-border); +} + +/* Notifications */ +.github-notifications-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + min-height: 32px; + margin: 0 0 16px; +} + +.github-notifications-title { + display: flex; + align-items: center; + gap: 8px; +} + +.github-notifications-title h1 { + margin: 0; + font-size: 20px; + font-weight: 600; + line-height: 28px; +} + +.github-notifications #notification_table { + overflow: hidden; + border: 1px solid var(--color-light-border); + border-radius: var(--border-radius); +} + +.github-notifications .notifications-item { + min-height: 55px; + padding: 8px 16px !important; + background: var(--color-box-header); +} + +.github-notifications .notifications-item + .notifications-item { + border-top: 1px solid var(--color-light-border); +} + +/* Read notifications recede: no tint and a muted title. */ +.github-notifications .notifications-item[data-status="2"] { + background: transparent; +} + +.github-notifications .notifications-item[data-status="2"] .issue-title { + color: var(--color-text-light-1); +} + +.github-notifications .notifications-item:hover { + background: var(--color-hover); +} + +.github-notifications .notifications-top-row { + color: var(--color-text-light-1); + font-size: 12px; + line-height: 18px; +} + +.github-notifications .notifications-bottom-row { + padding: 0; + font-size: 14px; + line-height: 20px; +} + +.github-notifications .notifications-updated { + color: var(--color-text-light-1); + font-size: 12px; +} + +.github-notifications .notifications-buttons .btn { + display: grid; + place-items: center; + width: 28px; + height: 28px; + padding: 0; + border-radius: var(--border-radius); + color: var(--color-text-light-1); +} + +.dashboard-list-context .ui.secondary.menu > .item { + align-self: flex-start; +} + +/* Forgejo's milestone page gives the list header its own box; the shared heading has none. */ +.github-milestones .list-header.list-header-issues { + padding: 0 !important; + border: 0 !important; + background: transparent !important; +} diff --git a/templates/base/component_assets.tmpl b/templates/base/component_assets.tmpl index b42b49df..702cf655 100644 --- a/templates/base/component_assets.tmpl +++ b/templates/base/component_assets.tmpl @@ -11,6 +11,7 @@ + diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl new file mode 100644 index 00000000..f46cf036 --- /dev/null +++ b/templates/user/dashboard/issues.tmpl @@ -0,0 +1,94 @@ +{{template "base/head" .}} +{{/* + The dashboard's issue and pull request lists use the repository list's layout and classes + ("repository issue-list") so they share its styles; nothing repository-specific is rendered. +*/}} +{{$keyword := StringUtils.RemoveAll $.Keyword "is:open" "-is:open" "is:closed" "-is:closed" "is:all"}} +
+ +
+ {{template "base/alert" .}} +
+
+ +

{{if eq .ViewType "assigned"}}{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}{{else if eq .ViewType "created_by"}}{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}{{else if eq .ViewType "mentioned"}}{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}{{else if eq .ViewType "review_requested"}}{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}{{else if eq .ViewType "reviewed_by"}}{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}{{else}}{{ctx.Locale.Tr "home.issues.in_your_repos"}}{{end}}

+
+
+ +
+ +
+ +
+
+ {{template "shared/issuelist" dict "." . "listType" "dashboard"}} +
+
+ + +{{template "base/footer" .}} diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl new file mode 100644 index 00000000..50759d20 --- /dev/null +++ b/templates/user/dashboard/milestones.tmpl @@ -0,0 +1,150 @@ +{{template "base/head" .}} +{{/* The dashboard's milestone list uses the issue list's layout and classes so it shares its styles. */}} +
+ +
+ {{template "base/alert" .}} +
+
+ +

{{ctx.Locale.Tr "repo.milestones"}}

+
+
+ + +
+ {{range .Milestones}} +
  • +
    +

    + + {{.Repo.FullName}} + + {{svg "octicon-milestone" 16}} + {{.Name}} +

    +
    + {{.Completeness}}% + +
    +
    +
    +
    +
    + {{svg "octicon-issue-opened" 14}} + {{ctx.Locale.PrettyNumber .NumOpenIssues}} {{ctx.Locale.Tr "repo.issues.open_title"}} +
    +
    + {{svg "octicon-check" 14}} + {{ctx.Locale.PrettyNumber .NumClosedIssues}} {{ctx.Locale.Tr "repo.issues.closed_title"}} +
    + {{if .TotalTrackedTime}} +
    + {{svg "octicon-clock"}} + {{.TotalTrackedTime|Sec2Time}} +
    + {{end}} + {{if .UpdatedUnix}} +
    + {{svg "octicon-clock"}} + {{ctx.Locale.Tr "repo.milestones.update_ago" (DateUtils.TimeSince .UpdatedUnix)}} +
    + {{end}} +
    + {{if .IsClosed}} + {{$closedDate:= DateUtils.TimeSince .ClosedDateUnix}} + {{svg "octicon-clock" 14}} + {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}} + {{else}} + {{if .DeadlineString}} + + {{svg "octicon-calendar" 14}} + {{DateUtils.AbsoluteShort (.DeadlineString|DateUtils.ParseLegacy)}} + + {{else}} + {{svg "octicon-calendar" 14}} + {{ctx.Locale.Tr "repo.milestones.no_due_date"}} + {{end}} + {{end}} +
    +
    +
    + {{if .Content}} +
    + {{.RenderedContent}} +
    + {{end}} +
  • + {{end}} + + {{template "base/paginate" .}} +
    +
    +
    + + +{{template "base/footer" .}} diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl new file mode 100644 index 00000000..293911b0 --- /dev/null +++ b/templates/user/notification/notification_div.tmpl @@ -0,0 +1,125 @@ +
    + {{$notificationUnreadCount := call .NotificationUnreadCount}} + +
    +
    +
    + +

    {{if eq .Status 2}}{{ctx.Locale.Tr "notification.read"}}{{else}}{{ctx.Locale.Tr "notification.unread"}}{{end}}

    +
    + {{if eq .Status 1}} +
    + +
    + {{end}} +
    +
    +
    + {{if not .Notifications}} +
    + {{svg "octicon-inbox" 56 "tw-mb-4"}} + {{if eq .Status 1}} + {{ctx.Locale.Tr "notification.no_unread"}} + {{else}} + {{ctx.Locale.Tr "notification.no_read"}} + {{end}} +
    + {{else}} + {{range $notification := .Notifications}} +
    +
    + {{if .Issue}} + {{template "shared/issueicon" .Issue}} + {{else}} + {{svg "octicon-repo" 16 "text grey"}} + {{end}} +
    + +
    + {{.Repository.FullName}} {{if .Issue}}#{{.Issue.Index}}{{end}} + {{if eq .Status 3}} + {{svg "octicon-pin" 13 "text blue tw-mt-0.5 tw-ml-1"}} + {{end}} +
    +
    + + {{if .Issue}} + {{RenderRefIssueTitle $.Context .Issue.Title}} + {{else}} + {{.Repository.FullName}} + {{end}} + +
    +
    +
    + {{if .Issue}} + {{DateUtils.TimeSince .Issue.UpdatedUnix}} + {{else}} + {{DateUtils.TimeSince .UpdatedUnix}} + {{end}} +
    +
    + {{if ne .Status 3}} +
    + + + +
    + {{end}} + {{if or (eq .Status 1) (eq .Status 3)}} +
    + + + + +
    + {{else if eq .Status 2}} +
    + + + + +
    + {{end}} +
    +
    + {{end}} + {{end}} +
    +
    + {{template "base/paginate" .}} +
    +