diff --git a/examples/gno.land/r/demo/teritori/projects_manager/filter.gno b/examples/gno.land/r/demo/teritori/projects_manager/filter.gno new file mode 100644 index 00000000000..0d727f3353d --- /dev/null +++ b/examples/gno.land/r/demo/teritori/projects_manager/filter.gno @@ -0,0 +1,81 @@ +package projects_manager + +import ( + "std" + + "gno.land/p/demo/dao_maker/jsonutil" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +type Filter interface { + FromJSON(ast *json.Node) +} + +func FilterFromJSON(ast *json.Node) Filter { + if ast.IsNull() { + return nil + } + var filter Filter + key, member := jsonutil.MustUnion(ast) + switch key { + case "byCandidatesForFunder": + filter = &FilterByCandidatesForFunder{} + case "byFunder": + filter = &FilterByFunder{} + case "byContractor": + filter = &FilterByContractor{} + case "byContractorAndFunder": + filter = &FilterByContractorAndFunder{} + default: + panic(ufmt.Sprintf("invalid filter kind `%s`", key)) + } + filter.FromJSON(member) + return filter +} + +type FilterByCandidatesForFunder struct { + Funder std.Address +} + +func (f *FilterByCandidatesForFunder) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Funder = jsonutil.MustAddress(obj["funder"]) +} + +var _ Filter = &FilterByCandidatesForFunder{} + +type FilterByFunder struct { + Funder std.Address +} + +func (f *FilterByFunder) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Funder = jsonutil.MustAddress(obj["funder"]) +} + +var _ Filter = &FilterByFunder{} + +type FilterByContractor struct { + Contractor std.Address +} + +func (f *FilterByContractor) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Contractor = jsonutil.MustAddress(obj["contractor"]) +} + +var _ Filter = &FilterByContractor{} + +type FilterByContractorAndFunder struct { + Contractor std.Address + Funder std.Address +} + +func (f *FilterByContractorAndFunder) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Contractor = jsonutil.MustAddress(obj["contractor"]) + f.Funder = jsonutil.MustAddress(obj["funder"]) +} + +var _ Filter = &FilterByContractorAndFunder{} diff --git a/examples/gno.land/r/demo/teritori/projects_manager/projects_manager.gno b/examples/gno.land/r/demo/teritori/projects_manager/projects_manager.gno index 756def95f21..ff3da646d6f 100644 --- a/examples/gno.land/r/demo/teritori/projects_manager/projects_manager.gno +++ b/examples/gno.land/r/demo/teritori/projects_manager/projects_manager.gno @@ -275,6 +275,7 @@ var ( contractsByFunder = avl.NewTree() // std.Address(funder) => contractID => *Contract contractsByContractor = avl.NewTree() // std.Address(contractor) => contractID => *Contract contractsByFunderAndContractor = avl.NewTree() // std.Address(funder) + std.Address(contractor) => contractID => *Contract + contractsWithCandidates = avl.NewTree() // std.Address(funder) => contractID => *Contract ) func setIndices(contract *Contract) { @@ -578,6 +579,18 @@ func AcceptContractor(contractId uint64, contractor std.Address) { } contracts[contractId].contractor = contractor + + funderKey := contract.funder.String() + byIDTreeIface, ok := contractsWithCandidates.Get(funderKey) + if !ok { + byIDTreeIface = avl.NewTree() + contractsWithCandidates.Set(funderKey, byIDTreeIface) + } + byIDTree := byIDTreeIface.(*avl.Tree) + byIDTree.Remove(seqid.ID(contract.id).String()) + if byIDTree.Size() == 0 { + contractsWithCandidates.Remove(funderKey) + } } func SubmitContractorCandidate(contractId uint64) { @@ -608,6 +621,14 @@ func SubmitContractorCandidate(contractId uint64) { } contracts[contractId].contractorCandidates = append(candidates, caller) + + funderKey := contract.funder.String() + byIDTree, ok := contractsWithCandidates.Get(funderKey) + if !ok { + byIDTree = avl.NewTree() + contractsWithCandidates.Set(funderKey, byIDTree) + } + byIDTree.(*avl.Tree).Set(seqid.ID(contract.id).String(), contract) } // Complete any milestone in review status and pay the needed amount @@ -822,7 +843,7 @@ func GetContractorCandidatesJSON(contractId uint64) string { return string(ret) } -func GetContracts(offset, limit int, filterByFunder string, filterByContractor string) []*Contract { +func GetContracts(offset, limit int, filter Filter) []*Contract { if offset < 0 { offset = 0 } @@ -831,14 +852,7 @@ func GetContracts(offset, limit int, filterByFunder string, filterByContractor s return nil } - var tree interface{} - if filterByFunder != "" && filterByContractor != "" { - tree, _ = contractsByFunderAndContractor.Get(filterByFunder + filterByContractor) - } else if filterByFunder != "" { - tree, _ = contractsByFunder.Get(filterByFunder) - } else if filterByContractor != "" { - tree, _ = contractsByContractor.Get(filterByContractor) - } else { + if filter == nil { end := offset + limit if end > len(contracts) { end = len(contracts) @@ -846,6 +860,20 @@ func GetContracts(offset, limit int, filterByFunder string, filterByContractor s return contracts[offset:end] } + var tree interface{} + switch f := filter.(type) { + case *FilterByCandidatesForFunder: + tree, _ = contractsWithCandidates.Get(f.Funder.String()) + case *FilterByContractorAndFunder: + tree, _ = contractsByFunderAndContractor.Get(f.Funder.String() + f.Contractor.String()) + case *FilterByContractor: + tree, _ = contractsByContractor.Get(f.Contractor.String()) + case *FilterByFunder: + tree, _ = contractsByFunder.Get(f.Funder.String()) + default: + panic("unknown filter") + } + if tree == nil { return nil } @@ -873,10 +901,15 @@ func RenderContractJSON(contractId uint64) string { return string(ret) } -func RenderContractsJSON(offset, limit int, filterByFunder string, filterByContractor string) string { - contracts := GetContracts(offset, limit, filterByFunder, filterByContractor) - contractsJSON := make([]*json.Node, len(contracts)) - for i, c := range contracts { +func RenderContractsJSON(offset, limit int, filterJSON string) string { + filter := FilterFromJSON(json.Must(json.Unmarshal([]byte(filterJSON)))) + contractsRes := GetContracts(offset, limit, filter) + return renderContractsJSON(contractsRes) +} + +func renderContractsJSON(contractsRes []*Contract) string { + contractsJSON := make([]*json.Node, len(contractsRes)) + for i, c := range contractsRes { contractsJSON[i] = c.ToJSON() }