From c91b76f807533c44fad90bf114d02c1d21a77eae Mon Sep 17 00:00:00 2001 From: radon Date: Sun, 19 Jan 2025 00:51:28 -0600 Subject: [PATCH] remove old files, move todo.md to readme.md --- old/main.go | 333 --------------------------------- old/main.go.bak | 366 ------------------------------------- old/messages.html | 7 - old/root.css | 247 ------------------------- old/root.html | 66 ------- old/root.js | 244 ------------------------- notes/todo.md => readme.md | 0 7 files changed, 1263 deletions(-) delete mode 100644 old/main.go delete mode 100644 old/main.go.bak delete mode 100644 old/messages.html delete mode 100644 old/root.css delete mode 100644 old/root.html delete mode 100644 old/root.js rename notes/todo.md => readme.md (100%) diff --git a/old/main.go b/old/main.go deleted file mode 100644 index 4d5ccde..0000000 --- a/old/main.go +++ /dev/null @@ -1,333 +0,0 @@ -package main - -import ( - "compress/gzip" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "os" - "strconv" - "strings" - "sync" - "time" -) - -type gzipResponseWriter struct { - http.ResponseWriter - io.Writer -} - -func (g *gzipResponseWriter) Write(data []byte) (int, error) { - return g.Writer.Write(data) -} - -func GzipMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { - next.ServeHTTP(w, r) - return - } - - gz := gzip.NewWriter(w) - defer gz.Close() - - w.Header().Set("Content-Encoding", "gzip") - next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r) - }) -} - -func getClientIP(r *http.Request) string { - if fwdIP := r.Header.Get("X-Forwarded-For"); fwdIP != "" { - return strings.Split(fwdIP, ",")[0] - } - - clientIP := r.RemoteAddr - if host, _, err := net.SplitHostPort(clientIP); err == nil { - return host - } - return clientIP -} - -type Message struct { - Content string - SenderIp string - SenderUsername string - Timestamp string -} - -func NewMessage(username, ip, content string) *Message { - timestamp := time.Now().Format("2006-01-02 15:04:05") - return &Message{ - Content: content, - SenderIp: ip, - SenderUsername: username, - Timestamp: timestamp, - } -} - -type Server struct { - Ip string - Port int - Messages []Message - // Add mappings for IPs and usernames - Connected map[string]time.Time // Map IP -> Last activity time - Usernames map[string]string // Map IP -> Username - mu sync.Mutex // For thread safety -} - -func NewServer(ip string, port int) *Server { - return &Server{ - Ip: ip, - Port: port, - Messages: make([]Message, 0), - Connected: make(map[string]time.Time), - Usernames: make(map[string]string), - mu: sync.Mutex{}, - } -} - -func (s *Server) AddMessage(username string, userip string, contents string) { - message := NewMessage(username, userip, contents) - s.Messages = append(s.Messages, *message) -} - -func (s *Server) updateActivity(ip string) { - s.mu.Lock() - defer s.mu.Unlock() - s.Connected[ip] = time.Now() -} - -func (s *Server) cleanupActivity() { - s.mu.Lock() - defer s.mu.Unlock() - for ip, lastActivity := range s.Connected { - if time.Since(lastActivity) > 10*time.Second { - delete(s.Connected, ip) - } - } -} - -func (s *Server) handlePing(w http.ResponseWriter, r *http.Request) { - clientIP := getClientIP(r) - s.updateActivity(clientIP) - s.cleanupActivity() - w.WriteHeader(http.StatusOK) -} - -func validUsername(username string) bool { - for _, c := range username { - if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') { - return false - } - } - return true -} - -func (s *Server) handleUsername(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPut { - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } - - w.Header().Set("Content-Type", "application/json") - - clientIP := getClientIP(r) - - var req struct { - Username string `json:"username"` - } - - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest) - return - } - - s.mu.Lock() - - if len(req.Username) > 64 { - http.Error(w, fmt.Sprintf(`{"error": "Username too long (must be less than 64 characters)"}`), http.StatusRequestEntityTooLarge) - s.mu.Unlock() - return - } - - if !validUsername(req.Username) { - http.Error(w, fmt.Sprintf(`{"error": "Username must only contain alphanumeric characters and/or underscores"}`), http.StatusBadRequest) - s.mu.Unlock() - return - } - - for _, username := range s.Usernames { - if username == req.Username { - s.mu.Unlock() - http.Error(w, fmt.Sprintf(`{"error": "Username already exists"}`), http.StatusConflict) - return - } - } - - if _, ok := s.Usernames[clientIP]; ok { - fmt.Println("Name change detected, fixing messages to reflect the change") - for i, msg := range s.Messages { - if msg.SenderIp == clientIP { - s.Messages[i].SenderUsername = req.Username - } - } - } - s.Usernames[clientIP] = req.Username - - s.mu.Unlock() - - json.NewEncoder(w).Encode(map[string]string{"status": "Username registered"}) - fmt.Println(clientIP, "->", req.Username) -} - -func getMessageTemplate(file string, body string) string { - contents, _ := os.ReadFile(file) - return strings.Replace(string(contents), "{{body}}", body, 1) -} - -func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodGet: - w.Header().Set("Content-Type", "text/html") - - var body string - for _, msg := range s.Messages { - body += fmt.Sprintf(`

%s %s
%s

`, - msg.SenderUsername, msg.Timestamp, msg.Content) - } - - w.Write([]byte(getMessageTemplate("messages.html", body))) - - case http.MethodPut: - w.Header().Set("Content-Type", "application/json") - - // Get client's IP - clientIP := getClientIP(r) - - // Check if IP is registered - s.mu.Lock() - username, exists := s.Usernames[clientIP] - s.mu.Unlock() - - if !exists { - errorFmt := fmt.Sprintf(`{"error": "IP %s not registered with username"}`, clientIP) - http.Error(w, errorFmt, http.StatusUnauthorized) - return - } - - var msg struct { - Message string `json:"message"` - } - - if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { - http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest) - return - } - - // Add message using the stored username and IP - s.AddMessage(username, clientIP, msg.Message) - - json.NewEncoder(w).Encode(map[string]string{ - "status": "Message received", - "from": username, - }) - default: - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } -} - -func (s *Server) handleUsernameStatus(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } - - w.Header().Set("Content-Type", "application/json") - clientIP := getClientIP(r) - - s.mu.Lock() - username, exists := s.Usernames[clientIP] - s.mu.Unlock() - - json.NewEncoder(w).Encode(map[string]interface{}{ - "hasUsername": exists, - "username": username, - }) -} - -func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } - - w.Header().Set("Content-Type", "application/json") - - clientIP := getClientIP(r) - s.updateActivity(clientIP) - s.cleanupActivity() - s.mu.Lock() - var users []string - for ip := range s.Connected { - users = append(users, s.Usernames[ip]) - } - s.mu.Unlock() - - json.NewEncoder(w).Encode(map[string]interface{}{ - "users": users, - }) -} - -func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.NotFound(w, r) - return - } - w.Header().Set("Content-Type", "text/html") - w.Write(readFile("root.html")) -} - -func readFile(filepath string) []byte { - contents, _ := os.ReadFile(filepath) - return contents -} - -func (s *Server) handleJs(w http.ResponseWriter, r *http.Request) { - _ = r - w.Header().Set("Content-Type", "application/javascript") - w.Write(readFile("root.js")) -} - -func (s *Server) handleCss(w http.ResponseWriter, r *http.Request) { - _ = r - w.Header().Set("Content-Type", "text/css") - w.Write(readFile("root.css")) -} - -func (s *Server) Run() { - handler := http.NewServeMux() - handler.HandleFunc("/ping", s.handlePing) - handler.HandleFunc("/username", s.handleUsername) - handler.HandleFunc("/messages", s.handleMessages) - handler.HandleFunc("/username/status", s.handleUsernameStatus) - handler.HandleFunc("/users", s.handleUsers) - handler.HandleFunc("/", s.handleRoot) - handler.HandleFunc("/root.js", s.handleJs) - handler.HandleFunc("/root.css", s.handleCss) - fmt.Printf("Server starting on %s:%d\n", s.Ip, s.Port) - if err := http.ListenAndServe(fmt.Sprintf("%s:%d", s.Ip, s.Port), GzipMiddleware(handler)); err != nil { - fmt.Printf("Server error: %v\n", err) - } -} - -func main() { - ip := os.Args[1] - port, _ := strconv.Atoi(os.Args[2]) - // ip := "localhost" - // port := 8080 - server := NewServer(ip, port) - server.Run() -} diff --git a/old/main.go.bak b/old/main.go.bak deleted file mode 100644 index cd9773e..0000000 --- a/old/main.go.bak +++ /dev/null @@ -1,366 +0,0 @@ -package main - -import ( - "compress/gzip" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "os" - "strconv" - "strings" - "sync" - "time" -) - -type gzipResponseWriter struct { - http.ResponseWriter - io.Writer -} - -func (g *gzipResponseWriter) Write(data []byte) (int, error) { - return g.Writer.Write(data) -} - -func GzipMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { - next.ServeHTTP(w, r) - return - } - - gz := gzip.NewWriter(w) - defer gz.Close() - - w.Header().Set("Content-Encoding", "gzip") - next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r) - }) -} - -func getClientIP(r *http.Request) string { - if fwdIP := r.Header.Get("X-Forwarded-For"); fwdIP != "" { - return strings.Split(fwdIP, ",")[0] - } - - clientIP := r.RemoteAddr - if host, _, err := net.SplitHostPort(clientIP); err == nil { - return host - } - return clientIP -} - -type Message struct { - Content string - SenderIp string - SenderUsername string - Timestamp string -} - -func NewMessage(username, ip, content string) *Message { - timestamp := time.Now().Format("2006-01-02 15:04:05") - return &Message{ - Content: content, - SenderIp: ip, - SenderUsername: username, - Timestamp: timestamp, - } -} - -// TODO: Use it -func writeMessageToFile(msg Message) { - contents := fmt.Sprintf("%s,%s,%s,%s\n", msg.SenderUsername, msg.SenderIp, msg.Timestamp, msg.Content) - os.WriteFile("messages.txt", []byte(contents), os.ModeAppend) -} - -// TODO: Use it -func writeMessagesToFile(messages []Message) { - var contents string - for _, msg := range messages { - contents += fmt.Sprintf("%s,%s,%s,%s\n", msg.SenderUsername, msg.SenderIp, msg.Timestamp, msg.Content) - } - os.WriteFile("messages.txt", []byte(contents), os.ModeAppend) -} - -// TODO: Use it -func readMessagesFromFile(filepath string) []Message { - contents, _ := os.ReadFile(filepath) - lines := strings.Split(string(contents), "\n") - var messages []Message - for _, line := range lines { - if line == "" { - continue - } - parts := strings.Split(line, ",") - username := parts[0] - timestamp := parts[1] - content := strings.Join(parts[2:], " ") - messages = append(messages, Message{username, "", content, timestamp}) - } - return messages -} - -type Server struct { - Ip string - Port int - Messages []Message - // Add mappings for IPs and usernames - Connected map[string]time.Time // Map IP -> Last activity time - Usernames map[string]string // Map IP -> Username - mu sync.Mutex // For thread safety -} - -func NewServer(ip string, port int) *Server { - return &Server{ - Ip: ip, - Port: port, - Messages: make([]Message, 0), - Connected: make(map[string]time.Time), - Usernames: make(map[string]string), - mu: sync.Mutex{}, - } -} - -func (s *Server) AddMessage(username string, userip string, contents string) { - message := NewMessage(username, userip, contents) - s.Messages = append(s.Messages, *message) -} - -func (s *Server) updateActivity(ip string) { - s.mu.Lock() - defer s.mu.Unlock() - s.Connected[ip] = time.Now() -} - -func (s *Server) cleanupActivity() { - s.mu.Lock() - defer s.mu.Unlock() - for ip, lastActivity := range s.Connected { - if time.Since(lastActivity) > 10*time.Second { - delete(s.Connected, ip) - } - } -} - -func (s *Server) handlePing(w http.ResponseWriter, r *http.Request) { - clientIP := getClientIP(r) - s.updateActivity(clientIP) - s.cleanupActivity() - w.WriteHeader(http.StatusOK) -} - -func validUsername(username string) bool { - for _, c := range username { - if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') { - return false - } - } - return true -} - -func (s *Server) handleUsername(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPut { - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } - - w.Header().Set("Content-Type", "application/json") - - clientIP := getClientIP(r) - - var req struct { - Username string `json:"username"` - } - - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest) - return - } - - s.mu.Lock() - - if len(req.Username) > 64 { - http.Error(w, fmt.Sprintf(`{"error": "Username too long (must be less than 64 characters)"}`), http.StatusRequestEntityTooLarge) - s.mu.Unlock() - return - } - - if !validUsername(req.Username) { - http.Error(w, fmt.Sprintf(`{"error": "Username must only contain alphanumeric characters and/or underscores"}`), http.StatusBadRequest) - s.mu.Unlock() - return - } - - for _, username := range s.Usernames { - if username == req.Username { - s.mu.Unlock() - http.Error(w, fmt.Sprintf(`{"error": "Username already exists"}`), http.StatusConflict) - return - } - } - - if _, ok := s.Usernames[clientIP]; ok { - fmt.Println("Name change detected, fixing messages to reflect the change") - for i, msg := range s.Messages { - if msg.SenderIp == clientIP { - s.Messages[i].SenderUsername = req.Username - } - } - } - s.Usernames[clientIP] = req.Username - - s.mu.Unlock() - - json.NewEncoder(w).Encode(map[string]string{"status": "Username registered"}) - fmt.Println(clientIP, "->", req.Username) -} - -func getMessageTemplate(file string, body string) string { - contents, _ := os.ReadFile(file) - return strings.Replace(string(contents), "{{body}}", body, 1) -} - -func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodGet: - w.Header().Set("Content-Type", "text/html") - - var body string - for _, msg := range s.Messages { - body += fmt.Sprintf(`

%s %s
%s

`, - msg.SenderUsername, msg.Timestamp, msg.Content) - } - - w.Write([]byte(getMessageTemplate("messages.html", body))) - - case http.MethodPut: - w.Header().Set("Content-Type", "application/json") - - // Get client's IP - clientIP := getClientIP(r) - - // Check if IP is registered - s.mu.Lock() - username, exists := s.Usernames[clientIP] - s.mu.Unlock() - - if !exists { - errorFmt := fmt.Sprintf(`{"error": "IP %s not registered with username"}`, clientIP) - http.Error(w, errorFmt, http.StatusUnauthorized) - return - } - - var msg struct { - Message string `json:"message"` - } - - if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { - http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest) - return - } - - // Add message using the stored username and IP - s.AddMessage(username, clientIP, msg.Message) - - json.NewEncoder(w).Encode(map[string]string{ - "status": "Message received", - "from": username, - }) - default: - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } -} - -func (s *Server) handleUsernameStatus(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } - - w.Header().Set("Content-Type", "application/json") - clientIP := getClientIP(r) - - s.mu.Lock() - username, exists := s.Usernames[clientIP] - s.mu.Unlock() - - json.NewEncoder(w).Encode(map[string]interface{}{ - "hasUsername": exists, - "username": username, - }) -} - -func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) - return - } - - w.Header().Set("Content-Type", "application/json") - - clientIP := getClientIP(r) - s.updateActivity(clientIP) - s.cleanupActivity() - s.mu.Lock() - var users []string - for ip := range s.Connected { - users = append(users, s.Usernames[ip]) - } - s.mu.Unlock() - - json.NewEncoder(w).Encode(map[string]interface{}{ - "users": users, - }) -} - -func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.NotFound(w, r) - return - } - w.Header().Set("Content-Type", "text/html") - w.Write(readFile("root.html")) -} - -func readFile(filepath string) []byte { - contents, _ := os.ReadFile(filepath) - return contents -} - -func (s *Server) handleJs(w http.ResponseWriter, r *http.Request) { - _ = r - w.Header().Set("Content-Type", "application/javascript") - w.Write(readFile("root.js")) -} - -func (s *Server) handleCss(w http.ResponseWriter, r *http.Request) { - _ = r - w.Header().Set("Content-Type", "text/css") - w.Write(readFile("root.css")) -} - -func (s *Server) Run() { - handler := http.NewServeMux() - handler.HandleFunc("/ping", s.handlePing) - handler.HandleFunc("/username", s.handleUsername) - handler.HandleFunc("/messages", s.handleMessages) - handler.HandleFunc("/username/status", s.handleUsernameStatus) - handler.HandleFunc("/users", s.handleUsers) - handler.HandleFunc("/", s.handleRoot) - handler.HandleFunc("/root.js", s.handleJs) - handler.HandleFunc("/root.css", s.handleCss) - fmt.Printf("Server starting on %s:%d\n", s.Ip, s.Port) - if err := http.ListenAndServe(fmt.Sprintf("%s:%d", s.Ip, s.Port), GzipMiddleware(handler)); err != nil { - fmt.Printf("Server error: %v\n", err) - } -} - -func main() { - ip := os.Args[1] - port, _ := strconv.Atoi(os.Args[2]) - // ip := "localhost" - // port := 8080 - server := NewServer(ip, port) - server.Run() -} diff --git a/old/messages.html b/old/messages.html deleted file mode 100644 index 2e97c0c..0000000 --- a/old/messages.html +++ /dev/null @@ -1,7 +0,0 @@ - - -
- {{body}} -
- - diff --git a/old/root.css b/old/root.css deleted file mode 100644 index 05ceb78..0000000 --- a/old/root.css +++ /dev/null @@ -1,247 +0,0 @@ -:root { - --radchat-color: #40a02b; - --main-bg-color: #11111b; - --pum-button-inactive-fg: #cdd6f4; - --pum-button-inactive-bg: #11111b; - --pum-button-active-fg: #89b4fa; - --pum-button-active-bg: #1e1e2e; - --pum-title-color: #cdd6f4; - --pum-bg-color: #1e1e2e; - --user-color: #89b4fa; - --timestamp-color: #313244; - --separator-color: #181825; - --message-color: #cdd6f4; - --message-bg-color: #1e1e2e; - --input-bg-color: #181825; - --input-text-color: #cdd6f4; - --input-button-inactive-bg: #b4befe; - --input-button-inactive-fg: #11111b; - --input-button-active-bg: #89b4fa; - --input-button-active-fg: #11111b; -} -body { - font-family: Arial, sans-serif; - margin: 0; - padding: 0; /* Remove padding from body */ - background-color: var(--main-bg-color); - color: var(--pum-title-color); - height: 100vh; - display: flex; - flex-direction: column; - overflow: hidden; /* Prevent body scroll */ -} -.container { - max-width: 800px; - margin: 0 auto; - width: 100%; - position: relative; - height: 100vh; - padding: 20px; /* Move padding here */ - padding-bottom: 80px; /* Space for message input */ - display: flex; - flex-direction: column; -} - -.header-controls { - position: fixed; - top: 20px; - left: 50%; - transform: translateX(-50%); - display: flex; - gap: 20px; /* Space between buttons */ - align-items: center; - justify-content: center; -} - -@media (max-width: 768px) { - .header-controls { - gap: 10px; - } -} - -.settings-button, .users-button { - top: 20px; - background: none; - border: none; - cursor: pointer; - padding: 5px; - border-radius: 50%; - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - background-color: var(--pum-button-inactive-bg); -} - -.settings-button { - right: calc(48%); -} - -.users-button { - right: calc(52%); -} - -.settings-icon, .users-icon { - width: 24px; - height: 24px; - fill: var(--pum-button-inactive-fg); -} - -.settings-button:hover, .users-button:hover { - background-color: var(--pum-button-active-bg); -} - -.settings-button:hover .settings-icon, -.users-button:hover .users-icon { - fill: var(--pum-button-active-fg); -} - -#users-list { - margin-top: 0px; -} - -.current-user { - position: absolute; - right: 0; - top: 28px; - color: var(--user-color); - font-weight: bold; -} - -.username-section, .users-section { - position: fixed; - transform: translateX(-50%); - top: 70px; - padding: 15px; - background-color: var(--pum-bg-color); - border-radius: 5px; - box-shadow: 0 2px 10px rgba(0,0,0,0.3); - z-index: 100; - display: none; - min-width: 250px; -} - -.username-section { - right: 50%; - transform: translateX(10%); -} - -.users-section { - right: 50%; - transform: translateX(-10%); -} - -.users-section h3 { - margin-top: 0; - margin-bottom: 10px; -} - -.user-item { - padding: 8px 0; - color: var(--user-color); - border-bottom: 1px solid var(--separator-color); -} - -.user-item:last-child { - border-bottom: none; -} -.messages-section { - flex-grow: 1; - margin-top: 60px; /* Space for gear icon */ - margin-bottom: 20px; - overflow-y: auto; - height: 0; /* Allow flex-grow to work */ -} -.message { - padding: 10px 0; -} -.message .username { - color: var(--user-color); - font-weight: bold; -} -.message .timestamp { - color: var(--timestamp-color); - font-weight: thin; - font-size: 0.8em; -} - -.message .content { - margin-top: 5px; - color: var(--message-color); - word-wrap: break-word; /* Handle long words */ - word-break: break-word; /* Break words at arbitrary points if needed */ - white-space: pre-wrap; /* Preserve whitespace and wraps */ - max-width: 100%; /* Ensure it doesn't exceed container width */ -} -.message-section { - position: fixed; - bottom: 0; - left: 0; - right: 0; - padding: 20px; - background-color: var(--message-bg-color); - z-index: 100; /* Ensure it stays on top */ -} - -.message-container { - max-width: 800px; - margin: 0 auto; - display: flex; - gap: 10px; -} -input { - padding: 10px; - border: none; - border-radius: 4px; - background-color: var(--input-bg-color); - color: var(--input-text-color); - flex-grow: 1; -} -button { - padding: 10px 20px; - border: none; - border-radius: 4px; - background-color: var(--input-button-inactive-bg); - color: var(--input-button-inactive-fg); - cursor: pointer; -} -button:hover { - background-color: var(--input-button-active-bg); - color: var(--input-button-active-fg) -} - -button.scroll { - padding: 10px 20px; - border-radius: 4px; - border: none; - background-color: var(--input-button-inactive-bg); - padding: 10px; - cursor: pointer; -} - -button.scroll:hover { - background-color: var(--input-button-active-bg); -} - -.scroll-icon { - width: 14px; - height: 14px; - fill: var(--var-input-button-inactive-fg); -} - -.scroll-icon:hover { - fill: var(--var-input-button-active-fg); -} - -.radchat { - position: absolute; - left: 0; - font-family: Arial, sans-serif; - font-size: 2em; - font-weight: bold; - color: var(--radchat-color); -} -#status, #username-status { - margin-top: 10px; -} diff --git a/old/root.html b/old/root.html deleted file mode 100644 index 9ad3beb..0000000 --- a/old/root.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - RadChat - - - - -
-
- RadChat -
-
- - -
-
- -
-

Online Users

-
- -
-
- -
-

Username

-
- - -
-
-
- -
- -
-
- -
-
- - - -
-
-
- - - diff --git a/old/root.js b/old/root.js deleted file mode 100644 index f144121..0000000 --- a/old/root.js +++ /dev/null @@ -1,244 +0,0 @@ -function toggleSettings() { - const panel = document.getElementById('settings-panel'); - panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; - if (panel.style.display === 'block') { - const username = document.getElementById('username'); - username.focus(); - username.selectionStart = username.selectionEnd = username.value.length - } -} - -function toggleUsers() { - const panel = document.getElementById('users-panel'); - panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; -} - -document.addEventListener('click', function(event) { - const settingsPanel = document.getElementById('settings-panel'); - const settingsButton = document.querySelector('.settings-button'); - const usersPanel = document.getElementById('users-panel'); - const usersButton = document.querySelector('.users-button'); - - if (!settingsPanel.contains(event.target) && !settingsButton.contains(event.target)) { - settingsPanel.style.display = 'none'; - } - - if (!usersPanel.contains(event.target) && !usersButton.contains(event.target)) { - usersPanel.style.display = 'none'; - } -}); - -document.addEventListener('keypress', function(event) { - if (event.key === 'Enter') { - const settingsPanel = document.getElementById('settings-panel'); - const inputPanel = document.getElementById('message'); - if (settingsPanel.contains(event.target)) { - setUsername(); - } - if (inputPanel.contains(event.target)) { - sendMessage(); - } - } -}); - -async function loadUsers() { - try { - const response = await fetch('/users'); - const data = await response.json(); - - const usersList = document.getElementById('users-list'); - usersList.innerHTML = ''; - - data.users.sort().forEach(user => { - const userDiv = document.createElement('div'); - userDiv.className = 'user-item'; - userDiv.textContent = user; - usersList.appendChild(userDiv); - }); - } catch (error) { - console.error('Error loading users:', error); - } -} - - -let lastMessageCount = 0; -async function loadMessages() { - try { - const messagesDiv = document.getElementById('messages'); - const response = await fetch('/messages'); - const text = await response.text(); - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = text; - - if (messagesDiv.scrollTop != bottom) { - // show a button to scroll to the bottom - const scrollToBottomButton = document.getElementById('scroll'); - scrollToBottomButton.style.display = 'block'; - scrollToBottomButton.onclick = scrollToBottom; - } else { - // hide the button - const scrollToBottomButton = document.getElementById('scroll'); - scrollToBottomButton.style.display = 'none'; - } - - const messages = tempDiv.getElementsByTagName('p'); - - const newMessageCount = messages.length; - - if (newMessageCount > lastMessageCount || lastMessageCount === 0) { - messagesDiv.innerHTML = ''; - Array.from(messages).forEach(msg => { - const messageDiv = document.createElement('div'); - messageDiv.className = 'message'; - const [username, content] = msg.innerHTML.split('
'); - messageDiv.innerHTML = '
' + username + '
' + - '
' + content + '
'; - messagesDiv.appendChild(messageDiv); - }); - scrollToBottom(); - lastMessageCount = newMessageCount; - } - } catch (error) { - console.error('Error loading messages:', error); - } -} - -var bottom = 0; - -function scrollToBottom() { - const messagesDiv = document.getElementById('messages'); - messagesDiv.scrollTop = messagesDiv.scrollHeight; - bottom = messagesDiv.scrollTop; -} - -async function checkUsername() { - try { - const response = await fetch('/username/status'); - const data = await response.json(); - if (!data.hasUsername) { - document.getElementById('settings-panel').style.display = 'block' - const username = document.getElementById('username'); - username.focus(); - username.selectionStart = username.selectionEnd = username.value.length - } - } catch (error) { - console.error('Error checking username status:', error); - } -} - -async function updateCurrentUser() { - try { - const response = await fetch('/username/status'); - const data = await response.json(); - const userDiv = document.getElementById('current-user'); - if (data.hasUsername) { - userDiv.textContent = data.username; - } else { - userDiv.textContent = ''; - } - } catch (error) { - console.error('Error getting username:', error); - } -} - -async function setUsername() { - const username = document.getElementById('username').value; - if (!username) { - showUsernameStatus('Please enter a username', 'red'); - return; - } - - try { - const response = await fetch('/username', { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username: username }) - }); - - const data = await response.json(); - - if (response.ok) { - showUsernameStatus('Username set successfully!', 'green'); - updateCurrentUser(); - setTimeout(() => { - document.getElementById('settings-panel').style.display = 'none'; - }, 500); - location.reload(); - } else { - showUsernameStatus(data.error || 'Failed to set username', 'red'); - } - } catch (error) { - showUsernameStatus('Error connecting to server', 'red'); - } -} - -async function sendMessage() { - const message = document.getElementById('message').value; - if (!message) { - return; - } - - try { - const response = await fetch('/messages', { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ message: message }) - }); - - const data = await response.json(); - - if (response.ok) { - document.getElementById('message').value = ''; - // showStatus('Message sent!', '#64b5f6'); - loadMessages(); - } else { - showStatus(data.error || 'Failed to send message', 'red'); - } - } catch (error) { - showStatus('Error connecting to server', 'red'); - } -} - -function showStatus(message, color) { - const status = document.getElementById('status'); - status.textContent = message; - status.style.color = color; - setTimeout(() => { - status.textContent = ''; - }, 3000); -} - -function showUsernameStatus(message, color) { - const status = document.getElementById('username-status'); - status.textContent = message; - status.style.color = color; - setTimeout(() => { - status.textContent = ''; - }, 3000); -} - -async function pingCheck() { - try { - await fetch('/ping', {method: 'POST'}); - } catch (error) { - console.error('Ping failed:', error); - } -} - -async function initialize() { - await loadMessages(); - document.getElementById('users-panel').style.display = 'none'; - document.getElementById('settings-panel').style.display = 'none'; - checkUsername(); - updateCurrentUser(); - setInterval(loadMessages, 1000); - setInterval(loadUsers, 1000); - setInterval(pingCheck, 3000); - scrollToBottom(); -} - -initialize(); diff --git a/notes/todo.md b/readme.md similarity index 100% rename from notes/todo.md rename to readme.md