Remove conflict origs
This commit is contained in:
@@ -1,531 +0,0 @@
|
||||
/*
|
||||
Copyright © Brian Buller <brian@bullercodeworks.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.bullercodeworks.com/brian/expds/data"
|
||||
"git.bullercodeworks.com/brian/expds/data/models"
|
||||
wd "git.bullercodeworks.com/brian/expds/widgets"
|
||||
w "git.bullercodeworks.com/brian/tcell-widgets"
|
||||
"github.com/bluesky-social/indigo/atproto/syntax"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
"github.com/spf13/viper"
|
||||
"golang.design/x/clipboard"
|
||||
)
|
||||
|
||||
type ScreenHome struct {
|
||||
a *App
|
||||
r *data.Repo
|
||||
w, h int
|
||||
style tcell.Style
|
||||
|
||||
alert *w.Alert
|
||||
|
||||
menuLayout *w.TopMenuLayout
|
||||
|
||||
openPdsEntry *w.Field
|
||||
|
||||
layout *w.LinearLayout
|
||||
columns *w.LinearLayout
|
||||
|
||||
activePds *models.Pds
|
||||
pdsListing *wd.TreeBrowser
|
||||
jsonContent *wd.JsonContent
|
||||
status *wd.StatusBar
|
||||
stPathBlock *wd.StatusBlock
|
||||
|
||||
cli *w.Cli
|
||||
|
||||
doOpen bool
|
||||
isLoading bool
|
||||
loading *w.Spinner
|
||||
|
||||
cursor int
|
||||
}
|
||||
|
||||
func (s *ScreenHome) Init(a *App) {
|
||||
s.a, s.r = a, a.repo
|
||||
s.r.SetLogFunc(s.Log)
|
||||
|
||||
s.style = a.style
|
||||
|
||||
s.alert = w.NewAlert("expds.alert", s.style)
|
||||
|
||||
s.openPdsEntry = w.NewField("home.openpds.field", s.style)
|
||||
s.openPdsEntry.SetLabel("ID")
|
||||
s.openPdsEntry.SetActive(true)
|
||||
s.openPdsEntry.SetPos(w.Coord{X: 3, Y: 1})
|
||||
s.openPdsEntry.SetSize(w.Coord{X: 20, Y: 1})
|
||||
s.doOpen = true
|
||||
|
||||
s.isLoading = false
|
||||
s.loading = w.NewSpinner("home.loading", s.style)
|
||||
s.loading.SetVisible(true)
|
||||
s.loading.SetPos(w.Coord{X: 2, Y: 1})
|
||||
|
||||
s.menuLayout = w.NewTopMenuLayout("home.toplayout", s.style)
|
||||
s.initMenu()
|
||||
|
||||
s.cli = w.NewCli("home.cli", s.style)
|
||||
s.initCli()
|
||||
s.cli.SetVisible(false)
|
||||
|
||||
s.layout = w.NewLinearLayout("home.layout", s.style)
|
||||
|
||||
s.columns = w.NewLinearLayout("home.layout.columns", s.style)
|
||||
s.columns.SetOrientation(w.LinLayH)
|
||||
|
||||
//s.pdsListing = w.NewSimpleListWithHelp("pdslisting", s.style)
|
||||
s.pdsListing = wd.NewTreeBrowser("pdslisting", s.style)
|
||||
s.pdsListing.SetBorder([]rune{'─', '┬', '│', '┴', '─', '└', '│', '┌', '├', '─', '┤', '┬', '│', '┴', '┼'})
|
||||
|
||||
s.pdsListing.SetTitle(strings.Repeat(" ", 30))
|
||||
s.pdsListing.SetOnSelect(s.selectPdsListingEntry)
|
||||
s.pdsListing.SetOnChange(s.changePdsList)
|
||||
s.pdsListing.SetVimMode(viper.GetBool(data.KeyVimMode))
|
||||
s.pdsListing.SetLogger(s.Log)
|
||||
|
||||
s.jsonContent = wd.NewJsonContent("jsoncontent", s.style)
|
||||
km := s.jsonContent.GetKeyMap()
|
||||
km.Add(
|
||||
w.NewKey(w.BuildEK(tcell.KeyEnter), func(ev *tcell.EventKey) bool {
|
||||
// Init returns an error if the package is not ready for use.
|
||||
err := clipboard.Init()
|
||||
if err != nil {
|
||||
s.Log("Error initializing clipboard: %s", err.Error())
|
||||
return true
|
||||
}
|
||||
clipboard.Write(clipboard.FmtText, []byte(s.jsonContent.GetSelectedValue()))
|
||||
return true
|
||||
}),
|
||||
w.NewKey(w.BuildEKr('O'), func(ev *tcell.EventKey) bool {
|
||||
url := fmt.Sprintf("https://%s/xrpc/com.atproto.sync.getBlob?did=did:plc:pqwuemo2ic5tqmpwrajb2phi&cid=%s", s.activePds.AtId.String(), s.jsonContent.GetSelectedValue())
|
||||
open.Run(url)
|
||||
return true
|
||||
}),
|
||||
)
|
||||
s.jsonContent.SetKeyMap(km)
|
||||
s.jsonContent.SetBorder(
|
||||
[]rune{'─', '┐', '│', '┘', '─', '─', ' ', '─', '├', '─', '┤', '┬', '│', '┴', '┼'},
|
||||
)
|
||||
|
||||
statusStyle := s.style.Background(tcell.ColorDarkSlateGray)
|
||||
s.status = wd.NewStatusBar("home.statusbar", statusStyle)
|
||||
s.status.SetPos(w.Coord{X: 0, Y: s.a.GetH() - 1})
|
||||
s.status.SetLogger(s.Log)
|
||||
s.stPathBlock = wd.NewStatusBlock("home.statusbar.block", statusStyle.Foreground(tcell.ColorDarkSlateGray).Background(tcell.ColorOrange))
|
||||
s.stPathBlock.SetType(wd.SBTypePath)
|
||||
s.stPathBlock.SetParts([]string{"No PDS Loaded"})
|
||||
s.status.Add(s.stPathBlock)
|
||||
|
||||
s.columns.AddAll(s.pdsListing, s.jsonContent)
|
||||
|
||||
s.layout.AddAll(s.columns)
|
||||
s.layout.SetLogger(s.Log)
|
||||
s.layout.SetWeight(s.columns, 4)
|
||||
s.menuLayout.SetWidget(s.layout)
|
||||
s.layout.SetLogger(s.Log)
|
||||
s.columns.SetLogger(s.Log)
|
||||
}
|
||||
|
||||
func (s *ScreenHome) GetName() string { return "home" }
|
||||
func (s *ScreenHome) HandleResize(ev *tcell.EventResize) {
|
||||
s.w, s.h = ev.Size()
|
||||
s.menuLayout.HandleResize(w.Coord{X: s.w, Y: s.h - 1}.ResizeEvent())
|
||||
s.alert.HandleResize(ev)
|
||||
|
||||
s.status.SetPos(w.Coord{X: 0, Y: s.h - 1})
|
||||
s.status.HandleResize(w.Coord{X: s.w, Y: 1}.ResizeEvent())
|
||||
}
|
||||
|
||||
func (s *ScreenHome) HandleKey(ev *tcell.EventKey) bool {
|
||||
<<<<<<< HEAD
|
||||
if s.showAlert {
|
||||
handled := s.alert.HandleKey(ev)
|
||||
s.alert.SetMessage(fmt.Sprintf("Alert Handled? %v -> %s", handled, ev.Name()))
|
||||
return handled
|
||||
}
|
||||
=======
|
||||
>>>>>>> 367d62ff009186e5aa584fd069dd57aaf7d46a8a
|
||||
if ev.Key() == tcell.KeyF12 {
|
||||
s.toggleCli()
|
||||
return true
|
||||
}
|
||||
m := s.menuLayout.Menu()
|
||||
if ev.Key() == tcell.KeyEscape {
|
||||
return s.menuLayout.HandleKey(ev)
|
||||
} else if m.Active() {
|
||||
return s.menuLayout.HandleKey(ev)
|
||||
}
|
||||
|
||||
if s.doOpen {
|
||||
if ev.Key() == tcell.KeyCtrlU {
|
||||
s.openPdsEntry.SetValue("")
|
||||
return true
|
||||
}
|
||||
|
||||
if ev.Key() == tcell.KeyEnter {
|
||||
return s.cliGetPds("getpds", s.openPdsEntry.Value())
|
||||
}
|
||||
return s.openPdsEntry.HandleKey(ev)
|
||||
}
|
||||
|
||||
return s.menuLayout.HandleKey(ev)
|
||||
}
|
||||
func (s *ScreenHome) HandleTime(ev *tcell.EventTime) {
|
||||
s.menuLayout.HandleTime(ev)
|
||||
s.loading.HandleTime(ev)
|
||||
s.status.HandleTime(ev)
|
||||
}
|
||||
func (s *ScreenHome) Draw() {
|
||||
if s.doOpen {
|
||||
s.menuLayout.SetStyle(s.style.Foreground(tcell.ColorGray))
|
||||
}
|
||||
s.menuLayout.SetStyle(s.style)
|
||||
m := s.menuLayout.Menu()
|
||||
s.a.DrawWidget(s.menuLayout)
|
||||
if s.doOpen && !m.Active() {
|
||||
s.a.DrawWidget(s.openPdsEntry)
|
||||
}
|
||||
|
||||
// These are outside of the menuLayout
|
||||
if s.isLoading {
|
||||
s.a.DrawWidget(s.loading)
|
||||
}
|
||||
s.a.DrawWidget(s.status)
|
||||
}
|
||||
func (s *ScreenHome) Exit() error { return nil }
|
||||
func (s *ScreenHome) Log(t string, a ...any) {
|
||||
s.cli.Log(t, a...)
|
||||
s.showCli()
|
||||
}
|
||||
|
||||
func (s *ScreenHome) initMenu() {
|
||||
s.menuLayout.SetActive(true)
|
||||
wrk := "[ ]"
|
||||
if viper.GetBool(data.KeyVimMode) {
|
||||
wrk = "[X]"
|
||||
}
|
||||
vimText := fmt.Sprintf("%s Vim Mode", wrk)
|
||||
wrk = "[ ]"
|
||||
if viper.GetBool(data.KeyRecNmInfer) {
|
||||
wrk = "[X]"
|
||||
}
|
||||
inferRecNm := fmt.Sprintf("%s Infer Record Names", wrk)
|
||||
s.menuLayout.AddMenuItems(
|
||||
s.menuLayout.CreateMenuItem("file", "File", nil, 'f',
|
||||
s.menuLayout.CreateMenuItem("file.openpds", "Open PDS", func() bool {
|
||||
s.menuLayout.ToggleMenu()
|
||||
s.doOpen = true
|
||||
s.pdsListing.SetTitle(strings.Repeat(" ", 30))
|
||||
return true
|
||||
}, 'o'),
|
||||
s.menuLayout.CreateMenuItem("file.reloadpds", "Reload PDS", func() bool {
|
||||
s.menuLayout.ToggleMenu()
|
||||
if s.activePds == nil {
|
||||
return false
|
||||
}
|
||||
return s.cliGetPds("getpds", s.activePds.AtId.String())
|
||||
}, 'r'),
|
||||
s.menuLayout.CreateMenuItem("file.exit", "Exit", func() bool {
|
||||
s.a.Exit()
|
||||
return true
|
||||
}, 'x'),
|
||||
),
|
||||
s.menuLayout.CreateMenuItem("settings", "Settings", nil, 's',
|
||||
s.menuLayout.CreateMenuItem("settings.vimmode", vimText, func() bool {
|
||||
s.menuLayout.ToggleMenu()
|
||||
viper.Set(data.KeyVimMode, !viper.GetBool(data.KeyVimMode))
|
||||
viper.WriteConfig()
|
||||
s.update()
|
||||
return true
|
||||
}, 'v'),
|
||||
s.menuLayout.CreateMenuItem("settings.inferrecnm", inferRecNm, func() bool {
|
||||
s.menuLayout.ToggleMenu()
|
||||
viper.Set(data.KeyRecNmInfer, !viper.GetBool(data.KeyRecNmInfer))
|
||||
viper.WriteConfig()
|
||||
s.update()
|
||||
return true
|
||||
}, 'r'),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *ScreenHome) update() {
|
||||
if s.doOpen {
|
||||
s.menuLayout.SetWidget(s.openPdsEntry)
|
||||
} else {
|
||||
s.menuLayout.SetWidget(s.layout)
|
||||
}
|
||||
s.pdsListing.SetVimMode(viper.GetBool(data.KeyVimMode))
|
||||
s.jsonContent.SetVimMode(viper.GetBool(data.KeyVimMode))
|
||||
vimMI := s.menuLayout.FindItem("settings.vimmode")
|
||||
wrk := "[ ]"
|
||||
if viper.GetBool(data.KeyVimMode) {
|
||||
wrk = "[X]"
|
||||
}
|
||||
vimMI.SetLabel(fmt.Sprintf("%s Vim Mode", wrk))
|
||||
recNmInf := s.menuLayout.FindItem("settings.inferrecnm")
|
||||
wrk = "[ ]"
|
||||
if viper.GetBool(data.KeyRecNmInfer) {
|
||||
wrk = "[X]"
|
||||
}
|
||||
recNmInf.SetLabel(fmt.Sprintf("%s Infer Record Names", wrk))
|
||||
|
||||
miReload := s.menuLayout.FindItem("file.reloadpds")
|
||||
if s.activePds == nil {
|
||||
miReload.SetDisabled(true)
|
||||
miReload.SetStyle(s.style.Foreground(tcell.ColorGray))
|
||||
} else {
|
||||
miReload.SetDisabled(false)
|
||||
miReload.SetStyle(s.style.Foreground(tcell.ColorLime))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ScreenHome) initCli() {
|
||||
s.cli.SetVisible(true)
|
||||
s.cli.AddCommand(w.NewCliCommand("getpds", s.cliGetPds))
|
||||
s.cli.AddCommand(w.NewCliCommand("authpds", s.cliAuthPds))
|
||||
s.cli.AddCommand(w.NewCliCommand("backuppds", s.cliBackupPds))
|
||||
s.cli.AddCommand(w.NewCliCommand("sendstatus", s.cliSendStatus))
|
||||
}
|
||||
|
||||
func (s *ScreenHome) toggleCli() {
|
||||
if s.layout.Contains(s.cli) {
|
||||
s.layout.Delete(s.cli)
|
||||
s.layout.ActivateWidget(s.columns)
|
||||
} else {
|
||||
s.layout.Add(s.cli)
|
||||
s.cli.SetVisible(true)
|
||||
s.layout.ActivateWidget(s.cli)
|
||||
}
|
||||
}
|
||||
func (s *ScreenHome) hideCli() {
|
||||
s.cli.SetVisible(false)
|
||||
if s.layout.Contains(s.cli) {
|
||||
s.layout.Delete(s.cli)
|
||||
}
|
||||
}
|
||||
func (s *ScreenHome) showCli() {
|
||||
s.cli.SetVisible(true)
|
||||
if !s.layout.Contains(s.cli) {
|
||||
s.layout.Add(s.cli)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ScreenHome) cliGetPds(args ...string) bool {
|
||||
s.isLoading = true
|
||||
if len(args) < 1 {
|
||||
s.Log("No id given.")
|
||||
s.Log("Usage: 'getpds <atproto id>'")
|
||||
s.isLoading = false
|
||||
return true
|
||||
}
|
||||
go func() {
|
||||
defer func() { s.isLoading = false }()
|
||||
var pds *models.Pds
|
||||
var err error
|
||||
if s.activePds != nil && string(s.activePds.AtId) == args[1] {
|
||||
pds, err = s.r.ReloadPds(args[1])
|
||||
} else {
|
||||
pds, err = s.r.GetPDS(args[1])
|
||||
}
|
||||
if err != nil {
|
||||
s.Log(err.Error())
|
||||
return
|
||||
} else if pds == nil {
|
||||
s.Log("PDS (%s) Not Found.", args[1])
|
||||
return
|
||||
}
|
||||
s.doOpen = false
|
||||
slog.Default().Debug(fmt.Sprintf("Retrieved: %s (%s) at %s", pds.AtId, pds.Did, time.Now().Format(time.RFC3339)))
|
||||
s.activePds = pds
|
||||
s.updatePdsListing()
|
||||
n, err := s.pdsListing.GetActiveNode()
|
||||
if err == nil && n != nil {
|
||||
s.changePdsList(n)
|
||||
}
|
||||
|
||||
s.layout.ActivateWidget(s.columns)
|
||||
s.columns.ActivateWidget(s.pdsListing)
|
||||
}()
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *ScreenHome) cliAuthPds(args ...string) bool {
|
||||
if s.activePds == nil {
|
||||
s.Log("No active PDS.")
|
||||
return true
|
||||
}
|
||||
s.isLoading = true
|
||||
go func() {
|
||||
defer func() { s.isLoading = false }()
|
||||
atid := s.activePds.AtId.String()
|
||||
callbackRes := make(chan url.Values, 1)
|
||||
listenPort, err := s.r.Auth.ListenForCallback(callbackRes)
|
||||
if err != nil {
|
||||
s.Log("Error Instantiating HTTP Server for Callback: %w", err)
|
||||
return
|
||||
}
|
||||
s.Log("Listening on %d", listenPort)
|
||||
var authUrl string
|
||||
authUrl, err = s.r.Auth.StartAuthFlow(listenPort, atid, callbackRes)
|
||||
if err != nil {
|
||||
s.Log("Error starting auth flow: %w", err)
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
s.alert.SetTitle("Authentication Started")
|
||||
s.alert.SetMessage(fmt.Sprintf("OAuth Process Started.\nIf a browser window didn't open, you can open this URL manually:\n%s", authUrl))
|
||||
s.alert.SetVisible(true)
|
||||
s.alert.SetActive(true)
|
||||
s.showAlert = true
|
||||
=======
|
||||
s.Log("Authentication Started")
|
||||
s.Log(fmt.Sprintf("OAuth Process Started.\nIf a browser window didn't open, you can open this URL manually:\n%s", strings.ReplaceAll(authUrl, "%", "%%")))
|
||||
>>>>>>> 367d62ff009186e5aa584fd069dd57aaf7d46a8a
|
||||
}()
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *ScreenHome) cliBackupPds(args ...string) bool {
|
||||
if s.activePds == nil {
|
||||
s.Log("No active PDS.")
|
||||
return true
|
||||
}
|
||||
s.isLoading = true
|
||||
go func() {
|
||||
defer func() { s.isLoading = false }()
|
||||
nm, sz, err := s.activePds.Backup()
|
||||
if err != nil {
|
||||
s.Log("Error: %w", err)
|
||||
return
|
||||
}
|
||||
s.Log("Backup Created: %s (%d bytes)", nm, sz)
|
||||
}()
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *ScreenHome) cliSendStatus(args ...string) bool {
|
||||
if s.activePds == nil {
|
||||
s.Log("No active PDS.")
|
||||
return true
|
||||
}
|
||||
s.isLoading = true
|
||||
go func() {
|
||||
defer func() { s.isLoading = false }()
|
||||
if !s.r.Auth.HasAuth(s.activePds.Did) {
|
||||
s.Log("Not authorized. Run `authpds`")
|
||||
return
|
||||
}
|
||||
err := s.r.SendToPDS(s.activePds.Did)
|
||||
if err != nil {
|
||||
s.Log("Error sending status: %w", err)
|
||||
}
|
||||
}()
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *ScreenHome) updatePdsListing() {
|
||||
s.pdsListing.SetTitle(fmt.Sprintf("─ %s (%s)", s.activePds.AtId.String(), s.activePds.Did.String()))
|
||||
s.pdsListing.Clear()
|
||||
nsidList := s.activePds.NSIDStringList()
|
||||
var tree []*wd.TreeNode
|
||||
for i, v := range nsidList {
|
||||
t := wd.NewTreeNode(v, v)
|
||||
nsid := s.activePds.NSIDs[i]
|
||||
rIds := s.activePds.GetRecordIdsFor(nsid)
|
||||
for j := range rIds {
|
||||
label := rIds[j]
|
||||
if viper.GetBool(data.KeyRecNmInfer) {
|
||||
if rec, ok := s.activePds.Records[rIds[j]]; ok {
|
||||
for k := range rec {
|
||||
if k == "name" || k == "title" || k == "label" || k == "displayName" {
|
||||
if f, ok := rec[k].(string); ok {
|
||||
label = fmt.Sprintf("%s (%s)", f, rIds[j])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c := wd.NewTreeNode(label, rIds[j])
|
||||
t.AddChild(c)
|
||||
}
|
||||
tree = append(tree, t)
|
||||
}
|
||||
s.pdsListing.SetTree(tree)
|
||||
s.layout.ActivateWidget(s.columns)
|
||||
s.columns.ActivateWidget(s.pdsListing)
|
||||
s.pdsListing.SetActive(true)
|
||||
s.pdsListing.SetFocusable(true)
|
||||
s.hideCli()
|
||||
}
|
||||
|
||||
func (s *ScreenHome) selectPdsListingEntry(tn *wd.TreeNode) bool {
|
||||
if !s.updateJsonView(tn) {
|
||||
return false
|
||||
}
|
||||
if tn.HasChildren() {
|
||||
tn.ToggleExpand()
|
||||
s.pdsListing.UpdateList()
|
||||
} else {
|
||||
s.columns.ActivateWidget(s.jsonContent)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *ScreenHome) changePdsList(tn *wd.TreeNode) bool {
|
||||
upd := s.updateJsonView(tn)
|
||||
upd = s.updateStatusPathBlock(tn)
|
||||
return upd
|
||||
}
|
||||
func (s *ScreenHome) updateStatusPathBlock(tn *wd.TreeNode) bool {
|
||||
s.stPathBlock.SetParts(tn.GetLabelPath())
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *ScreenHome) updateJsonView(tn *wd.TreeNode) bool {
|
||||
// TODO: Update JSON View
|
||||
if tn.Depth() == 0 {
|
||||
nsid, err := syntax.ParseNSID(tn.Value())
|
||||
if err != nil {
|
||||
s.Log("error parsing NSID from %s: %w", tn.Value(), err)
|
||||
return false
|
||||
}
|
||||
recordIds := s.activePds.GetRecordIdsFor(nsid)
|
||||
s.jsonContent.SetValue(recordIds)
|
||||
return true
|
||||
} else {
|
||||
s.jsonContent.SetValue(s.activePds.Records[tn.Value()])
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user