diff --git a/internal/database/createDb.sql b/internal/database/createDb.sql index 55e3a67..78a9b77 100644 --- a/internal/database/createDb.sql +++ b/internal/database/createDb.sql @@ -14,4 +14,10 @@ create table if not exists Chapter ( Number integer not null, TimeStampUnixEpoch integer not null, foreign key(MangaID) references Manga(ID) +); + +create table if not exists Setting ( + Name text not null primary key, + Value text, + DefaultValue text not null ); \ No newline at end of file diff --git a/internal/database/database.go b/internal/database/database.go index 1cdabd9..879abed 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -11,6 +11,7 @@ type Manager struct { db *sql.DB Mangas DbTable[int, Manga] Chapters DbTable[int, Chapter] + Settings DbTable[string, Setting] CreateIfNotExists bool } @@ -18,8 +19,9 @@ func NewDatabase(connectionString string, createIfNotExists bool) Manager { return Manager{ ConnectionString: connectionString, db: nil, - Mangas: NewDbTable[int, Manga](updateManga, insertManga, loadMangas, deleteManga), - Chapters: NewDbTable[int, Chapter](updateChapter, insertChapter, loadChapters, deleteChapter), + Mangas: NewDbTable(updateManga, insertManga, loadMangas, deleteManga), + Chapters: NewDbTable(updateChapter, insertChapter, loadChapters, deleteChapter), + Settings: NewDbTable(updateSetting, insertSetting, loadSettings, deleteSetting), CreateIfNotExists: createIfNotExists, } } @@ -75,8 +77,12 @@ func (dbMgr *Manager) Save() error { if err != nil { return err } + err = dbMgr.Chapters.Save(dbMgr.db) + if err != nil { + return err + } - return dbMgr.Chapters.Save(dbMgr.db) + return dbMgr.Settings.Save(dbMgr.db) } //go:embed createDb.sql @@ -98,5 +104,11 @@ func (dbMgr *Manager) load() error { return err } + err = dbMgr.Settings.Load(dbMgr.db) + if err != nil { + return err + } + initSettings(&dbMgr.Settings) + return nil } diff --git a/internal/database/setting.go b/internal/database/setting.go new file mode 100644 index 0000000..deb3d39 --- /dev/null +++ b/internal/database/setting.go @@ -0,0 +1,65 @@ +package database + +import ( + "database/sql" +) + +type Setting struct { + Name string + Value string + Default string +} + +func NewSetting(name string, defaultValue string) Setting { + return Setting{ + Name: name, + Value: defaultValue, + Default: defaultValue, + } +} + +func initSettings(settings *DbTable[string, Setting]) { + addSettingIfNotExists(NewSetting("theme", "white"), settings) + addSettingIfNotExists(NewSetting("order", "title"), settings) +} + +func addSettingIfNotExists(setting Setting, settings *DbTable[string, Setting]) { + _, exists := settings.Get(setting.Name) + if !exists { + settings.Set(setting.Name, setting) + } +} + +func updateSetting(db *sql.DB, s *Setting) error { + const cmd = "UPDATE Setting set Value = ? WHERE Name = ?" + _, err := db.Exec(cmd, s.Value, s.Name) + return err +} + +func insertSetting(db *sql.DB, s *Setting) error { + const cmd = "INSERT INTO Setting(Name, Value, DefaultValue) VALUES(?, ?, ?)" + _, err := db.Exec(cmd, s.Name, s.Value, s.Default) + return err +} + +func loadSettings(db *sql.DB) (map[string]Setting, error) { + const cmd = "SELECT Name, Value, DefaultValue from Setting" + rows, err := db.Query(cmd) + + res := make(map[string]Setting) + + for rows.Next() { + setting := Setting{} + if err = rows.Scan(&setting.Name, &setting.Value, &setting.Default); err != nil { + return nil, err + } + res[setting.Name] = setting + } + return res, err +} + +func deleteSetting(db *sql.DB, key string) error { + const cmd = "UPDATE Setting set Value = DefaultValue WHERE Name = ?" + _, err := db.Exec(cmd, key) + return err +} diff --git a/internal/database/table.go b/internal/database/table.go index d259bd3..b43e377 100644 --- a/internal/database/table.go +++ b/internal/database/table.go @@ -88,6 +88,16 @@ func (d *DbTable[K, T]) All() []T { return res } +func (d *DbTable[K, T]) Map() map[K]T { + d.mutex.Lock() + defer d.mutex.Unlock() + res := make(map[K]T, len(d.items)) + for k, manga := range d.items { + res[k] = manga + } + return res +} + func (d *DbTable[K, T]) Delete(db *sql.DB, key K) error { d.mutex.Lock() defer d.mutex.Unlock() diff --git a/internal/server/handler.go b/internal/server/handler.go index 36c820c..ce2e89c 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -136,7 +136,8 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { fmt.Printf("Sorting took %d ms\n", (nex-n)/1000000) menuViewModel := view.MenuViewModel{ - Mangas: mangaViewModels, + Settings: s.DbMgr.Settings.Map(), + Mangas: mangaViewModels, } err := tmpl.Execute(w, menuViewModel) @@ -336,6 +337,23 @@ func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/current/", http.StatusTemporaryRedirect) } +func (s *Server) HandleSetting(w http.ResponseWriter, r *http.Request) { + settingName := r.PostFormValue("setting") + settingValue := r.PostFormValue(settingName) + + setting, ok := s.DbMgr.Settings.Get(settingName) + if !ok { + s.DbMgr.Settings.Set(settingName, database.NewSetting(settingName, settingValue)) + } else { + if setting.Value != settingValue { + setting.Value = settingValue + s.DbMgr.Settings.Set(settingName, setting) + } + } + + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) +} + func (s *Server) HandleNewQuery(w http.ResponseWriter, r *http.Request) { sub := r.PostFormValue("subUrl") diff --git a/internal/server/server.go b/internal/server/server.go index a75db0b..2a80c84 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -58,6 +58,7 @@ func (s *Server) Start(port int) error { http.HandleFunc("POST /exit", s.HandleExit) http.HandleFunc("POST /delete", s.HandleDelete) http.HandleFunc("/favicon.ico", s.HandleFavicon) + http.HandleFunc("POST /setting/", s.HandleSetting) // Update Latest Chapter every 5 Minutes go func(s *Server) { diff --git a/internal/view/Views/menu.gohtml b/internal/view/Views/menu.gohtml index 07b090b..d95e978 100644 --- a/internal/view/Views/menu.gohtml +++ b/internal/view/Views/menu.gohtml @@ -12,7 +12,7 @@ font-size: 25px; } - .dark-mode { + .dark { background-color: #171717; color: white; } @@ -101,17 +101,31 @@ text-align: center; } + label { + display: flex; + align-content: center; + justify-content: center; + margin: 0 auto; + } + + select { + width: 10em; + margin-bottom: 10px; + margin-top: 10px; + } - +
- + +
+ + + +
diff --git a/internal/view/viewmodels.go b/internal/view/viewmodels.go index 254fd55..d15f599 100644 --- a/internal/view/viewmodels.go +++ b/internal/view/viewmodels.go @@ -1,5 +1,7 @@ package view +import "mangaGetter/internal/database" + type Image struct { Path string Index int @@ -21,5 +23,6 @@ type MangaViewModel struct { } type MenuViewModel struct { - Mangas []MangaViewModel + Settings map[string]database.Setting + Mangas []MangaViewModel }