Skip to content

Commit

Permalink
Merge pull request #35 from jefft0/feat/add-ListUsersByPrefix
Browse files Browse the repository at this point in the history
feat: add list users by prefix ListJsonUsersByPrefix
  • Loading branch information
jefft0 authored Mar 7, 2024
2 parents f1b457b + 1c86452 commit 5150c73
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 64 deletions.
104 changes: 53 additions & 51 deletions realm/public.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ func PostMessage(body string) PostID {
panic("please register")
}

userPosts := getUserPosts(caller)
if userPosts == nil {
url := "/r/berty/social:" + name
userPosts = newUserPosts(url, caller)
gUserPostsByAddress.Set(caller.String(), userPosts)
}

userPosts := getOrCreateUserPosts(caller, name)
thread := userPosts.AddThread(body)
return thread.id
}
Expand Down Expand Up @@ -77,12 +71,7 @@ func RepostThread(userPostsAddr std.Address, threadid PostID, comment string) Po
if name == "" {
panic("please register")
}
dstUserPosts := getUserPosts(caller)
if dstUserPosts == nil {
url := "/r/berty/social:" + name
dstUserPosts = newUserPosts(url, caller)
gUserPostsByAddress.Set(caller.String(), dstUserPosts)
}
dstUserPosts := getOrCreateUserPosts(caller, name)

if userPostsAddr == caller {
panic("Cannot repost a user's own message")
Expand All @@ -99,36 +88,6 @@ func RepostThread(userPostsAddr std.Address, threadid PostID, comment string) Po
return repost.id
}

// Get all users with their address. The response is an avl.Tree where the key is the
// (sorted) user name and the value is the address.
func GetUsers() *avl.Tree {
allUsers := avl.Tree{}
gUserPostsByAddress.Iterate("", "", func(addr string, userPostsI interface{}) bool {
if user := users.GetUserByAddress(std.Address(addr)); user != nil {
// allUsers will be sorted by the name key.
allUsers.Set(user.Name, addr)
}
return false
})

return &allUsers
}

// Get all users with their address (using GetUsers), sorted by name. The response is a JSON string.
func GetJsonUsers() string {
allUsers := GetUsers()
usersJson := ""
allUsers.Iterate("", "", func(name string, addr interface{}) bool {
if usersJson != "" {
usersJson += ",\n "
}
usersJson += ufmt.Sprintf("{\"name\": \"%s\", \"address\": \"%s\"}", name, addr.(string))
return false
})

return ufmt.Sprintf("{\"n_users\": %d, \"users\": [\n %s]}", allUsers.Size(), usersJson)
}

// Get posts in a thread for a user. A thread is the sequence of posts without replies.
// While each post has an an arbitrary id, it also has an index within the thread starting from 0.
// Limit the response to posts from startIndex up to (not including) endIndex within the thread.
Expand Down Expand Up @@ -224,14 +183,8 @@ func Follow(followedAddr std.Address) {
panic("you can't follow yourself")
}

userPosts := getUserPosts(caller)
if userPosts == nil {
// A user can follow someone before doing any posts, so create the UserPosts.
url := "/r/berty/social:" + name
userPosts = newUserPosts(url, caller)
gUserPostsByAddress.Set(caller.String(), userPosts)
}

// A user can follow someone before doing any posts, so create the UserPosts if needed.
userPosts := getOrCreateUserPosts(caller, name)
userPosts.Follow(followedAddr)
}

Expand Down Expand Up @@ -265,3 +218,52 @@ func GetJsonUserByAddress(addr std.Address) string {
"{\"address\": \"%s\", \"name\": \"%s\", \"profile\": %s, \"number\": %d, \"invites\": %d, \"inviter\": \"%s\"}",
user.Address.String(), user.Name, strconv.Quote(user.Profile), user.Number, user.Invites, user.Inviter.String())
}

