diff --git a/.gitignore b/.gitignore index 9f51806..e076fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ node_modules frontend/dist frontend/wailsjs -.prettierignore \ No newline at end of file +.prettierignore + +config.json \ No newline at end of file diff --git a/app.go b/app.go index cba1dda..31bb295 100644 --- a/app.go +++ b/app.go @@ -2,14 +2,26 @@ package main import ( "context" + "errors" "fmt" + "log" + "os" + "sync" "github.com/tylertravisty/go-utils/random" + "github.com/tylertravisty/rum-goggles/internal/config" + rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go" +) + +const ( + configFilepath = "./config.json" ) // App struct type App struct { - ctx context.Context + ctx context.Context + cfg *config.App + cfgMu sync.Mutex } // NewApp creates a new App application struct @@ -21,6 +33,79 @@ func NewApp() *App { // so we can call the runtime methods func (a *App) startup(ctx context.Context) { a.ctx = ctx + err := a.loadConfig() + if err != nil { + // TODO: handle error better on startup + log.Fatal("error loading config: ", err) + } +} + +func (a *App) loadConfig() error { + cfg, err := config.Load(configFilepath) + if err != nil { + if !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("error loading config: %v", err) + } + + return a.newConfig() + } + + a.cfg = cfg + return nil +} + +func (a *App) newConfig() error { + cfg := &config.App{Channels: []config.Channel{}} + err := cfg.Save(configFilepath) + if err != nil { + return fmt.Errorf("error saving new config: %v", err) + } + + a.cfg = cfg + return nil +} + +func (a *App) Config() *config.App { + return a.cfg +} + +func (a *App) SaveConfig() error { + err := a.cfg.Save(configFilepath) + if err != nil { + // TODO: log error; return user error + return fmt.Errorf("Error saving config") + } + + return nil +} + +func (a *App) AddChannel(url string) (*config.App, error) { + client := rumblelivestreamlib.Client{StreamKey: url} + resp, err := client.Request() + if err != nil { + // TODO: log error + fmt.Println("error requesting api:", err) + return nil, fmt.Errorf("error querying API") + } + + name := resp.Username + if resp.ChannelName != "" { + name = resp.ChannelName + } + + channel := config.Channel{ApiUrl: url, Name: name} + + a.cfgMu.Lock() + defer a.cfgMu.Unlock() + a.cfg.Channels = append(a.cfg.Channels, channel) + err = a.cfg.Save(configFilepath) + if err != nil { + // TODO: log error + fmt.Println("error saving config:", err) + return nil, fmt.Errorf("error saving new channel") + } + + return a.cfg, nil } // Greet returns a greeting for the given name diff --git a/frontend/src/assets/icons/index.jsx b/frontend/src/assets/icons/index.jsx index e506d73..6e2b0b5 100644 --- a/frontend/src/assets/icons/index.jsx +++ b/frontend/src/assets/icons/index.jsx @@ -5,6 +5,7 @@ import heart from './heart-fill.png'; import house from './house.png'; import pause from './pause-fill.png'; import play from './play-fill.png'; +import plus_circle from './plus-circle-fill.png'; import star from './star-fill.png'; import thumbs_down from './hand-thumbs-down.png'; import thumbs_up from './hand-thumbs-up.png'; @@ -16,6 +17,7 @@ export const Heart = heart; export const House = house; export const Pause = pause; export const Play = play; +export const PlusCircle = plus_circle; export const Star = star; export const ThumbsDown = thumbs_down; export const ThumbsUp = thumbs_up; diff --git a/frontend/src/assets/icons/plus-circle-fill.png b/frontend/src/assets/icons/plus-circle-fill.png new file mode 100644 index 0000000..785e701 Binary files /dev/null and b/frontend/src/assets/icons/plus-circle-fill.png differ diff --git a/frontend/src/components/ChannelList.css b/frontend/src/components/ChannelList.css new file mode 100644 index 0000000..7cbd1a9 --- /dev/null +++ b/frontend/src/components/ChannelList.css @@ -0,0 +1,66 @@ +.channel-list { + align-items: center; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + width: 100%; +} + +.channel-list-title { + color: #85c742; + font-family: sans-serif; + font-size: 24px; + font-weight: bold; + padding: 5px; +} + +.channels { + background-color: white; + border: 1px solid #D6E0EA; + border-radius: 5px; + height: 100%; + overflow: auto; + width: 100%; +} + +.channel { + align-items: center; + /* border-top: 1px solid #D6E0EA; */ + display: flex; +} + +.channel-add { + background-color: #f3f5f8; + border: none; + padding: 10px; +} + +.channel-add:hover { + cursor: pointer; +} + +.channel-add-icon { + height: 36px; + width: 36px; +} + +.channel-button { + background-color: white; + border: none; + border-radius: 5px; + color: #061726; + font-family: sans-serif; + font-size: 24px; + font-weight: bold; + overflow: hidden; + padding: 10px 10px; + text-align: left; + white-space: nowrap; + width: 100%; +} + +.channel-button:hover { + background-color: #85c742; + cursor: pointer; +} \ No newline at end of file diff --git a/frontend/src/components/ChannelList.jsx b/frontend/src/components/ChannelList.jsx new file mode 100644 index 0000000..ea11bca --- /dev/null +++ b/frontend/src/components/ChannelList.jsx @@ -0,0 +1,34 @@ +import { PlusCircle } from '../assets/icons'; +import './ChannelList.css'; + +function ChannelList(props) { + const sortChannelsAlpha = () => { + let sorted = [...props.channels].sort((a, b) => + a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1 + ); + return sorted; + }; + + return ( +