Compare commits
No commits in common. "582897903ecd6605df10377c7e2a94cc33e7f838" and "83113a563acf1ec954795a63910c2c2f88d32259" have entirely different histories.
582897903e
...
83113a563a
|
|
@ -1,83 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"threadr/models"
|
|
||||||
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PreferencesHandler(app *App) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
session := r.Context().Value("session").(*sessions.Session)
|
|
||||||
userID, ok := session.Values["user_id"].(int)
|
|
||||||
if !ok {
|
|
||||||
http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle POST request (saving preferences)
|
|
||||||
if r.Method == http.MethodPost {
|
|
||||||
// Get form values
|
|
||||||
autoSaveDrafts := r.FormValue("auto_save_drafts") == "on"
|
|
||||||
|
|
||||||
// Get current preferences (or create if not exists)
|
|
||||||
prefs, err := models.GetUserPreferences(app.DB, userID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error fetching preferences: %v", err)
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update preferences
|
|
||||||
prefs.AutoSaveDrafts = autoSaveDrafts
|
|
||||||
|
|
||||||
err = models.UpdateUserPreferences(app.DB, prefs)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error updating preferences: %v", err)
|
|
||||||
http.Error(w, "Failed to save preferences", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect back to preferences page with success
|
|
||||||
http.Redirect(w, r, app.Config.ThreadrDir+"/preferences/?saved=true", http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle GET request (displaying preferences form)
|
|
||||||
prefs, err := models.GetUserPreferences(app.DB, userID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error fetching preferences: %v", err)
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we should show success message
|
|
||||||
showSuccess := r.URL.Query().Get("saved") == "true"
|
|
||||||
|
|
||||||
data := struct {
|
|
||||||
PageData
|
|
||||||
Preferences *models.UserPreferences
|
|
||||||
ShowSuccess bool
|
|
||||||
}{
|
|
||||||
PageData: PageData{
|
|
||||||
Title: "ThreadR - Preferences",
|
|
||||||
Navbar: "preferences",
|
|
||||||
LoggedIn: true,
|
|
||||||
ShowCookieBanner: false,
|
|
||||||
BasePath: app.Config.ThreadrDir,
|
|
||||||
StaticPath: app.Config.ThreadrDir + "/static",
|
|
||||||
CurrentURL: r.URL.Path,
|
|
||||||
},
|
|
||||||
Preferences: prefs,
|
|
||||||
ShowSuccess: showSuccess,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := app.Tmpl.ExecuteTemplate(w, "preferences", data); err != nil {
|
|
||||||
log.Printf("Error executing template in PreferencesHandler: %v", err)
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
main.go
16
main.go
|
|
@ -217,20 +217,6 @@ func createTablesIfNotExist(db *sql.DB) error {
|
||||||
return fmt.Errorf("error creating users table: %v", err)
|
return fmt.Errorf("error creating users table: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create user_preferences table
|
|
||||||
_, err = db.Exec(`
|
|
||||||
CREATE TABLE user_preferences (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
user_id INT NOT NULL UNIQUE,
|
|
||||||
auto_save_drafts BOOLEAN DEFAULT TRUE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
||||||
)`)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error creating user_preferences table: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Database tables created.")
|
log.Println("Database tables created.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -376,7 +362,6 @@ func main() {
|
||||||
filepath.Join(dir, "templates/pages/thread.html"),
|
filepath.Join(dir, "templates/pages/thread.html"),
|
||||||
filepath.Join(dir, "templates/pages/userhome.html"),
|
filepath.Join(dir, "templates/pages/userhome.html"),
|
||||||
filepath.Join(dir, "templates/pages/chat.html"),
|
filepath.Join(dir, "templates/pages/chat.html"),
|
||||||
filepath.Join(dir, "templates/pages/preferences.html"),
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error parsing page templates:", err)
|
log.Fatal("Error parsing page templates:", err)
|
||||||
|
|
@ -414,7 +399,6 @@ func main() {
|
||||||
http.HandleFunc(config.ThreadrDir+"/about/", app.SessionMW(handlers.AboutHandler(app)))
|
http.HandleFunc(config.ThreadrDir+"/about/", app.SessionMW(handlers.AboutHandler(app)))
|
||||||
http.HandleFunc(config.ThreadrDir+"/profile/", app.SessionMW(app.RequireLoginMW(handlers.ProfileHandler(app))))
|
http.HandleFunc(config.ThreadrDir+"/profile/", app.SessionMW(app.RequireLoginMW(handlers.ProfileHandler(app))))
|
||||||
http.HandleFunc(config.ThreadrDir+"/profile/edit/", app.SessionMW(app.RequireLoginMW(handlers.ProfileEditHandler(app))))
|
http.HandleFunc(config.ThreadrDir+"/profile/edit/", app.SessionMW(app.RequireLoginMW(handlers.ProfileEditHandler(app))))
|
||||||
http.HandleFunc(config.ThreadrDir+"/preferences/", app.SessionMW(app.RequireLoginMW(handlers.PreferencesHandler(app))))
|
|
||||||
http.HandleFunc(config.ThreadrDir+"/like/", app.SessionMW(app.RequireLoginMW(handlers.LikeHandler(app))))
|
http.HandleFunc(config.ThreadrDir+"/like/", app.SessionMW(app.RequireLoginMW(handlers.LikeHandler(app))))
|
||||||
http.HandleFunc(config.ThreadrDir+"/news/", app.SessionMW(handlers.NewsHandler(app)))
|
http.HandleFunc(config.ThreadrDir+"/news/", app.SessionMW(handlers.NewsHandler(app)))
|
||||||
http.HandleFunc(config.ThreadrDir+"/signup/", app.SessionMW(handlers.SignupHandler(app)))
|
http.HandleFunc(config.ThreadrDir+"/signup/", app.SessionMW(handlers.SignupHandler(app)))
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserPreferences struct {
|
|
||||||
ID int
|
|
||||||
UserID int
|
|
||||||
AutoSaveDrafts bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserPreferences retrieves preferences for a user, creating defaults if none exist
|
|
||||||
func GetUserPreferences(db *sql.DB, userID int) (*UserPreferences, error) {
|
|
||||||
query := `SELECT id, user_id, auto_save_drafts
|
|
||||||
FROM user_preferences WHERE user_id = ?`
|
|
||||||
|
|
||||||
prefs := &UserPreferences{}
|
|
||||||
err := db.QueryRow(query, userID).Scan(
|
|
||||||
&prefs.ID,
|
|
||||||
&prefs.UserID,
|
|
||||||
&prefs.AutoSaveDrafts,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
// No preferences exist, create defaults
|
|
||||||
return CreateDefaultPreferences(db, userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDefaultPreferences creates default preferences for a new user
|
|
||||||
func CreateDefaultPreferences(db *sql.DB, userID int) (*UserPreferences, error) {
|
|
||||||
query := `INSERT INTO user_preferences (user_id, auto_save_drafts)
|
|
||||||
VALUES (?, TRUE)`
|
|
||||||
|
|
||||||
result, err := db.Exec(query, userID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := result.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &UserPreferences{
|
|
||||||
ID: int(id),
|
|
||||||
UserID: userID,
|
|
||||||
AutoSaveDrafts: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUserPreferences updates user preferences
|
|
||||||
func UpdateUserPreferences(db *sql.DB, prefs *UserPreferences) error {
|
|
||||||
query := `UPDATE user_preferences
|
|
||||||
SET auto_save_drafts = ?, updated_at = NOW()
|
|
||||||
WHERE user_id = ?`
|
|
||||||
|
|
||||||
_, err := db.Exec(query, prefs.AutoSaveDrafts, prefs.UserID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
@ -59,6 +59,7 @@ function enableEnterToSubmit(input, form) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize on DOM ready
|
// Initialize on DOM ready
|
||||||
|
|
@ -282,92 +283,3 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Draft Auto-Save Functions
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
// Save draft to localStorage
|
|
||||||
function saveDraft(key, content) {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(key, content);
|
|
||||||
localStorage.setItem(key + '_timestamp', Date.now().toString());
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to save draft:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load draft from localStorage
|
|
||||||
function loadDraft(key) {
|
|
||||||
try {
|
|
||||||
return localStorage.getItem(key);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to load draft:', e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear draft from localStorage
|
|
||||||
function clearDraft(key) {
|
|
||||||
try {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
localStorage.removeItem(key + '_timestamp');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to clear draft:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get draft timestamp
|
|
||||||
function getDraftTimestamp(key) {
|
|
||||||
try {
|
|
||||||
const timestamp = localStorage.getItem(key + '_timestamp');
|
|
||||||
return timestamp ? parseInt(timestamp) : null;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to get draft timestamp:', e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format time ago
|
|
||||||
function formatTimeAgo(timestamp) {
|
|
||||||
const now = Date.now();
|
|
||||||
const diff = now - timestamp;
|
|
||||||
const seconds = Math.floor(diff / 1000);
|
|
||||||
const minutes = Math.floor(seconds / 60);
|
|
||||||
const hours = Math.floor(minutes / 60);
|
|
||||||
const days = Math.floor(hours / 24);
|
|
||||||
|
|
||||||
if (days > 0) return `${days} day${days > 1 ? 's' : ''} ago`;
|
|
||||||
if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
|
||||||
if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
|
||||||
return 'just now';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show draft restoration indicator
|
|
||||||
function showDraftIndicator(content, timestamp, onRestore, onDiscard) {
|
|
||||||
const indicator = document.createElement('div');
|
|
||||||
indicator.className = 'draft-indicator';
|
|
||||||
indicator.innerHTML = `
|
|
||||||
<div class="draft-indicator-content">
|
|
||||||
<span class="draft-indicator-icon">📝</span>
|
|
||||||
<span class="draft-indicator-text">Draft from ${formatTimeAgo(timestamp)}</span>
|
|
||||||
<button type="button" class="draft-indicator-button restore">Restore</button>
|
|
||||||
<button type="button" class="draft-indicator-button discard">Discard</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const restoreBtn = indicator.querySelector('.restore');
|
|
||||||
const discardBtn = indicator.querySelector('.discard');
|
|
||||||
|
|
||||||
restoreBtn.addEventListener('click', () => {
|
|
||||||
onRestore(content);
|
|
||||||
indicator.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
discardBtn.addEventListener('click', () => {
|
|
||||||
onDiscard();
|
|
||||||
indicator.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
return indicator;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -687,70 +687,6 @@ input.error, textarea.error, select.error {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draft indicator styles */
|
|
||||||
.draft-indicator {
|
|
||||||
background-color: #f3d2c1;
|
|
||||||
border: 1px solid #001858;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
animation: slideInFromTop 0.3s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-text {
|
|
||||||
flex: 1;
|
|
||||||
color: #001858;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button {
|
|
||||||
padding: 5px 12px;
|
|
||||||
border: 1px solid #001858;
|
|
||||||
background-color: #fef6e4;
|
|
||||||
color: #001858;
|
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 0.85em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button:hover {
|
|
||||||
background-color: #001858;
|
|
||||||
color: #fef6e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button.restore {
|
|
||||||
background-color: #8bd3dd;
|
|
||||||
border-color: #001858;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button.restore:hover {
|
|
||||||
background-color: #001858;
|
|
||||||
color: #8bd3dd;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideInFromTop {
|
|
||||||
from {
|
|
||||||
transform: translateY(-20px);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateY(0);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.spinner {
|
.spinner {
|
||||||
border-color: #444;
|
border-color: #444;
|
||||||
|
|
@ -788,37 +724,6 @@ input.error, textarea.error, select.error {
|
||||||
.char-counter {
|
.char-counter {
|
||||||
color: #fef6e4;
|
color: #fef6e4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draft-indicator {
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
border-color: #fef6e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-text {
|
|
||||||
color: #fef6e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button {
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
color: #fef6e4;
|
|
||||||
border-color: #fef6e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button:hover {
|
|
||||||
background-color: #fef6e4;
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button.restore {
|
|
||||||
background-color: #8bd3dd;
|
|
||||||
color: #001858;
|
|
||||||
border-color: #8bd3dd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draft-indicator-button.restore:hover {
|
|
||||||
background-color: #fef6e4;
|
|
||||||
color: #001858;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
|
|
|
||||||
|
|
@ -851,61 +851,6 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draft auto-save functionality
|
|
||||||
const draftKey = 'draft_chat_{{.Board.ID}}';
|
|
||||||
let draftSaveTimeout;
|
|
||||||
|
|
||||||
// Load existing draft on page load
|
|
||||||
const existingDraft = loadDraft(draftKey);
|
|
||||||
const draftTimestamp = getDraftTimestamp(draftKey);
|
|
||||||
if (existingDraft && draftTimestamp) {
|
|
||||||
// Check if draft is less than 7 days old
|
|
||||||
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
|
|
||||||
if (draftTimestamp > sevenDaysAgo) {
|
|
||||||
const indicator = showDraftIndicator(
|
|
||||||
existingDraft,
|
|
||||||
draftTimestamp,
|
|
||||||
(content) => {
|
|
||||||
chatInput.value = content;
|
|
||||||
chatInput.focus();
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
clearDraft(draftKey);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// Insert indicator before chat input
|
|
||||||
const chatInputContainer = document.querySelector('.chat-input');
|
|
||||||
chatInputContainer.parentNode.insertBefore(indicator, chatInputContainer);
|
|
||||||
} else {
|
|
||||||
// Draft is too old, clear it
|
|
||||||
clearDraft(draftKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save draft on input (debounced)
|
|
||||||
chatInput.addEventListener('input', () => {
|
|
||||||
clearTimeout(draftSaveTimeout);
|
|
||||||
draftSaveTimeout = setTimeout(() => {
|
|
||||||
const content = chatInput.value.trim();
|
|
||||||
if (content) {
|
|
||||||
saveDraft(draftKey, content);
|
|
||||||
} else {
|
|
||||||
clearDraft(draftKey);
|
|
||||||
}
|
|
||||||
}, 2000); // Save after 2 seconds of inactivity
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear draft after successful message send
|
|
||||||
const originalSendMessage = window.sendMessage;
|
|
||||||
window.sendMessage = function() {
|
|
||||||
const input = document.getElementById('chat-input-text');
|
|
||||||
const content = input.value.trim();
|
|
||||||
if (content !== '') {
|
|
||||||
clearDraft(draftKey);
|
|
||||||
}
|
|
||||||
originalSendMessage();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial check for scroll position
|
// Initial check for scroll position
|
||||||
checkScrollPosition();
|
checkScrollPosition();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
{{define "preferences"}}
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{{.Title}}</title>
|
|
||||||
<link rel="stylesheet" href="{{.StaticPath}}/style.css">
|
|
||||||
<script src="{{.StaticPath}}/app.js" defer></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{{template "navbar" .}}
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<h2>Preferences</h2>
|
|
||||||
</header>
|
|
||||||
{{if .ShowSuccess}}
|
|
||||||
<div class="notification success" style="position: static; margin-bottom: 1em; animation: none;">
|
|
||||||
Preferences saved successfully!
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
<section>
|
|
||||||
<form method="post" action="{{.BasePath}}/preferences/">
|
|
||||||
<h3>Draft Auto-Save</h3>
|
|
||||||
<label for="auto_save_drafts" style="display: flex; align-items: center; gap: 0.5em; cursor: pointer;">
|
|
||||||
<input type="checkbox" id="auto_save_drafts" name="auto_save_drafts" {{if .Preferences.AutoSaveDrafts}}checked{{end}}>
|
|
||||||
<span>Automatically save drafts while typing in chat</span>
|
|
||||||
</label>
|
|
||||||
<p style="margin-left: 1.5em; margin-top: 0.25em; font-size: 0.9em; opacity: 0.8;">
|
|
||||||
Drafts are saved to your browser's local storage and restored when you return to chat.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<input type="submit" value="Save Preferences" style="margin-top: 2em;">
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{{template "cookie_banner" .}}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
{{end}}
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
{{if .LoggedIn}}
|
{{if .LoggedIn}}
|
||||||
<li><a {{if eq .Navbar "userhome"}}class="active"{{end}} href="{{.BasePath}}/userhome/">User Home</a></li>
|
<li><a {{if eq .Navbar "userhome"}}class="active"{{end}} href="{{.BasePath}}/userhome/">User Home</a></li>
|
||||||
<li><a {{if eq .Navbar "profile"}}class="active"{{end}} href="{{.BasePath}}/profile/">Profile</a></li>
|
<li><a {{if eq .Navbar "profile"}}class="active"{{end}} href="{{.BasePath}}/profile/">Profile</a></li>
|
||||||
<li><a {{if eq .Navbar "preferences"}}class="active"{{end}} href="{{.BasePath}}/preferences/">Preferences</a></li>
|
|
||||||
<li><a href="{{.BasePath}}/logout/">Logout</a></li>
|
<li><a href="{{.BasePath}}/logout/">Logout</a></li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li><a {{if eq .Navbar "login"}}class="active"{{end}} href="{{.BasePath}}/login/">Login</a></li>
|
<li><a {{if eq .Navbar "login"}}class="active"{{end}} href="{{.BasePath}}/login/">Login</a></li>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue