diff --git a/app.go b/app.go
index 31bb295..3675453 100644
--- a/app.go
+++ b/app.go
@@ -7,8 +7,9 @@ import (
"log"
"os"
"sync"
+ "time"
- "github.com/tylertravisty/go-utils/random"
+ "github.com/tylertravisty/rum-goggles/internal/api"
"github.com/tylertravisty/rum-goggles/internal/config"
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
)
@@ -22,17 +23,20 @@ type App struct {
ctx context.Context
cfg *config.App
cfgMu sync.Mutex
+ api *api.Api
+ apiMu sync.Mutex
}
// NewApp creates a new App application struct
func NewApp() *App {
- return &App{}
+ return &App{api: api.NewApi()}
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
+ a.api.Startup(ctx)
err := a.loadConfig()
if err != nil {
// TODO: handle error better on startup
@@ -55,7 +59,7 @@ func (a *App) loadConfig() error {
}
func (a *App) newConfig() error {
- cfg := &config.App{Channels: []config.Channel{}}
+ cfg := &config.App{Channels: map[string]config.Channel{}}
err := cfg.Save(configFilepath)
if err != nil {
return fmt.Errorf("error saving new config: %v", err)
@@ -93,11 +97,15 @@ func (a *App) AddChannel(url string) (*config.App, error) {
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.NewChannel(url, name)
+ if err != nil {
+ // TODO: log error
+ fmt.Println("error creating new channel:", err)
+ return nil, fmt.Errorf("error creating new channel")
+ }
+
err = a.cfg.Save(configFilepath)
if err != nil {
// TODO: log error
@@ -108,26 +116,24 @@ func (a *App) AddChannel(url string) (*config.App, error) {
return a.cfg, nil
}
-// Greet returns a greeting for the given name
-func (a *App) Greet(name string) string {
- random, err := random.String(10)
- if err != nil {
- fmt.Println("random.Alphabetic err:", err)
- return name
+func (a *App) StartApi(cid string) error {
+ channel, found := a.cfg.Channels[cid]
+ if !found {
+ // TODO: log error
+ fmt.Println("could not find channel CID:", cid)
+ return fmt.Errorf("channel CID not found")
}
- //return fmt.Sprintf("Hello %s, It's show time!", name)
- return fmt.Sprintf("Hello %s, It's show time!", random)
+
+ err := a.api.Start(channel.ApiUrl, channel.Interval*time.Second)
+ if err != nil {
+ // TODO: log error
+ fmt.Println("error starting api:", err)
+ return fmt.Errorf("error starting API")
+ }
+
+ return nil
}
-// func (a *App) QueryAPI(url string) (*rumblelivestreamlib.Followers, error) {
-// fmt.Println("QueryAPI")
-// client := rumblelivestreamlib.Client{StreamKey: url}
-// resp, err := client.Request()
-// if err != nil {
-// // TODO: log error
-// fmt.Println("client.Request err:", err)
-// return nil, fmt.Errorf("API request failed")
-// }
-
-// return &resp.Followers, nil
-// }
+func (a *App) StopApi() {
+ a.api.Stop()
+}
diff --git a/frontend/src/components/ChannelList.jsx b/frontend/src/components/ChannelList.jsx
index ea11bca..8a64348 100644
--- a/frontend/src/components/ChannelList.jsx
+++ b/frontend/src/components/ChannelList.jsx
@@ -3,8 +3,13 @@ import './ChannelList.css';
function ChannelList(props) {
const sortChannelsAlpha = () => {
- let sorted = [...props.channels].sort((a, b) =>
- a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
+ let keys = Object.keys(props.channels);
+ // let sorted = [...props.channels].sort((a, b) =>
+ // a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
+ // );
+
+ let sorted = [...keys].sort((a, b) =>
+ props.channels[a].name.toLowerCase() > props.channels[b].name.toLowerCase() ? 1 : -1
);
return sorted;
};
@@ -17,9 +22,9 @@ function ChannelList(props) {
))}
diff --git a/frontend/src/screens/Dashboard.jsx b/frontend/src/screens/Dashboard.jsx
index 98c3ff0..643a54a 100644
--- a/frontend/src/screens/Dashboard.jsx
+++ b/frontend/src/screens/Dashboard.jsx
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
-import { Start, Stop } from '../../wailsjs/go/api/Api';
+import { StartApi, StopApi } from '../../wailsjs/go/main/App';
import './Dashboard.css';
import { EventsEmit, EventsOn } from '../../wailsjs/runtime/runtime';
@@ -17,7 +17,7 @@ function Dashboard() {
const navigate = useNavigate();
const [refresh, setRefresh] = useState(false);
const [active, setActive] = useState(false);
- const [streamKey, setStreamKey] = useState(location.state.streamKey);
+ const [cid, setCID] = useState(location.state.cid);
const [username, setUsername] = useState('');
const [channelName, setChannelName] = useState('');
const [followers, setFollowers] = useState({});
@@ -41,7 +41,7 @@ function Dashboard() {
useEffect(() => {
console.log('use effect start');
- Start(streamKey);
+ StartApi(cid);
setActive(true);
EventsOn('QueryResponse', (response) => {
@@ -77,7 +77,7 @@ function Dashboard() {
}, []);
const home = () => {
- Stop()
+ StopApi()
.then(() => setActive(false))
.then(() => {
navigate(NavSignIn);
@@ -89,7 +89,7 @@ function Dashboard() {
const startQuery = () => {
console.log('start');
- Start(streamKey)
+ StartApi(cid)
.then(() => {
setActive(true);
})
@@ -100,7 +100,7 @@ function Dashboard() {
const stopQuery = () => {
console.log('stop');
- Stop().then(() => {
+ StopApi().then(() => {
setActive(false);
});
};
diff --git a/frontend/src/screens/SignIn.jsx b/frontend/src/screens/SignIn.jsx
index a8a8072..39538c6 100644
--- a/frontend/src/screens/SignIn.jsx
+++ b/frontend/src/screens/SignIn.jsx
@@ -8,7 +8,7 @@ import ChannelList from '../components/ChannelList';
function SignIn() {
const navigate = useNavigate();
- const [config, setConfig] = useState({ channels: [] });
+ const [config, setConfig] = useState({ channels: {} });
const [streamKey, setStreamKey] = useState('');
const updateStreamKey = (event) => setStreamKey(event.target.value);
const [showStreamKey, setShowStreamKey] = useState(false);
@@ -17,10 +17,10 @@ function SignIn() {
useEffect(() => {
Config()
.then((response) => {
- console.log(response);
setConfig(response);
})
.catch((err) => {
+ // TODO: display error to user
console.log('error getting config', err);
});
}, []);
@@ -37,8 +37,8 @@ function SignIn() {
});
};
- const openStreamDashboard = (key) => {
- navigate(NavDashboard, { state: { streamKey: key } });
+ const openStreamDashboard = (cid) => {
+ navigate(NavDashboard, { state: { cid: cid } });
};
return (
diff --git a/internal/api/api.go b/internal/api/api.go
index 99a78fa..77e0483 100644
--- a/internal/api/api.go
+++ b/internal/api/api.go
@@ -11,27 +11,22 @@ import (
)
type Api struct {
- ctx context.Context
- cancel context.CancelFunc
- cancelMu sync.Mutex
- querying bool
- queryingMu sync.Mutex
- queryInterval time.Duration
- queryIntervalMu sync.Mutex
+ ctx context.Context
+ cancel context.CancelFunc
+ cancelMu sync.Mutex
+ querying bool
+ queryingMu sync.Mutex
}
func NewApi() *Api {
- return &Api{queryInterval: 10 * time.Second}
+ return &Api{}
}
func (a *Api) Startup(ctx context.Context) {
a.ctx = ctx
- runtime.EventsOn(ctx, "StopQuery", func(optionalData ...interface{}) {
- a.Stop()
- })
}
-func (a *Api) Start(url string) error {
+func (a *Api) Start(url string, interval time.Duration) error {
fmt.Println("Api.Start")
if url == "" {
return fmt.Errorf("empty stream key")
@@ -48,7 +43,7 @@ func (a *Api) Start(url string) error {
a.cancelMu.Lock()
a.cancel = cancel
a.cancelMu.Unlock()
- a.start(ctx, url)
+ go a.start(ctx, url, interval)
} else {
fmt.Println("Querying already started")
}
@@ -65,12 +60,9 @@ func (a *Api) Stop() {
a.cancelMu.Unlock()
}
-func (a *Api) start(ctx context.Context, url string) {
+func (a *Api) start(ctx context.Context, url string, interval time.Duration) {
for {
a.query(url)
- a.queryIntervalMu.Lock()
- interval := a.queryInterval
- a.queryIntervalMu.Unlock()
timer := time.NewTimer(interval)
select {
case <-ctx.Done():
diff --git a/internal/config/config.go b/internal/config/config.go
index 7e8e351..d82d435 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -4,15 +4,39 @@ import (
"encoding/json"
"fmt"
"os"
+ "time"
+
+ "github.com/tylertravisty/go-utils/random"
+)
+
+const (
+ CIDLen = 8
+ DefaultInterval = 10
)
type Channel struct {
- ApiUrl string `json:"api_url"`
- Name string `json:"name"`
+ ID string `json:"id"`
+ ApiUrl string `json:"api_url"`
+ Name string `json:"name"`
+ Interval time.Duration `json:"interval"`
+}
+
+func (a *App) NewChannel(url string, name string) (string, error) {
+ for {
+ id, err := random.String(CIDLen)
+ if err != nil {
+ return "", fmt.Errorf("config: error generating ID: %v", err)
+ }
+
+ if _, exists := a.Channels[id]; !exists {
+ a.Channels[id] = Channel{id, url, name, DefaultInterval}
+ return id, nil
+ }
+ }
}
type App struct {
- Channels []Channel `json:"channels"`
+ Channels map[string]Channel `json:"channels"`
}
func Load(filepath string) (*App, error) {
diff --git a/main.go b/main.go
index a848514..e9600bb 100644
--- a/main.go
+++ b/main.go
@@ -1,10 +1,8 @@
package main
import (
- "context"
"embed"
- "github.com/tylertravisty/rum-goggles/internal/api"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
@@ -15,7 +13,6 @@ var assets embed.FS
func main() {
// Create an instance of the app structure
- api := api.NewApi()
app := NewApp()
// Create application with options
@@ -27,13 +24,9 @@ func main() {
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 255},
- OnStartup: func(ctx context.Context) {
- app.startup(ctx)
- api.Startup(ctx)
- },
+ OnStartup: app.startup,
Bind: []interface{}{
app,
- api,
},
})