Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: r/profile dapp #1983

Merged
merged 46 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e73da9b
profile realm
kazai777 Apr 25, 2024
f98cafa
panic message start with lowercase letter
kazai777 May 8, 2024
9c6b768
add mapping comment
kazai777 May 9, 2024
5f29419
modify comparison
kazai777 May 9, 2024
bf3b0dd
replace return and bool by panic
kazai777 May 9, 2024
3796c3c
modify name of function and replace return by panic
kazai777 May 9, 2024
4770d4b
replace return by panic
kazai777 May 9, 2024
f13d0d6
add emit
kazai777 May 12, 2024
a0b7975
Merge branch 'gnolang:master' into addprofile
kazai777 May 13, 2024
38b5439
remove unnecessary condition
kazai777 May 13, 2024
d1c108c
displaymodificationhistory simplified
kazai777 May 14, 2024
69173f8
Merge branch 'master' into addprofile
kazai777 May 14, 2024
a0a1027
add mux for routing
kazai777 May 14, 2024
21f6ccb
remove old username in avl tree after update username
kazai777 May 14, 2024
e78e0bc
make tidy
kazai777 May 14, 2024
9b87d58
add link in profile page and hisotry page
kazai777 May 14, 2024
3db2185
Merge branch 'master' into addprofile
kazai777 May 15, 2024
208b913
Merge branch 'master' into addprofile
kazai777 Jun 3, 2024
61bdfc2
add an avl.tree to add multiple websites
kazai777 Jun 18, 2024
8793a51
add std.Emit with the changes made in UpdateProfile
kazai777 Jun 18, 2024
6fac788
renaming getter functions with the Get prefix
kazai777 Jun 18, 2024
19d5b23
Refactoring v0.1
kazai777 Jun 21, 2024
bec9bcb
remove no used contextKey
kazai777 Jun 21, 2024
8a66188
mod
kazai777 Jun 21, 2024
f38eb7f
refactor, adding minimal getters and setters
kazai777 Jun 24, 2024
51d5577
whitout test, whitout render refactoring
kazai777 Jun 24, 2024
57fd874
add unit test
Jun 25, 2024
2c962ca
clean code and refactoring handlers
kazai777 Jun 25, 2024
7101443
Merge branch 'master' into addprofile
kazai777 Jun 25, 2024
5110a6c
Update examples/gno.land/r/demo/profile/handlers.gno
kazai777 Jun 28, 2024
a857573
Update examples/gno.land/r/demo/profile/handlers.gno
kazai777 Jun 28, 2024
8f83cfd
Update examples/gno.land/r/demo/profile/profile.gno
kazai777 Jun 28, 2024
b61da85
Update examples/gno.land/r/demo/profile/profile.gno
kazai777 Jun 28, 2024
214dd6f
Update examples/gno.land/r/demo/profile/handlers.gno
kazai777 Jun 28, 2024
6755ac9
fix: remove Getters call on homeHandler, remove profile display homeH…
kazai777 Jun 29, 2024
52a0e4b
Merge branch 'master' into addprofile
kazai777 Jun 29, 2024
b30325b
Update examples/gno.land/r/demo/profile/handlers.gno
kazai777 Jun 29, 2024
8631c48
Update examples/gno.land/r/demo/profile/handlers.gno
kazai777 Jun 29, 2024
f7ff18c
refactor: i used constants for they keys
kazai777 Jun 29, 2024
a3795fb
Merge branch 'master' into addprofile
moul Jul 4, 2024
fadbc59
fix: move constant on the profile file, rename file handlers by rende…
kazai777 Jul 8, 2024
5789de8
refactor: testfile with new uassert function, global declaration for …
kazai777 Jul 8, 2024
cea8dee
make tidy
kazai777 Jul 8, 2024
0869813
Merge branch 'master' into addprofile
kazai777 Jul 8, 2024
f51af5f
Merge branch 'master' into addprofile
leohhhn Jul 13, 2024
2c27587
Merge branch 'master' into addprofile
moul Jul 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/gno.land/r/demo/profile/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/r/demo/profile

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/mux v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
)
367 changes: 367 additions & 0 deletions examples/gno.land/r/demo/profile/profile.gno
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
package profile

import (
"bytes"
"gno.land/p/demo/avl"
"gno.land/p/demo/ufmt"
"gno.land/p/demo/mux"
"std"
"time"
)

type UserProfile struct {
Username string
Address std.Address
AvatarURL string
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
Age string
Gender string
Website string
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
Country string
City string
ModificationHistory map[string][]string
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
}

