Link editor
This commit is contained in:
parent
ba86da3374
commit
908c8b02f8
|
@ -0,0 +1,48 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/migration/types"
|
||||||
|
"git.handmade.network/hmn/hmn/src/utils"
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerMigration(AddPrimaryToLinks{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddPrimaryToLinks struct{}
|
||||||
|
|
||||||
|
func (m AddPrimaryToLinks) Version() types.MigrationVersion {
|
||||||
|
return types.MigrationVersion(time.Date(2024, 6, 30, 23, 36, 30, 0, time.UTC))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AddPrimaryToLinks) Name() string {
|
||||||
|
return "AddPrimaryToLinks"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AddPrimaryToLinks) Description() string {
|
||||||
|
return "Adds 'primary_link' field to links"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AddPrimaryToLinks) Up(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
utils.Must1(tx.Exec(ctx,
|
||||||
|
`
|
||||||
|
ALTER TABLE link
|
||||||
|
ADD COLUMN primary_link BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
`,
|
||||||
|
))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AddPrimaryToLinks) Down(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
utils.Must1(tx.Exec(ctx,
|
||||||
|
`
|
||||||
|
ALTER TABLE link
|
||||||
|
DROP COLUMN primary_link;
|
||||||
|
`,
|
||||||
|
))
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ type Link struct {
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
URL string `db:"url"`
|
URL string `db:"url"`
|
||||||
Ordering int `db:"ordering"`
|
Ordering int `db:"ordering"`
|
||||||
|
Primary bool `db:"primary_link"`
|
||||||
UserID *int `db:"user_id"`
|
UserID *int `db:"user_id"`
|
||||||
ProjectID *int `db:"project_id"`
|
ProjectID *int `db:"project_id"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -335,6 +335,7 @@ func LinkToTemplate(link *models.Link) Link {
|
||||||
ServiceName: service.Name,
|
ServiceName: service.Name,
|
||||||
Icon: service.IconName,
|
Icon: service.IconName,
|
||||||
Username: username,
|
Username: username,
|
||||||
|
Primary: link.Primary,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
<fieldset>
|
||||||
|
<legend class="flex justify-between">
|
||||||
|
<span>Links</span>
|
||||||
|
<a href="#" class="normal" onclick="addLink(event)">+ Add Link</a>
|
||||||
|
</legend>
|
||||||
|
<div class="pa3 input-group">
|
||||||
|
<div id="links" class="flex flex-column g2 relative">
|
||||||
|
<div>Primary Links</div>
|
||||||
|
<div class="drop_slot secondary_links">Secondary Links</div>
|
||||||
|
</div>
|
||||||
|
<template id="link_row">
|
||||||
|
<div class="link_row drop_slot w-100 flex flex-row items-center" data-tmpl="root">
|
||||||
|
<span class="link_handle svgicon pr3 pointer grab" onmousedown="startLinkDrag(event)">{{ svg "draggable" }}</span>
|
||||||
|
<input data-tmpl="nameInput" class="link_name mr3 w5" type="text" placeholder="Name" />
|
||||||
|
<input data-tmpl="urlInput" class="link_url flex-grow-1" type="url" placeholder="Link" />
|
||||||
|
<a class="delete_link svgicon link-normal pl3 f3" href="javascript:;" onclick="deleteLink(event)">{{ svg "delete" }}</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="link_row_dummy">
|
||||||
|
<div class="link_row_dummy drop_slot flex flex-row" data-tmpl="root">
|
||||||
|
<input class="o-0">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<input id="links_json" type="hidden" name="links">
|
||||||
|
<script>
|
||||||
|
const linksContainer = document.querySelector("#links");
|
||||||
|
const parentForm = linksContainer.closest("form");
|
||||||
|
const secondaryLinksTitle = linksContainer.querySelector(".secondary_links");
|
||||||
|
const linksJSONInput = document.querySelector("#links_json");
|
||||||
|
const linkTemplate = makeTemplateCloner("link_row");
|
||||||
|
const dummyLinkTemplate = makeTemplateCloner("link_row_dummy");
|
||||||
|
|
||||||
|
parentForm.addEventListener("submit", function() {
|
||||||
|
updateLinksJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialLinks = JSON.parse("{{ . }}");
|
||||||
|
for (const link of initialLinks) {
|
||||||
|
const l = linkTemplate();
|
||||||
|
l.nameInput.value = link.name;
|
||||||
|
l.urlInput.value = link.url;
|
||||||
|
if (link.primary) {
|
||||||
|
secondaryLinksTitle.insertAdjacentElement("beforebegin", l.root);
|
||||||
|
} else {
|
||||||
|
linksContainer.appendChild(l.root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ensureLinksEmptyState();
|
||||||
|
|
||||||
|
function addLink(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
linksContainer.appendChild(linkTemplate().root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteLink(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const l = e.target.closest(".link_row");
|
||||||
|
l.remove();
|
||||||
|
|
||||||
|
ensureLinksEmptyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureLinksEmptyState() {
|
||||||
|
if (!linksContainer.querySelector(".link_row")) {
|
||||||
|
// Empty state is a single row
|
||||||
|
linksContainer.appendChild(linkTemplate().root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLinksJSON() {
|
||||||
|
const links = [];
|
||||||
|
let primary = true;
|
||||||
|
let els = linksContainer.children;
|
||||||
|
for (let i = 0; i < els.length; ++i) {
|
||||||
|
let el = els[i];
|
||||||
|
if (el.classList.contains("secondary_links")) {
|
||||||
|
primary = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (el.classList.contains("link_row")) {
|
||||||
|
const name = el.querySelector(".link_name").value;
|
||||||
|
const url = el.querySelector(".link_url").value;
|
||||||
|
if (!url) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
links.push({
|
||||||
|
"name": name,
|
||||||
|
"url": url,
|
||||||
|
"primary": primary,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linksJSONInput.value = JSON.stringify(links);
|
||||||
|
}
|
||||||
|
|
||||||
|
let draggingLink = null;
|
||||||
|
let linkDragStartY = 0;
|
||||||
|
let linkDragStartMouseY = 0;
|
||||||
|
|
||||||
|
function startLinkDrag(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const l = e.target.closest(".link_row");
|
||||||
|
|
||||||
|
const top = l.offsetTop;
|
||||||
|
|
||||||
|
l.insertAdjacentElement("beforebegin", dummyLinkTemplate().root);
|
||||||
|
document.body.classList.add("grabbing");
|
||||||
|
|
||||||
|
l.style.position = "absolute";
|
||||||
|
l.style.top = `${top}px`;
|
||||||
|
l.classList.add("link_dragging");
|
||||||
|
l.classList.remove("drop_slot");
|
||||||
|
|
||||||
|
draggingLink = l;
|
||||||
|
linkDragStartY = top;
|
||||||
|
linkDragStartMouseY = e.pageY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doLinkDrag(e) {
|
||||||
|
if (!draggingLink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxTop = linksContainer.offsetHeight - draggingLink.offsetHeight;
|
||||||
|
|
||||||
|
const delta = e.pageY - linkDragStartMouseY;
|
||||||
|
const top = Math.max(0, Math.min(maxTop, linkDragStartY + delta));
|
||||||
|
const middle = top + draggingLink.offsetHeight/2;
|
||||||
|
|
||||||
|
draggingLink.style.top = `${top}px`;
|
||||||
|
|
||||||
|
const slots = linksContainer.querySelectorAll(".drop_slot");
|
||||||
|
let closestSlot = null;
|
||||||
|
let slotDist = Number.MAX_VALUE;
|
||||||
|
for (let i = 0; i < slots.length; ++i) {
|
||||||
|
let slotMiddle = slots[i].offsetTop + slots[i].offsetHeight/2;
|
||||||
|
let dist = Math.abs(middle - slotMiddle);
|
||||||
|
if (dist < slotDist) {
|
||||||
|
closestSlot = slots[i];
|
||||||
|
slotDist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dummy = linksContainer.querySelector(".link_row_dummy");
|
||||||
|
if (!closestSlot.classList.contains("link_row_dummy")) {
|
||||||
|
let replaceType = "afterend";
|
||||||
|
if (closestSlot.offsetTop < dummy.offsetTop) {
|
||||||
|
replaceType = "beforebegin";
|
||||||
|
}
|
||||||
|
closestSlot.insertAdjacentElement(replaceType, dummy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function endLinkDrag(e) {
|
||||||
|
if (!draggingLink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dummy = linksContainer.querySelector(".link_row_dummy");
|
||||||
|
draggingLink.remove();
|
||||||
|
dummy.insertAdjacentElement("beforebegin", draggingLink);
|
||||||
|
dummy.remove();
|
||||||
|
|
||||||
|
draggingLink.style.position = null;
|
||||||
|
draggingLink.style.top = null;
|
||||||
|
draggingLink.classList.remove("link_dragging");
|
||||||
|
draggingLink.classList.add("drop_slot");
|
||||||
|
|
||||||
|
document.body.classList.remove("grabbing");
|
||||||
|
draggingLink = null;
|
||||||
|
}
|
||||||
|
window.addEventListener("mouseup", endLinkDrag);
|
||||||
|
window.addEventListener("mousemove", doLinkDrag);
|
||||||
|
</script>
|
||||||
|
</fieldset>
|
||||||
|
|
|
@ -155,32 +155,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<fieldset>
|
{{ template "link_editor.html" .ProjectSettings.LinksJSON }}
|
||||||
<legend class="flex justify-between">
|
|
||||||
<span>Links</span>
|
|
||||||
<a href="#" class="normal" onclick="addLink(event)">+ Add Link</a>
|
|
||||||
</legend>
|
|
||||||
<div class="pa3 input-group">
|
|
||||||
<div id="links" class="flex flex-column g3 relative">
|
|
||||||
<div>Primary Links</div>
|
|
||||||
<div>Secondary Links</div>
|
|
||||||
</div>
|
|
||||||
<template id="link_row">
|
|
||||||
<div class="link_row w-100 flex flex-row items-center" data-tmpl="root">
|
|
||||||
<span class="link_handle svgicon pr3 pointer grab" onmousedown="startLinkDrag(event)">{{ svg "draggable" }}</span>
|
|
||||||
<input data-tmpl="nameInput" class="link_name mr3 w5" type="text" placeholder="Name" />
|
|
||||||
<input data-tmpl="urlInput" class="link_url flex-grow-1" type="url" placeholder="Link" />
|
|
||||||
<a class="delete_link svgicon link-normal pl3 f3" href="javascript:;" onclick="deleteLink(event)">{{ svg "delete" }}</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="link_row_dummy">
|
|
||||||
<div class="link_row_dummy flex flex-row" data-tmpl="root">
|
|
||||||
<input class="o-0">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<input id="links_json" type="hidden" name="links">
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
{{ if and .Editing .User.IsStaff }}
|
{{ if and .Editing .User.IsStaff }}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -422,127 +397,5 @@
|
||||||
{{ .TextEditor.UploadUrl }}
|
{{ .TextEditor.UploadUrl }}
|
||||||
);
|
);
|
||||||
|
|
||||||
///////////
|
|
||||||
// Links //
|
|
||||||
///////////
|
|
||||||
|
|
||||||
const linksContainer = document.querySelector("#links");
|
|
||||||
const linksJSONInput = document.querySelector("#links_json");
|
|
||||||
const linkTemplate = makeTemplateCloner("link_row");
|
|
||||||
const dummyLinkTemplate = makeTemplateCloner("link_row_dummy");
|
|
||||||
|
|
||||||
const initialLinks = JSON.parse("{{ .ProjectSettings.LinksJSON }}");
|
|
||||||
for (const link of initialLinks) {
|
|
||||||
const l = linkTemplate();
|
|
||||||
l.nameInput.value = link.name;
|
|
||||||
l.urlInput.value = link.url;
|
|
||||||
linksContainer.appendChild(l.root)
|
|
||||||
}
|
|
||||||
ensureLinksEmptyState();
|
|
||||||
|
|
||||||
projectForm.addEventListener("submit", () => updateLinksJSON());
|
|
||||||
|
|
||||||
function addLink(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
linksContainer.appendChild(linkTemplate().root);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteLink(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const l = e.target.closest(".link_row");
|
|
||||||
l.remove();
|
|
||||||
|
|
||||||
ensureLinksEmptyState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureLinksEmptyState() {
|
|
||||||
if (!linksContainer.querySelector(".link_row")) {
|
|
||||||
// Empty state is a single row
|
|
||||||
linksContainer.appendChild(linkTemplate().root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLinksJSON() {
|
|
||||||
const links = [];
|
|
||||||
for (const l of linksContainer.querySelectorAll(".link_row")) {
|
|
||||||
const value = l.querySelector(".link_name").value;
|
|
||||||
const url = l.querySelector(".link_url").value;
|
|
||||||
if (!url) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
links.push({
|
|
||||||
"name": name,
|
|
||||||
"url": url,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
linksJSONInput.value = JSON.stringify(links);
|
|
||||||
}
|
|
||||||
|
|
||||||
let draggingLink = null;
|
|
||||||
let linkDragStartY = 0;
|
|
||||||
let linkDragStartMouseY = 0;
|
|
||||||
|
|
||||||
function startLinkDrag(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const l = e.target.closest(".link_row");
|
|
||||||
|
|
||||||
const top = l.offsetTop;
|
|
||||||
|
|
||||||
l.insertAdjacentElement("beforebegin", dummyLinkTemplate().root);
|
|
||||||
document.querySelector("body").classList.add("grabbing");
|
|
||||||
|
|
||||||
l.style.position = "absolute";
|
|
||||||
l.style.top = `${top}px`;
|
|
||||||
l.classList.add("link_dragging");
|
|
||||||
|
|
||||||
draggingLink = l;
|
|
||||||
linkDragStartY = top;
|
|
||||||
linkDragStartMouseY = e.pageY;
|
|
||||||
}
|
|
||||||
|
|
||||||
function doLinkDrag(e) {
|
|
||||||
if (!draggingLink) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxTop = linksContainer.offsetHeight - draggingLink.offsetHeight;
|
|
||||||
|
|
||||||
const delta = e.pageY - linkDragStartMouseY;
|
|
||||||
const top = Math.max(0, Math.min(maxTop, linkDragStartY + delta));
|
|
||||||
const middle = top + draggingLink.offsetHeight/2;
|
|
||||||
|
|
||||||
draggingLink.style.top = `${top}px`;
|
|
||||||
|
|
||||||
const numLinks = linksContainer.querySelectorAll(".link_row").length;
|
|
||||||
const itemHeight = linksContainer.offsetHeight / numLinks;
|
|
||||||
const index = Math.floor(middle / itemHeight);
|
|
||||||
|
|
||||||
const links = linksContainer.querySelectorAll(".link_row:not(.link_dragging)");
|
|
||||||
const dummy = linksContainer.querySelector(".link_row_dummy");
|
|
||||||
dummy.remove();
|
|
||||||
linksContainer.insertBefore(dummy, links[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function endLinkDrag(e) {
|
|
||||||
if (!draggingLink) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dummy = linksContainer.querySelector(".link_row_dummy");
|
|
||||||
draggingLink.remove();
|
|
||||||
linksContainer.insertBefore(draggingLink, dummy);
|
|
||||||
dummy.remove();
|
|
||||||
|
|
||||||
draggingLink.style.position = null;
|
|
||||||
draggingLink.style.top = null;
|
|
||||||
draggingLink.classList.remove("link_dragging");
|
|
||||||
|
|
||||||
document.querySelector("body").classList.remove("grabbing");
|
|
||||||
draggingLink = null;
|
|
||||||
}
|
|
||||||
window.addEventListener("mouseup", endLinkDrag);
|
|
||||||
window.addEventListener("mousemove", doLinkDrag);
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
{{ define "extrahead" }}
|
{{ define "extrahead" }}
|
||||||
<script src="{{ static "js/image_selector.js" }}"></script>
|
<script src="{{ static "js/image_selector.js" }}"></script>
|
||||||
|
<script src="{{ static "js/templates.js" }}"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
|
@ -63,9 +64,7 @@
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
{{ template "link_editor.html" .LinksJSON }}
|
||||||
<legend>Links</legend>
|
|
||||||
</fieldset>
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label for="shortbio">Short bio</label>
|
<label for="shortbio">Short bio</label>
|
||||||
<textarea class="w-100" maxlength="140" data-max-chars="140" name="shortbio" id="shortbio">
|
<textarea class="w-100" maxlength="140" data-max-chars="140" name="shortbio" id="shortbio">
|
||||||
|
@ -306,7 +305,6 @@
|
||||||
<div class="pt-input-ns">Links:</div>
|
<div class="pt-input-ns">Links:</div>
|
||||||
<div>
|
<div>
|
||||||
<textarea class="links" name="links" id="links" maxlength="2048" data-max-chars="2048">
|
<textarea class="links" name="links" id="links" maxlength="2048" data-max-chars="2048">
|
||||||
{{- .LinksText -}}
|
|
||||||
</textarea>
|
</textarea>
|
||||||
<div class="c--dim f7">
|
<div class="c--dim f7">
|
||||||
<div>Relevant links to put on your profile.</div>
|
<div>Relevant links to put on your profile.</div>
|
||||||
|
|
|
@ -225,6 +225,7 @@ type Link struct {
|
||||||
ServiceName string `json:"serviceName"`
|
ServiceName string `json:"serviceName"`
|
||||||
Username string `json:"text"`
|
Username string `json:"text"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
|
Primary bool `json:"primary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Podcast struct {
|
type Podcast struct {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
type ParsedLink struct {
|
type ParsedLink struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
|
Primary bool `json:"primary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseLinks(text string) []ParsedLink {
|
func ParseLinks(text string) []ParsedLink {
|
||||||
|
|
|
@ -935,12 +935,13 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P
|
||||||
for i, link := range payload.Links {
|
for i, link := range payload.Links {
|
||||||
_, err = tx.Exec(ctx,
|
_, err = tx.Exec(ctx,
|
||||||
`
|
`
|
||||||
INSERT INTO link (name, url, ordering, project_id)
|
INSERT INTO link (name, url, ordering, primary_link, project_id)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
`,
|
`,
|
||||||
link.Name,
|
link.Name,
|
||||||
link.Url,
|
link.Url,
|
||||||
i,
|
i,
|
||||||
|
link.Primary,
|
||||||
payload.ProjectID,
|
payload.ProjectID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package website
|
package website
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -193,7 +194,7 @@ func UserSettings(c *RequestContext) ResponseData {
|
||||||
Avatar *templates.Asset
|
Avatar *templates.Asset
|
||||||
Email string // these fields are handled specially on templates.User
|
Email string // these fields are handled specially on templates.User
|
||||||
ShowEmail bool
|
ShowEmail bool
|
||||||
LinksText string
|
LinksJSON string
|
||||||
HasPassword bool
|
HasPassword bool
|
||||||
|
|
||||||
SubmitUrl string
|
SubmitUrl string
|
||||||
|
@ -219,7 +220,7 @@ func UserSettings(c *RequestContext) ResponseData {
|
||||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
|
||||||
}
|
}
|
||||||
|
|
||||||
linksText := LinksToText(links)
|
linksJSON := string(utils.Must1(json.Marshal(templates.LinksToTemplate(links))))
|
||||||
|
|
||||||
var tduser *templates.DiscordUser
|
var tduser *templates.DiscordUser
|
||||||
var numUnsavedMessages int
|
var numUnsavedMessages int
|
||||||
|
@ -269,7 +270,7 @@ func UserSettings(c *RequestContext) ResponseData {
|
||||||
Avatar: templates.AssetToTemplate(c.CurrentUser.AvatarAsset),
|
Avatar: templates.AssetToTemplate(c.CurrentUser.AvatarAsset),
|
||||||
Email: c.CurrentUser.Email,
|
Email: c.CurrentUser.Email,
|
||||||
ShowEmail: c.CurrentUser.ShowEmail,
|
ShowEmail: c.CurrentUser.ShowEmail,
|
||||||
LinksText: linksText,
|
LinksJSON: linksJSON,
|
||||||
HasPassword: c.CurrentUser.Password != "",
|
HasPassword: c.CurrentUser.Password != "",
|
||||||
|
|
||||||
SubmitUrl: hmnurl.BuildUserSettings(""),
|
SubmitUrl: hmnurl.BuildUserSettings(""),
|
||||||
|
@ -378,12 +379,13 @@ func UserSettingsSave(c *RequestContext) ResponseData {
|
||||||
for i, link := range links {
|
for i, link := range links {
|
||||||
_, err := tx.Exec(c,
|
_, err := tx.Exec(c,
|
||||||
`
|
`
|
||||||
INSERT INTO link (name, url, ordering, user_id)
|
INSERT INTO link (name, url, ordering, primary_link, user_id)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
`,
|
`,
|
||||||
link.Name,
|
link.Name,
|
||||||
link.Url,
|
link.Url,
|
||||||
i,
|
i,
|
||||||
|
link.Primary,
|
||||||
c.CurrentUser.ID,
|
c.CurrentUser.ID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue