forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add social follow system (#15)
- Loading branch information
1 parent
a2e42d9
commit 1c0c4ee
Showing
6 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
gnokey maketx addpkg \ | ||
-deposit="1ugnot" \ | ||
-gas-fee="1ugnot" \ | ||
-gas-wanted="5000000" \ | ||
-broadcast="true" \ | ||
-remote="51.15.236.215:26657" \ | ||
-chainid="teritori-1" \ | ||
-pkgdir="." \ | ||
-pkgpath="gno.land/r/demo/social_follow_2" \ | ||
mykey2 | ||
|
||
gnokey maketx call \ | ||
-gas-fee="1ugnot" \ | ||
-gas-wanted="5000000" \ | ||
-broadcast="true" \ | ||
-remote="51.15.236.215:26657" \ | ||
-chainid="teritori-1" \ | ||
-pkgpath="gno.land/r/demo/social_follow_2" \ | ||
-func="Follow" \ | ||
-args="g1c5y8jpe585uezcvlmgdjmk5jt2glfw88wxa3xq" \ | ||
mykey3 | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
Followers("g1c5y8jpe585uezcvlmgdjmk5jt2glfw88wxa3xq",0,1)' -remote="51.15.236.215:26657" | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
FollowersCount("g1c5y8jpe585uezcvlmgdjmk5jt2glfw88wxa3xq")' -remote="51.15.236.215:26657" | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
Followed("g1j3ylca07vlhklzftrznw7jyquzqf2wtxvjdm4r",0,1)' -remote="51.15.236.215:26657" | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
FollowedCount("g1j3ylca07vlhklzftrznw7jyquzqf2wtxvjdm4r")' -remote="51.15.236.215:26657" | ||
|
||
gnokey maketx call \ | ||
-gas-fee="1ugnot" \ | ||
-gas-wanted="5000000" \ | ||
-broadcast="true" \ | ||
-remote="51.15.236.215:26657" \ | ||
-chainid="teritori-1" \ | ||
-pkgpath="gno.land/r/demo/social_follow_2" \ | ||
-func="Unfollow" \ | ||
-args="g1c5y8jpe585uezcvlmgdjmk5jt2glfw88wxa3xq" \ | ||
mykey3 | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
Followers("g1c5y8jpe585uezcvlmgdjmk5jt2glfw88wxa3xq",0,1)' -remote="51.15.236.215:26657" | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
FollowersCount("g1c5y8jpe585uezcvlmgdjmk5jt2glfw88wxa3xq")' -remote="51.15.236.215:26657" | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
Followed("g1j3ylca07vlhklzftrznw7jyquzqf2wtxvjdm4r",0,1)' -remote="51.15.236.215:26657" | ||
|
||
gnokey query "vm/qeval" -data='gno.land/r/demo/social_follow_2 | ||
FollowedCount("g1j3ylca07vlhklzftrznw7jyquzqf2wtxvjdm4r")' -remote="51.15.236.215:26657" |
83 changes: 83 additions & 0 deletions
83
examples/gno.land/r/demo/teritori/social_follow/follow.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package social_follow | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/p/demo/avl" | ||
) | ||
|
||
var addr2User avl.Tree // std.Address -> *User | ||
|
||
func getOrCreateUser(addr std.Address) *User { | ||
userI, ok := addr2User.Get(addr.String()) | ||
if ok { | ||
return userI.(*User) | ||
} | ||
user := &User{ | ||
address: addr, | ||
} | ||
addr2User.Set(addr.String(), user) | ||
return user | ||
} | ||
|
||
func Follow(addr std.Address) { | ||
caller := std.PrevRealm().Addr() | ||
callerUser := getOrCreateUser(caller) | ||
user := getOrCreateUser(addr) | ||
callerUser.Follow(user) | ||
} | ||
|
||
func Unfollow(addr std.Address) { | ||
caller := std.PrevRealm().Addr() | ||
callerUser := getOrCreateUser(caller) | ||
user := getOrCreateUser(addr) | ||
callerUser.Unfollow(user) | ||
} | ||
|
||
func Followers(addr std.Address, page, pageSize int) []std.Address { | ||
userI, ok := addr2User.Get(addr.String()) | ||
if !ok { | ||
return nil | ||
} | ||
user := userI.(*User) | ||
return user.Followers(page, pageSize) | ||
} | ||
|
||
func FollowedCount(addr std.Address) uint { | ||
userI, ok := addr2User.Get(addr.String()) | ||
if !ok { | ||
return 0 | ||
} | ||
user := userI.(*User) | ||
return uint(user.followeds.Size()) | ||
} | ||
|
||
func Followed(addr std.Address, page, pageSize int) []std.Address { | ||
userI, ok := addr2User.Get(addr.String()) | ||
if !ok { | ||
return nil | ||
} | ||
|
||
user := userI.(*User) | ||
|
||
return user.Followed(page, pageSize) | ||
} | ||
|
||
func FollowersCount(addr std.Address) uint { | ||
userI, ok := addr2User.Get(addr.String()) | ||
if !ok { | ||
return 0 | ||
} | ||
user := userI.(*User) | ||
return uint(user.followers.Size()) | ||
} | ||
|
||
func IsFollower(follower std.Address, followed std.Address) bool { | ||
userI, ok := addr2User.Get(followed.String()) | ||
if !ok { | ||
return false | ||
} | ||
user := userI.(*User) | ||
_, ok = user.followers.Get(follower.String()) | ||
return ok | ||
} |
106 changes: 106 additions & 0 deletions
106
examples/gno.land/r/demo/teritori/social_follow/follow_test.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package social_follow | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/p/demo/avl" | ||
"gno.land/p/demo/testutils" | ||
) | ||
|
||
func TestFollow_Follow(t *testing.T) { | ||
addr2User = avl.Tree{} | ||
main := testutils.TestAddress("main") | ||
std.TestSetOrigCaller(main) | ||
|
||
user := testutils.TestAddress("user") | ||
|
||
Follow(user) | ||
|
||
if c := FollowersCount(user); c != 1 { | ||
t.Fatalf("FollowersCount expected to have 1 has %d", c) | ||
} | ||
|
||
if followers := Followers(user, 0, 1); followers[0].String() != string(main) { | ||
t.Fatalf("Followers expected to have %s has %s", string(main), followers[0].String()) | ||
} | ||
|
||
if followed := Followed(main, 0, 1); followed[0].String() != string(user) { | ||
t.Fatalf("Followed expected to have %s has %s", string(user), followed[0].String()) | ||
} | ||
} | ||
|
||
func TestFollow_UnFollow(t *testing.T) { | ||
addr2User = avl.Tree{} | ||
|
||
user1 := testutils.TestAddress("user1") | ||
user2 := testutils.TestAddress("user2") | ||
std.TestSetOrigCaller(user1) | ||
|
||
Follow(user2) | ||
if IsFollower(user1, user2) != true { | ||
t.Fatalf("expected to be a follower") | ||
} | ||
|
||
Unfollow(user2) | ||
if IsFollower(user1, user2) != false { | ||
t.Fatalf("expected to not be a follower") | ||
} | ||
} | ||
|
||
func TestFollow_FollowedPagination(t *testing.T) { | ||
addr2User = avl.Tree{} | ||
|
||
main := testutils.TestAddress("main") | ||
std.TestSetOrigCaller(main) | ||
for i := 0; i < 10; i++ { | ||
user := testutils.TestAddress("user" + string(i)) | ||
Follow(user) | ||
} | ||
|
||
for i := 0; i < 10; i++ { | ||
if followed := Followed(main, i, 1); len(followed) != 1 { | ||
t.Fatalf("at page %d expected to have 1 has %d", i, len(followed)) | ||
} | ||
} | ||
|
||
if followed := Followed(main, 10, 1); len(followed) != 0 { | ||
t.Fatalf("at page 10 expected to have 0 has %d", len(followed)) | ||
} | ||
|
||
if followed := Followed(main, 0, 10); len(followed) != 10 { | ||
t.Fatalf("at page 0 expected to have 10 has %d", len(followed)) | ||
} | ||
|
||
if followed := Followed(main, 1, 8); len(followed) != 2 { | ||
t.Fatalf("at page 2 expected to have 2 has %d", len(followed)) | ||
} | ||
} | ||
|
||
func TestFollow_FollowersPagination(t *testing.T) { | ||
main := testutils.TestAddress("main") | ||
|
||
for i := 0; i < 10; i++ { | ||
user := testutils.TestAddress("user" + string(i)) | ||
std.TestSetOrigCaller(user) | ||
Follow(main) | ||
} | ||
|
||
for i := 0; i < 10; i++ { | ||
if followers := Followers(main, i, 1); len(followers) != 1 { | ||
t.Fatalf("at page %d expected to have 1 has %d", i, len(followers)) | ||
} | ||
} | ||
|
||
if followers := Followers(main, 10, 1); len(followers) != 0 { | ||
t.Fatalf("at page 10 expected to have 0 has %d", len(followers)) | ||
} | ||
|
||
if followers := Followers(main, 0, 10); len(followers) != 10 { | ||
t.Fatalf("at page 0 expected to have 10 has %d", len(followers)) | ||
} | ||
|
||
if followers := Followers(main, 1, 8); len(followers) != 2 { | ||
t.Fatalf("at page 2 expected to have 2 has %d", len(followers)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module gno.land/r/demo/teritori/social_follow | ||
|
||
require ( | ||
gno.land/p/demo/avl v0.0.0-latest | ||
gno.land/p/demo/testutils v0.0.0-latest | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Social Follow System | ||
|
||
## Overview | ||
The Social Network System is designed to facilitate user interactions within a social network. It allows users to follow and unfollow each other, as well as retrieve information about followers and followed users. | ||
|
||
## Modules | ||
|
||
### 1. User Management | ||
- This module manages user information within the social follow. | ||
- Each user is represented by a `User` struct containing their address and lists of followers and followed users. | ||
|
||
### 2. Following Functionality | ||
- Users can follow and unfollow other users. | ||
- When a user follows another user, they are added to the followed user's list of followers, and the followed user is added to the user's list of followed users. | ||
- When a user unfollows another user, they are removed from the followed user's list of followers, and the followed user is removed from the user's list of followed users. | ||
|
||
## Data Structures | ||
|
||
### User | ||
```go | ||
type User struct { | ||
address std.Address | ||
followers *avl.Tree // std.Address -> *User | ||
followeds *avl.Tree // std.Address -> *User | ||
} | ||
``` | ||
|
||
## Functions | ||
|
||
### User Management | ||
- `Followers(page, pageSize int) []std.Address `: Returns a list of addresses of users following the user. | ||
- `Followed(page, pageSize int) []std.Address `: Returns a list of addresses of users whom the user is following. | ||
- `FollowedCount(addr std.Address) uint`: Returns the number of users being followed by the user with the given address. | ||
- `FollowersCount(addr std.Address) uint`: Returns the number of users following the user with the given address. | ||
|
||
### Following Functionality | ||
- `Follow(user *User)`: Adds the given user to the list of users being followed by the user. | ||
- `Unfollow(user *User)`: Removes the given user from the list of users being followed by the user. | ||
|
||
## Realm Configuration Process | ||
- Users are managed within the social network system using the provided functionality. | ||
- The system utilizes an AVL tree data structure to efficiently store and retrieve user information. | ||
|
||
## Usage | ||
- Users interact with the system by following or unfollowing other users. | ||
- User information is maintained and updated dynamically as users follow and unfollow each other. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package social_follow | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/p/demo/avl" | ||
) | ||
|
||
type User struct { | ||
address std.Address | ||
followers avl.Tree // std.Address -> *User | ||
followeds avl.Tree // std.Address -> *User | ||
} | ||
|
||
func (u *User) Address() std.Address { | ||
return u.address | ||
} | ||
|
||
func (u *User) Followers(page, pageSize int) []std.Address { | ||
followers := make([]std.Address, 0, u.followers.Size()) | ||
u.followers.IterateByOffset(page*pageSize, pageSize, func(key string, value interface{}) bool { | ||
follower := value.(*User) | ||
followers = append(followers, follower.address) | ||
return false | ||
}) | ||
return followers | ||
} | ||
|
||
func (u *User) Followed(page, pageSize int) []std.Address { | ||
followeds := make([]std.Address, 0, u.followeds.Size()) | ||
u.followeds.IterateByOffset(page*pageSize, pageSize, func(key string, value interface{}) bool { | ||
followed := value.(*User) | ||
followeds = append(followeds, followed.address) | ||
return false | ||
}) | ||
return followeds | ||
} | ||
|
||
func (u *User) Follow(user *User) { | ||
if u.address == user.address { | ||
panic("can't follow self") | ||
} | ||
if _, ok := u.followeds.Get(user.address.String()); ok { | ||
panic("already follow") | ||
} | ||
u.followeds.Set(user.address.String(), user) | ||
user.followers.Set(u.address.String(), u) | ||
} | ||
|
||
func (u *User) Unfollow(user *User) { | ||
if _, ok := u.followeds.Get(user.address.String()); !ok { | ||
panic("not follow") | ||
} | ||
if _, ok := u.followeds.Remove(user.address.String()); !ok { | ||
panic("can't remove on followed") | ||
} | ||
|
||
if _, ok := user.followers.Remove(u.address.String()); !ok { | ||
panic("can't remove on follower") | ||
} | ||
} |