var profiles *avl.Tree // address -> UserProfile
var usernameIndex *avl.Tree // username -> address
var router *mux.Router

func init() {
profiles = avl.NewTree()
usernameIndex = avl.NewTree()
router = mux.NewRouter()

router.HandleFunc("", homeHandler)
router.HandleFunc("info/{username}", profileHandler)
router.HandleFunc("history/{username}", historyHandler)
}

func CreateProfile(username, avatarurl, age, gender, website, country, city string) {
caller := std.GetOrigCaller()

// Check if a profile already exists for this address
if _, exists := profiles.Get(caller.String()); exists {
panic("a profile already exists for this address")
}

// Check if the username is already taken
if username != "" {
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
_, exists := usernameIndex.Get(username)
if exists {
panic("Username is already taken")
}
}

profile := UserProfile{
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
Username: username,
Address: caller,
AvatarURL: avatarurl,
Age: age,
Gender: gender,
Website: website,
Country: country,
City: city,
}

profiles.Set(caller.String(), profile)


usernameIndex.Set(username, caller.String())
std.Emit("UserProfileCreated", "username", username, "address", caller.String())
}

func UpdateProfile(username, avatarurl, age, gender, website, country, city string) {
caller := std.GetOrigCaller()
value, exists := profiles.Get(caller.String())
if !exists {
panic("Profile not found")
}

// Retrieve the existing profile
existingProfile := value.(UserProfile)

// update the username and manage index if changed
if username != "" && username != existingProfile.Username {
_, exists := usernameIndex.Get(username)
if exists {
panic("Username is already taken")
}

// remove the old username from the index
if existingProfile.Username != "" {
usernameIndex.Remove(existingProfile.Username)
}

// set the new username in the index
usernameIndex.Set(username, caller.String())
}

// Initialize the ModificationHistory map if it's nil
if existingProfile.ModificationHistory == nil {
existingProfile.ModificationHistory = make(map[string][]string)
}

// Function to update the profile field if changed
updateField := func(field *string, newValue, fieldName string, profile *UserProfile) {
if newValue != "" && *field != newValue {
changeDescription := time.Now().Format(time.RFC3339) + ": Changed from " + *field + " to " + newValue
if profile.ModificationHistory == nil {
profile.ModificationHistory = make(map[string][]string)
}
profile.ModificationHistory[fieldName] = append(profile.ModificationHistory[fieldName], changeDescription)
*field = newValue
}
}

// Update fields and log changes
updateField(&existingProfile.Username, username, "Username", &existingProfile)
updateField(&existingProfile.AvatarURL, avatarurl, "AvatarURL", &existingProfile)
updateField(&existingProfile.Age, age, "Age", &existingProfile)
updateField(&existingProfile.Gender, gender, "Gender", &existingProfile)
updateField(&existingProfile.Website, website, "Website", &existingProfile)
updateField(&existingProfile.Country, country, "Country", &existingProfile)
updateField(&existingProfile.City, city, "City", &existingProfile)

// Update the profile in the tree
profiles.Set(caller.String(), existingProfile)

// Update the username index if the username is changed
/*if username != existingProfile.Username {
usernameIndex.Set(username, caller.String())
}*/
}

// Display complete Profile by Address
func DisplayProfileByAddress(address std.Address) UserProfile {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile)
}

panic("profile not found")
}



// Get complete profile by username
func DisplayProfileByUsername(username string) UserProfile {
if addr, exists := usernameIndex.Get(username); exists {
return DisplayProfileByAddress(std.Address(addr.(string)))
}

panic("username not found")
}

// Display username
func DisplayUsernameByAddress(address std.Address) string {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).Username
}

panic("address not found")
}

// Display address from username
func DisplayAddressByUsername(username string) string {
if addr, exists := usernameIndex.Get(username); exists {
return addr.(string)
}

panic("username not found")
}

// Display avatarurl from address or username
func DisplayAvatarURL(address std.Address, username string) string {
if address != "" {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).AvatarURL
}
} else if username != "" {
if addr, exists := usernameIndex.Get(username); exists {
if value, exists := profiles.Get(addr.(string)); exists {
return value.(UserProfile).AvatarURL
}
}
}

panic("address or username not found")
}

// Display age from address or username
func DisplayAge(address std.Address, username string) string {
if address != "" {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).Age
}
} else if username != "" {
if addr, exists := usernameIndex.Get(username); exists {
if value, exists := profiles.Get(addr.(string)); exists {
return value.(UserProfile).Age
}
}
}