// TODO: This is a temporary copy. Remove this when they merge https://github.com/gnolang/gno/pull/1708.
func listKeysByPrefix(tree avl.Tree, prefix string, maxResults int) []string {
end := ""
n := len(prefix)
// To make the end of the search, increment the final character ASCII by one.
for n > 0 {
if ascii := int(prefix[n-1]); ascii < 0xff {
end = prefix[0:n-1] + string(ascii+1)
break
}

// The last character is 0xff. Try the previous character.
n--
}

result := []string{}
tree.Iterate(prefix, end, func(key string, value interface{}) bool {
result = append(result, key)
if len(result) >= maxResults {
return true
}
return false
})
return result
}

// Get a list of user names starting from the given prefix. Limit the
// number of results to maxResults.
func ListUsersByPrefix(prefix string, maxResults int) []string {
return listKeysByPrefix(gUserAddressByName, prefix, maxResults)
}

// Get a list of user names starting from the given prefix. Limit the
// number of results to maxResults.
// The response is a JSON string.
func ListJsonUsersByPrefix(prefix string, maxResults int) string {
names := ListUsersByPrefix(prefix, maxResults)

json := "["
for i, name := range names {
if i > 0 {
json += ", "
}
json += strconv.Quote(name)
}
json += "]"
return json
}
15 changes: 3 additions & 12 deletions realm/render.gno
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package social

import (
"sort"
"std"
"strconv"
"strings"

Expand All @@ -13,18 +11,11 @@ func Render(path string) string {
if path == "" {
str := "Welcome to GnoSocial!\n\n"

// List the users who have posted, sorted by name.
names := []string{}
gUserPostsByAddress.Iterate("", "", func(key string, value interface{}) bool {
if user := users.GetUserByAddress(std.Address(key)); user != nil {
names = append(names, user.Name)
}
// List the users who have posted. gUserAddressByName is already sorted by name.
gUserAddressByName.Iterate("", "", func(name string, value interface{}) bool {
str += " * [@" + name + "](/r/berty/social:" + name + ")" + "\n"
return false
})
sort.Strings(names)
for _, name := range names {
str += " * [@" + name + "](/r/berty/social:" + name + ")" + "\n"
}

return str
}
Expand Down
1 change: 1 addition & 0 deletions realm/social.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import (

var (
gUserPostsByAddress avl.Tree // user's std.Address -> *UserPosts
gUserAddressByName avl.Tree // user's username -> std.Address
postsCtr uint64 // increments Post.id globally
)
26 changes: 25 additions & 1 deletion realm/util.gno
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
//----------------------------------------
// private utility methods

// Get the userPosts for the user.
// Get the UserPosts for the user.
func getUserPosts(userAddr std.Address) *UserPosts {
userPosts, exists := gUserPostsByAddress.Get(userAddr.String())
if !exists {
Expand All @@ -21,6 +21,30 @@ func getUserPosts(userAddr std.Address) *UserPosts {
return userPosts.(*UserPosts)
}

// Get the UserPosts for the userAddr. If not found, add a new UserPosts to
// gUserPostsByAddress and update gUserAddressByName with the username.
// (The caller usually has already called usernameOf to get the username, but if
// it is "" then this will get it.)
func getOrCreateUserPosts(userAddr std.Address, username string) *UserPosts {
userPosts := getUserPosts(userAddr)
if userPosts != nil {
return userPosts
}

if username == "" {
username := usernameOf(userAddr)
if username == "" {
panic("no username for address " + userAddr.String())
}
}

userPosts = newUserPosts("/r/berty/social:"+username, userAddr)
gUserPostsByAddress.Set(userAddr.String(), userPosts)
gUserAddressByName.Set(username, userAddr)

return userPosts
}

func padZero(u64 uint64, length int) string {
str := strconv.Itoa(int(u64))
if len(str) >= length {
Expand Down

0 comments on commit 5150c73

Please sign in to comment.