Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: unify polling mechanism #187

Merged
merged 6 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 19 additions & 80 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import (
"os/exec"
"runtime"
"strings"
"time"

"github.com/hashicorp/go-version"
wailsRuntime "github.com/wailsapp/wails/v2/pkg/runtime"

"github.com/williamsjokvist/cfn-tracker/pkg/browser"
"github.com/williamsjokvist/cfn-tracker/pkg/config"
Expand All @@ -25,16 +23,16 @@ import (
"github.com/williamsjokvist/cfn-tracker/pkg/storage/nosql"
"github.com/williamsjokvist/cfn-tracker/pkg/storage/sql"
"github.com/williamsjokvist/cfn-tracker/pkg/storage/txt"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker/sf6"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker/sfv"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker/t8"
)

type CmdHandler interface {
SetContext(ctx context.Context)
}

// The CommandHandler is the interface between the GUI and the core
type CommandHandler struct {
ctx context.Context
tracker tracker.GameTracker
ctx context.Context

browser *browser.Browser

sqlDb *sql.Storage
Expand All @@ -44,6 +42,8 @@ type CommandHandler struct {
cfg *config.Config
}

var _ CmdHandler = (*CommandHandler)(nil)

func NewCommandHandler(browser *browser.Browser, sqlDb *sql.Storage, nosqlDb *nosql.Storage, txtDb *txt.Storage, cfg *config.Config) *CommandHandler {
return &CommandHandler{
sqlDb: sqlDb,
Expand All @@ -59,26 +59,6 @@ func (ch *CommandHandler) SetContext(ctx context.Context) {
ch.ctx = ctx
}

func (ch *CommandHandler) GetAppVersion() string {
return ch.cfg.AppVersion
}

func (ch *CommandHandler) GetTranslation(locale string) (*locales.Localization, error) {
lng, err := i18n.GetTranslation(locale)
if err != nil {
log.Println(err)
if !errorsx.ContainsFormattedError(err) {
err = errorsx.NewFormattedError(http.StatusNotFound, fmt.Errorf(`failed to get translation %w`, err))
}
return nil, err
}
return lng, nil
}

func (ch *CommandHandler) GetSupportedLanguages() []string {
return i18n.GetSupportedLanguages()
}

func (ch *CommandHandler) CheckForUpdate() (bool, error) {
currentVersion, err := version.NewVersion(ch.cfg.AppVersion)
if err != nil {
Expand All @@ -96,21 +76,20 @@ func (ch *CommandHandler) CheckForUpdate() (bool, error) {
return hasUpdate, nil
}

func (ch *CommandHandler) StopTracking() {
log.Println(`Stopped tracking`)
ch.tracker.Stop()
}

func (ch *CommandHandler) StartTracking(cfn string, restore bool) error {
log.Printf(`Starting tracking for %s, restoring = %v`, cfn, restore)
err := ch.tracker.Start(ch.ctx, cfn, restore, 30*time.Second)
func (ch *CommandHandler) GetTranslation(locale string) (*locales.Localization, error) {
lng, err := i18n.GetTranslation(locale)
if err != nil {
log.Println(err)
if !errorsx.ContainsFormattedError(err) {
err = errorsx.NewFormattedError(http.StatusInternalServerError, fmt.Errorf(`failed to start tracking %w`, err))
err = errorsx.NewFormattedError(http.StatusNotFound, fmt.Errorf(`failed to get translation %w`, err))
}
return nil, err
}
return err
return lng, nil
}

func (ch *CommandHandler) GetAppVersion() string {
return ch.cfg.AppVersion
}

func (ch *CommandHandler) OpenResultsDirectory() {
Expand Down Expand Up @@ -192,44 +171,8 @@ func (ch *CommandHandler) GetThemes() ([]model.Theme, error) {
return combinedThemes, nil
}

func (ch *CommandHandler) SelectGame(game string) error {
var username, password string

switch game {
case tracker.GameTypeT8.String():
ch.tracker = t8.NewT8Tracker(ch.sqlDb, ch.txtDb)
case tracker.GameTypeSF6.String():
ch.tracker = sf6.NewSF6Tracker(ch.browser, ch.sqlDb, ch.txtDb)
username = ch.cfg.CapIDEmail
password = ch.cfg.CapIDPassword
case tracker.GameTypeSFV.String():
ch.tracker = sfv.NewSFVTracker(ch.browser)
username = ch.cfg.SteamUsername
password = ch.cfg.SteamPassword
default:
return errorsx.NewFormattedError(http.StatusInternalServerError, fmt.Errorf(`failed to select game`))
}

authChan := make(chan tracker.AuthStatus)
go ch.tracker.Authenticate(username, password, authChan)
for status := range authChan {
if status.Err != nil {
return errorsx.NewFormattedError(http.StatusUnauthorized, status.Err)
}
wailsRuntime.EventsEmit(ch.ctx, "auth-progress", status.Progress)

if status.Progress >= 100 {
close(authChan)
break
}
}
return nil
}

func (ch *CommandHandler) ForcePoll() {
if ch.tracker != nil {
ch.tracker.ForcePoll()
}
func (ch *CommandHandler) GetSupportedLanguages() []string {
return i18n.GetSupportedLanguages()
}

func (ch *CommandHandler) SaveLocale(locale string) error {
Expand All @@ -248,10 +191,6 @@ func (ch *CommandHandler) SaveTheme(theme model.ThemeName) error {
return ch.nosqlDb.SaveTheme(theme)
}

func (ch *CommandHandler) GetTrackingStateUnused() *model.TrackingState {
return nil
}

func (ch *CommandHandler) GetFormattedErrorModelUnused() *errorsx.FormattedError {
return nil
}
162 changes: 162 additions & 0 deletions cmd/tracking.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package cmd

import (
"context"
"fmt"
"log"
"net/http"
"time"

wailsRuntime "github.com/wailsapp/wails/v2/pkg/runtime"

"github.com/williamsjokvist/cfn-tracker/pkg/browser"
"github.com/williamsjokvist/cfn-tracker/pkg/config"
"github.com/williamsjokvist/cfn-tracker/pkg/errorsx"
"github.com/williamsjokvist/cfn-tracker/pkg/model"
"github.com/williamsjokvist/cfn-tracker/pkg/storage/nosql"
"github.com/williamsjokvist/cfn-tracker/pkg/storage/sql"
"github.com/williamsjokvist/cfn-tracker/pkg/storage/txt"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker/sf6"
_ "github.com/williamsjokvist/cfn-tracker/pkg/tracker/sfv"
"github.com/williamsjokvist/cfn-tracker/pkg/tracker/t8"
)

type TrackingHandler struct {
ctx context.Context
gameTracker tracker.GameTracker

browser *browser.Browser

cancelPolling context.CancelFunc
forcePollChan chan struct{}

sqlDb *sql.Storage
nosqlDb *nosql.Storage
txtDb *txt.Storage

cfg *config.Config
}

var _ CmdHandler = (*TrackingHandler)(nil)

func NewTrackingHandler(browser *browser.Browser, sqlDb *sql.Storage, nosqlDb *nosql.Storage, txtDb *txt.Storage, cfg *config.Config) *TrackingHandler {
return &TrackingHandler{
sqlDb: sqlDb,
nosqlDb: nosqlDb,
txtDb: txtDb,
browser: browser,
cfg: cfg,
}
}

// The CommandHandler needs the wails runtime context in order to emit events
func (ch *TrackingHandler) SetContext(ctx context.Context) {
ch.ctx = ctx
}

func (ch *TrackingHandler) StartTracking(userCode string, restore bool) {
log.Printf(`Starting tracking for %s, restoring = %v`, userCode, restore)
ticker := time.NewTicker(30 * time.Second)
pollCtx, cancel := context.WithCancel(ch.ctx)
ch.cancelPolling = cancel
ch.forcePollChan = make(chan struct{})
var matchChan = make(chan model.Match)

defer func() {
ticker.Stop()
cancel()
close(ch.forcePollChan)
ch.forcePollChan = nil
wailsRuntime.EventsEmit(ch.ctx, "stopped-tracking")
log.Println("stopped polling")
}()

session, err := ch.gameTracker.Init(pollCtx, userCode, restore)
if err != nil {
return
}

go func() {
log.Println("polling")
ch.gameTracker.Poll(pollCtx, cancel, session, matchChan)
for {
select {
case <-ch.forcePollChan:
log.Println("forced poll")
ch.gameTracker.Poll(pollCtx, cancel, session, matchChan)
case <-ticker.C:
log.Println("polling")
ch.gameTracker.Poll(pollCtx, cancel, session, matchChan)
case <-pollCtx.Done():
close(matchChan)
return
}
}
}()

for match := range matchChan {
session.LP = match.LP
session.MR = match.MR
session.Matches = append([]*model.Match{&match}, session.Matches...)
if err := ch.sqlDb.UpdateSession(ch.ctx, session); err != nil {
log.Println("failed to update session", err)
return
}
if err := ch.sqlDb.SaveMatch(ch.ctx, match); err != nil {
log.Println("failed to save match", err)
return
}

wailsRuntime.EventsEmit(ch.ctx, `cfn-data`, match)
if err := ch.txtDb.SaveMatch(match); err != nil {
log.Print("failed to save tracking state:", err)
return
}
}
}

func (ch *TrackingHandler) StopTracking() {
ch.cancelPolling()
}

func (ch *TrackingHandler) SelectGame(game model.GameType) error {
var username, password string

switch game {
case model.GameTypeT8:
ch.gameTracker = t8.NewT8Tracker(ch.sqlDb, ch.txtDb)
case model.GameTypeSF6:
ch.gameTracker = sf6.NewSF6Tracker(ch.browser, ch.sqlDb, ch.txtDb)
username = ch.cfg.CapIDEmail
password = ch.cfg.CapIDPassword
case model.GameTypeSFV:
// gameTracker = sfv.NewSFVTracker(ch.browser)
// username = ch.cfg.SteamUsername
// password = ch.cfg.SteamPassword
fallthrough
default:
return errorsx.NewFormattedError(http.StatusInternalServerError, fmt.Errorf(`failed to select game`))
}

authChan := make(chan tracker.AuthStatus)
go ch.gameTracker.Authenticate(username, password, authChan)
for status := range authChan {
if status.Err != nil {
return errorsx.NewFormattedError(http.StatusUnauthorized, status.Err)
}
wailsRuntime.EventsEmit(ch.ctx, "auth-progress", status.Progress)

if status.Progress >= 100 {
close(authChan)
break
}
}
return nil
}
williamsjokvist marked this conversation as resolved.
Show resolved Hide resolved

func (ch *TrackingHandler) ForcePoll() {
if ch.forcePollChan != nil {
ch.forcePollChan <- struct{}{}
}
williamsjokvist marked this conversation as resolved.
Show resolved Hide resolved
}
williamsjokvist marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion gui/.config/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default defineConfig({
'@@': path.resolve(dirname, "wailsjs"),
"@runtime": path.resolve(dirname, "wailsjs", "runtime", "runtime.js"),
"@model": path.resolve(dirname, "wailsjs", "go", "models.ts"),
"@cmd": path.resolve(dirname, "wailsjs", "go", "cmd", "CommandHandler.js"),
"@cmd": path.resolve(dirname, "wailsjs", "go", "cmd"),
williamsjokvist marked this conversation as resolved.
Show resolved Hide resolved
},
},
css: {
Expand Down
2 changes: 1 addition & 1 deletion gui/package.json.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f5aab2da6a3208cbfc7296b675f1fcbc
42fe0975641ef40aa09a3006fecfb6c5
2 changes: 1 addition & 1 deletion gui/src/main/app-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Icon } from '@iconify/react'

import { cn } from '@/helpers/cn'
import { AuthMachineContext } from '@/state/auth-machine'
import { CheckForUpdate } from '@cmd'
import { CheckForUpdate } from '@cmd/CommandHandler'
import { BrowserOpenURL } from '@runtime'

import { useErrorPopup } from './error-popup'
Expand Down
2 changes: 1 addition & 1 deletion gui/src/main/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import { useTranslation } from 'react-i18next'

import type { model } from '@model'
import { GetGuiConfig } from '@cmd'
import { GetGuiConfig } from '@cmd/CommandHandler'

const initialConfig: model.GuiConfig = {
locale: 'en-GB',
Expand Down
2 changes: 1 addition & 1 deletion gui/src/main/i18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import HttpBackend, { type HttpBackendOptions } from 'i18next-http-backend'
import { I18nextProvider, initReactI18next } from 'react-i18next'

import type { locales } from '@model'
import { GetTranslation } from '@cmd'
import { GetTranslation } from '@cmd/CommandHandler'

export type LocalizationKey = keyof locales.Localization

Expand Down
2 changes: 1 addition & 1 deletion gui/src/main/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TrackingPage } from '@/pages/tracking'
import { AppWrapper } from './app-wrapper'
import { AppErrorBoundary, PageErrorBoundary } from './app-error'

import { GetUsers, GetSessions, GetMatches } from '@cmd'
import { GetUsers, GetSessions, GetMatches } from '@cmd/CommandHandler'

const router = createHashRouter([
{
Expand Down
Loading