panic("address or username not found")
}

// Display gender from address or username
func DisplayGender(address std.Address, username string) string {
if address != "" {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).Gender
}
} else if username != "" {
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
if addr, exists := usernameIndex.Get(username); exists {
if value, exists := profiles.Get(addr.(string)); exists {
return value.(UserProfile).Gender
}
}
}

panic("address or username not found")
}

// Display website from address or username
func DisplayWebsite(address std.Address, username string) string {
if address != "" {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).Website
}
} else if username != "" {
if addr, exists := usernameIndex.Get(username); exists {
if value, exists := profiles.Get(addr.(string)); exists {
return value.(UserProfile).Website
}
}
}

panic("address or username not found")
}

// Display country from address or username
func DisplayCountry(address std.Address, username string) string {
if address != "" {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).Country
}
} else if username != "" {
if addr, exists := usernameIndex.Get(username); exists {
if value, exists := profiles.Get(addr.(string)); exists {
return value.(UserProfile).Country
}
}
}

panic("address or username not found")
}

// Display city from address or username
func DisplayCity(address std.Address, username string) string {
kazai777 marked this conversation as resolved.
Show resolved Hide resolved
if address != "" {
if value, exists := profiles.Get(address.String()); exists {
return value.(UserProfile).City
}
} else if username != "" {
if addr, exists := usernameIndex.Get(username); exists {
if value, exists := profiles.Get(addr.(string)); exists {
return value.(UserProfile).City
}
}
}

panic("address or username not found")
}

// Display modification history specific to a field from address or username
func DisplayModificationHistory(address std.Address, username, field string) string {
var value interface{}
var exists bool

if address != "" {
value, exists = profiles.Get(address.String())
} else if username != "" {
if addr, ok := usernameIndex.Get(username); ok {
value, exists = profiles.Get(addr.(string))
}
}

if !exists {
panic("address or username not found")
}

profile, ok := value.(UserProfile)
if !ok || profile.ModificationHistory == nil {
panic("no modification history available")
}

changes, found := profile.ModificationHistory[field]
if !found {
panic("no modifications recorded for the field")
}

var b bytes.Buffer
for _, change := range changes {
b.WriteString(ufmt.Sprintf("%s\n", change))
}
return b.String()
}

func homeHandler(res *mux.ResponseWriter, req *mux.Request) {
var b bytes.Buffer
b.WriteString("<html><body>")
res.Write("<h1>Profiles List</h1><br>")
usernameIndex.Iterate("", "", func(key string, value interface{}) bool {
username := key
res.Write("<p>Username: " + username + " [<a href='/r/demo/profile:info/" + username + "'>" + "View Profile</a>]</p>")
return true
})
b.WriteString("</body></html>")
res.Write(b.String())
}

func profileHandler(res *mux.ResponseWriter, req *mux.Request) {
username := req.GetVar("username")
profile := DisplayProfileByUsername(username)

var b bytes.Buffer

b.WriteString("<html><body>")
// Reteturn home link
b.WriteString("<p>[<a href='/r/demo/profile'>Home</a>]</p>")
b.WriteString("<h1>Profile of " + profile.Username + "</h1>")
b.WriteString("<p>Username: " + profile.Username + "</p>")
b.WriteString("<p>Address: " + profile.Address.String() + "</p>")
b.WriteString("<p>Avatar URL: <img src='" + profile.AvatarURL + "' /></p>")
b.WriteString("<p>[<a href='/r/demo/profile:history/" + username + "'>View Modification History</a>]</p>")
b.WriteString("</body></html>")

res.Write(b.String())
}

func historyHandler(res *mux.ResponseWriter, req *mux.Request) {
username := req.GetVar("username")
profile := DisplayProfileByUsername(username)

var b bytes.Buffer

b.WriteString("<html><body>")

b.WriteString("<p>[<a href='/r/demo/profile'>Home</a>] | [<a href='/r/demo/profile:info/" + username + "'>Profile " + username + "</a>]</p>")
b.WriteString("<h1>Modification History for " + profile.Username + "</h1>")

if profile.ModificationHistory != nil && len(profile.ModificationHistory) > 0 {
for field, changes := range profile.ModificationHistory {
b.WriteString("<h2>" + field + "</h2>")
for _, change := range changes {
b.WriteString("<p>" + change + "</p>")
}
}
} else {
b.WriteString("<p>No modifications recorded.</p>")
}

b.WriteString("</body></html>")
res.Write(b.String())
}

func Render(path string) string {
return router.Render(path)
}
Loading