package app
import (
"context"
"encoding/json"
"net/url"
"strings"
"time"
"github.com/google/uuid"
"github.com/pancsta/go-app/pkg/errors"
)
type Context struct {
context .Context
page func () Page
appUpdatable bool
resolveURL func (string ) string
navigate func (*url .URL , bool )
localStorage BrowserStorage
sessionStorage BrowserStorage
dispatch func (func ())
defere func (func ())
async func (func ())
addComponentUpdate func (Composer , int )
removeComponentUpdate func (Composer )
handleAction func (string , UI , bool , ActionHandler )
postAction func (Context , Action )
observeState func (Context , string , any ) Observer
getState func (Context , string , any )
setState func (Context , string , any ) State
delState func (Context , string )
sourceElement UI
notifyComponentEvent func (Context , UI , any )
}
func (ctx Context ) Src () UI {
return ctx .sourceElement
}
func (ctx Context ) JSSrc () Value {
return ctx .sourceElement .JSValue ()
}
func (ctx Context ) AppUpdateAvailable () bool {
return ctx .appUpdatable
}
func (ctx Context ) IsAppInstallable () bool {
if Window ().Get ("goappIsAppInstallable" ).Truthy () {
return Window ().Call ("goappIsAppInstallable" ).Bool ()
}
return false
}
func (ctx Context ) IsAppleBrowser () bool {
if Window ().Get ("goappIsAppleBrowser" ).Truthy () {
return Window ().Call ("goappIsAppleBrowser" ).Bool ()
}
return false
}
func (ctx Context ) ShowAppInstallPrompt () {
if ctx .IsAppInstallable () {
Window ().Call ("goappShowInstallPrompt" )
}
}
func (ctx Context ) DeviceID () string {
var id string
if err := ctx .localStorage .Get ("/go-app/deviceID" , &id ); err != nil {
panic (errors .New ("retrieving device id failed" ).Wrap (err ))
}
if id != "" {
return id
}
id = uuid .NewString ()
if err := ctx .localStorage .Set ("/go-app/deviceID" , id ); err != nil {
panic (errors .New ("creating device id failed" ).Wrap (err ))
}
return id
}
func (ctx Context ) Page () Page {
return ctx .page ()
}
func (ctx Context ) Reload () {
if IsServer {
return
}
Window ().Get ("location" ).Call ("reload" )
}
func (ctx Context ) Navigate (rawURL string ) {
u , err := url .Parse (rawURL )
if err != nil {
Log (errors .New ("navigating to URL failed" ).
WithTag ("url" , rawURL ).
Wrap (err ))
return
}
ctx .NavigateTo (u )
}
func (ctx Context ) NavigateTo (u *url .URL ) {
ctx .navigate (u , true )
}
func (ctx Context ) ResolveStaticResource (v string ) string {
return ctx .resolveURL (v )
}
func (ctx Context ) ScrollTo (id string ) {
ctx .Defer (func (ctx Context ) {
Window ().ScrollToID (id )
})
}
func (ctx Context ) LocalStorage () BrowserStorage {
return ctx .localStorage
}
func (ctx Context ) SessionStorage () BrowserStorage {
return ctx .sessionStorage
}
func (ctx Context ) Encrypt (v any ) ([]byte , error ) {
b , err := json .Marshal (v )
if err != nil {
return nil , errors .New ("encoding value failed" ).Wrap (err )
}
b , err = encrypt (ctx .cryptoKey (), b )
if err != nil {
return nil , errors .New ("encrypting value failed" ).Wrap (err )
}
return b , nil
}
func (ctx Context ) Decrypt (crypted []byte , v any ) error {
b , err := decrypt (ctx .cryptoKey (), crypted )
if err != nil {
return errors .New ("decrypting value failed" ).Wrap (err )
}
if err := json .Unmarshal (b , v ); err != nil {
return errors .New ("decoding value failed" ).Wrap (err )
}
return nil
}
func (ctx Context ) cryptoKey () string {
return strings .ReplaceAll (ctx .DeviceID (), "-" , "" )
}
func (ctx Context ) Notifications () NotificationService {
return NotificationService {}
}
func (ctx Context ) Dispatch (v func (Context )) {
ctx .dispatch (func () {
if !ctx .sourceElement .Mounted () {
return
}
for c , ok := component (ctx .sourceElement ); ok ; c , ok = component (c .parent ()) {
ctx .addComponentUpdate (c , 1 )
}
if v != nil {
v (ctx )
}
})
}
func (ctx Context ) Defer (v func (Context )) {
ctx .defere (func () {
if !ctx .sourceElement .Mounted () {
return
}
if v != nil {
v (ctx )
}
})
}
func (ctx Context ) Async (v func ()) {
ctx .async (v )
}
func (ctx Context ) After (d time .Duration , f func (Context )) {
ctx .async (func () {
time .Sleep (d )
ctx .Dispatch (f )
})
}
func (ctx Context ) PreventUpdate () {
for c , ok := component (ctx .sourceElement ); ok ; c , ok = component (c .parent ()) {
ctx .addComponentUpdate (c , -1 )
}
}
func (ctx Context ) Update () {
ctx .Dispatch (nil )
}
func (ctx Context ) Handle (action string , h ActionHandler ) {
ctx .handleAction (action , ctx .sourceElement , false , h )
}
func (ctx Context ) NewAction (action string , tags ...Tagger ) {
ctx .NewActionWithValue (action , nil , tags ...)
}
func (ctx Context ) NewActionWithValue (action string , v any , tags ...Tagger ) {
var tagMap Tags
for _ , tag := range tags {
if tagMap == nil {
tagMap = make (Tags )
}
for k , v := range tag .Tags () {
tagMap [k ] = v
}
}
ctx .postAction (ctx , Action {
Name : action ,
Value : v ,
Tags : tagMap ,
})
}
func (ctx Context ) ObserveState (state string , recv any ) Observer {
return ctx .observeState (ctx , state , recv )
}
func (ctx Context ) GetState (state string , recv any ) {
ctx .getState (ctx , state , recv )
}
func (ctx Context ) SetState (state string , v any ) State {
return ctx .setState (ctx , state , v )
}
func (ctx Context ) DelState (state string ) {
ctx .delState (ctx , state )
}
func (ctx Context ) ResizeContent () {
ctx .Defer (func (ctx Context ) {
ctx .Dispatch (func (ctx Context ) {
ctx .notifyComponentEvent (ctx , ctx .Src (), resize {})
})
})
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .