/ wallets.go
wallets.go
  1  package main
  2  
  3  import (
  4  	"encoding/json"
  5  	"log"
  6  	"strconv"
  7  
  8  	"github.com/maxence-charriere/go-app/v10/pkg/app"
  9  	shell "github.com/stateless-minds/go-ipfs-api"
 10  )
 11  
 12  // wallets is a component that holds cyber-gubi. A component is a
 13  // customizable, independent, and reusable UI element. It is created by
 14  // embedding app.Compo into a struct.
 15  type wallets struct {
 16  	app.Compo
 17  	sh          *shell.Shell
 18  	loggedIn    bool
 19  	userID      string
 20  	countryCode string
 21  	wallet      Wallet
 22  	wallets     []Wallet
 23  	observer    app.Value
 24  	callback    app.Func
 25  	lastIndex   int
 26  	indexStep   int
 27  }
 28  
 29  func (w *wallets) OnMount(ctx app.Context) {
 30  	sh := shell.NewShell("localhost:5001")
 31  	w.sh = sh
 32  	w.indexStep = 99
 33  
 34  	ctx.GetState("loggedIn", &w.loggedIn)
 35  	if !w.loggedIn {
 36  		ctx.Navigate("/auth")
 37  	}
 38  
 39  	w.callback = app.FuncOf(func(this app.Value, args []app.Value) interface{} {
 40  		entries := args[0]
 41  		for i := 0; i < entries.Length(); i++ {
 42  			entry := entries.Index(i)
 43  			if entry.Get("isIntersecting").Bool() {
 44  				// Element is visible - do something
 45  				w.getWallets(ctx)
 46  			}
 47  		}
 48  		return nil
 49  	})
 50  
 51  	// Select the root element by class name
 52  	rootElement := app.Window().Get("document").Call("querySelector", ".list")
 53  
 54  	options := map[string]interface{}{
 55  		"root":       rootElement,
 56  		"rootMargin": "0px",
 57  		"threshold":  1,
 58  	}
 59  
 60  	observerConstructor := app.Window().Get("IntersectionObserver")
 61  	w.observer = observerConstructor.New(w.callback, options)
 62  
 63  	ctx.GetState("userID", &w.userID)
 64  	ctx.GetState("balance", &w.wallet)
 65  
 66  	ctx.GetState("countryCode", &w.countryCode)
 67  
 68  	w.getWallets(ctx)
 69  }
 70  
 71  func (w *wallets) OnUpdate(ctx app.Context) {
 72  	// Wrap your observation logic in a Go function
 73  	callback := func() {
 74  		target := app.Window().GetElementByID("last-item")
 75  		if !target.IsNull() && !target.IsUndefined() {
 76  			w.observer.Call("disconnect")
 77  			w.observer.Call("observe", target)
 78  		}
 79  	}
 80  
 81  	var goFunc app.Func
 82  
 83  	// Wrap callback as JS function
 84  	goFunc = app.FuncOf(func(this app.Value, args []app.Value) interface{} {
 85  		callback()
 86  		goFunc.Release() // release after call to avoid leaks
 87  		return nil
 88  	})
 89  
 90  	// Call JS setTimeout with delay 10ms
 91  	app.Window().Call("goAppSetTimeout", goFunc, 100)
 92  }
 93  
 94  func (w *wallets) OnDismount(ctx app.Context) {
 95  	w.observer.Call("disconnect")
 96  	w.callback.Release()
 97  }
 98  
 99  func (w *wallets) getWallets(ctx app.Context) {
100  	ctx.Async(func() {
101  		rangeStart := strconv.Itoa(w.lastIndex)
102  		rangeEnd := strconv.Itoa(w.lastIndex + w.indexStep)
103  		// wls, err := w.sh.OrbitDocsQuery(dbWallet, "country_code", w.countryCode+",range="+rangeStart+"-"+rangeEnd)
104  		wls, err := w.sh.OrbitDocsQuery(dbWallet, "country_code", "BG"+",range="+rangeStart+"-"+rangeEnd)
105  		if err != nil {
106  			log.Fatal(err)
107  		}
108  
109  		wallets := []Wallet{}
110  
111  		if len(wls) != 0 {
112  			err = json.Unmarshal(wls, &wallets) // Unmarshal the byte slice directly
113  			if err != nil {
114  				log.Fatal(err)
115  			}
116  		} else {
117  			w.OnDismount(ctx)
118  		}
119  
120  		excludingOwnWallet := []Wallet{}
121  
122  		for _, wallet := range wallets {
123  			if wallet.ID != string(w.userID) {
124  				excludingOwnWallet = append(excludingOwnWallet, wallet)
125  			}
126  		}
127  
128  		ctx.Dispatch(func(ctx app.Context) {
129  			w.wallets = append(w.wallets, excludingOwnWallet...)
130  			w.lastIndex = w.lastIndex + 1 + w.indexStep
131  			w.OnUpdate(ctx)
132  		})
133  	})
134  }
135  
136  func (w *wallets) getBalance(userID string) (balance Wallet, err error) {
137  	b, err := w.sh.OrbitDocsQuery(dbWallet, "_id", userID)
138  	if err != nil {
139  		return Wallet{}, err
140  	}
141  
142  	if len(b) == 0 {
143  		return Wallet{}, err
144  	}
145  
146  	wallets := []Wallet{}
147  
148  	err = json.Unmarshal(b, &wallets) // Unmarshal the byte slice directly
149  	if err != nil {
150  		return Wallet{}, err
151  	}
152  
153  	return wallets[0], nil
154  }
155  
156  func (w *wallets) updateBalance(userID string, balance, income int, date string) error {
157  	wallet := Wallet{
158  		ID:           userID,
159  		Balance:      balance,
160  		Income:       income,
161  		LastReceived: date,
162  	}
163  
164  	walletJSON, err := json.Marshal(wallet)
165  	if err != nil {
166  		return err
167  	}
168  
169  	err = w.sh.OrbitDocsPut(dbWallet, walletJSON)
170  	if err != nil {
171  		return err
172  	}
173  
174  	return nil
175  }
176  
177  func (w *wallets) storeTransaction(transaction Transaction) error {
178  	transactionJSON, err := json.Marshal(transaction)
179  	if err != nil {
180  		return err
181  	}
182  
183  	err = w.sh.OrbitDocsPut(dbTransaction, transactionJSON)
184  	if err != nil {
185  		return err
186  	}
187  
188  	return nil
189  }
190  
191  // The Render method is where the component appearance is defined. Here, a
192  // payment form is displayed.
193  func (w *wallets) Render() app.UI {
194  	return app.Div().Class("container").Body(
195  		app.Div().Class("mobile").Body(
196  			app.Div().Class("header").Body(
197  				newNav(),
198  				app.Div().Class("header-summary").Body(
199  					app.Span().Class("logo").Text("cyber-gubi"),
200  					app.Div().Class("summary-text").Body(
201  						app.Span().Text("Treasury"),
202  					),
203  					app.Div().Class("summary-balance").Body(
204  						app.Span().Text(strconv.Itoa(w.wallet.Balance/100)+" GUBI"),
205  					),
206  				),
207  			),
208  			app.Div().ID("content").Body(
209  				app.Div().Class("card").Body(
210  					app.Div().Class("upper-row single").Body(
211  						app.Div().Class("card-item").Body(
212  							app.Span().Class("span-header-sub").Text("Wallets"),
213  						),
214  					),
215  				),
216  				app.Div().Class("list").Body(
217  					app.If(len(w.wallets) == 0, func() app.UI {
218  						return app.Div().Class("list-item").Body(
219  							app.Span().Class("empty").Text("No wallets found"),
220  						).Style("pointer-events", "none")
221  					}),
222  					app.Range(w.wallets).Slice(func(i int) app.UI {
223  						return app.If(i == len(w.wallets)-1 && len(w.wallets)%5 == 0, func() app.UI {
224  							return app.Div().ID("last-item").Class("list-item").Body(
225  								app.Div().Class("w-details").Body(
226  									app.Div().Class("w-title").Body(
227  										app.Span().Text(w.wallets[i].ID),
228  									),
229  									app.Div().Class("menu-btn menu-list").Body(
230  										app.A().Href("transactions").Class("submit submit-list").Type("submit").Text("Transactions"),
231  									),
232  									app.Div().Class("menu-btn menu-list").Body(
233  										app.A().Href("tax/"+w.wallets[i].ID).Class("submit submit-list").Type("submit").Text("Collect Tax"),
234  									),
235  								),
236  							)
237  						}).Else(func() app.UI {
238  							return app.Div().Class("list-item").Body(
239  								app.Div().Class("w-details").Body(
240  									app.Div().Class("w-title").Body(
241  										app.Span().Text(w.wallets[i].ID),
242  									),
243  									app.Div().Class("menu-btn menu-list").Body(
244  										app.A().Href("transactions/"+w.wallets[i].ID).Class("submit submit-list").Type("submit").Text("Transactions"),
245  									),
246  									app.Div().Class("menu-btn menu-list").Body(
247  										app.A().Href("tax/"+w.wallets[i].ID).Class("submit submit-list").Type("submit").Text("Collect Tax"),
248  									),
249  								),
250  							)
251  						})
252  					}),
253  				),
254  			),
255  		),
256  	)
257  }