profile.gno
3.20 Kb · 135 lines
1package arena
2
3import (
4 "chain"
5 "chain/runtime"
6 "strings"
7)
8
9// Register opens a profile for the calling account.
10// handle is 3–16 chars of a-z, 0-9 and underscore. referrer may be empty
11// or the bech32 address of an existing member. A member can register once.
12func Register(cur realm, handle, name, bio, referrer string) {
13 who := caller(cur)
14 mustNoPay()
15 if profileByAddr(who) != nil {
16 panic("already registered")
17 }
18 handle = strings.ToLower(strings.TrimSpace(handle))
19 if !validHandle(handle) {
20 panic("bad handle")
21 }
22 if state.byHandle.Has(handle) {
23 panic("handle taken")
24 }
25 if name == "" {
26 name = handle
27 }
28 name = cleanLine(name, maxNameRunes, maxNameBytes, "name", false)
29 bio = cleanLine(bio, maxBioRunes, maxBioBytes, "bio", true)
30
31 var ref address
32 referrer = strings.TrimSpace(referrer)
33 if referrer != "" {
34 if len(referrer) > 60 {
35 panic("bad referrer")
36 }
37 ref = address(referrer)
38 if !ref.IsValid() {
39 panic("bad referrer")
40 }
41 if ref == who {
42 panic("self referral")
43 }
44 if profileByAddr(ref) == nil {
45 panic("referrer not registered")
46 }
47 }
48
49 p := &Profile{
50 Addr: who,
51 Handle: handle,
52 Name: name,
53 Bio: bio,
54 Referrer: ref,
55 Joined: runtime.ChainHeight(),
56 }
57 state.byAddr.Set(who.String(), p)
58 state.byHandle.Set(handle, p)
59 state.books.Set(who.String(), &book{})
60 chain.Emit(
61 "ArenaRegister",
62 "addr", who.String(),
63 "handle", handle,
64 "referrer", ref.String(),
65 )
66}
67
68// UpdateProfile replaces the display name and bio. The handle stays.
69func UpdateProfile(cur realm, name, bio string) {
70 who := caller(cur)
71 mustNoPay()
72 p := mustMember(who)
73 if strings.TrimSpace(name) == "" {
74 name = p.Handle
75 }
76 p.Name = cleanLine(name, maxNameRunes, maxNameBytes, "name", false)
77 p.Bio = cleanLine(bio, maxBioRunes, maxBioBytes, "bio", true)
78 chain.Emit("ArenaProfile", "addr", who.String(), "handle", p.Handle)
79}
80
81// Follow records that the caller follows handle.
82func Follow(cur realm, handle string) {
83 who := caller(cur)
84 mustNoPay()
85 self := mustMember(who)
86 target := lookup(handle)
87 if target == nil {
88 panic("unknown handle")
89 }
90 if target.Addr == who {
91 panic("cannot follow self")
92 }
93 k := followKey(who, target.Addr)
94 if state.follows.Has(k) {
95 panic("already following")
96 }
97 state.follows.Set(k, 1)
98 state.fans.Set(fanKey(target.Addr, who), 1)
99 self.Following++
100 target.Followers++
101 chain.Emit("ArenaFollow", "from", who.String(), "to", target.Addr.String(), "handle", target.Handle)
102}
103
104// Unfollow removes a follow.
105func Unfollow(cur realm, handle string) {
106 who := caller(cur)
107 mustNoPay()
108 self := mustMember(who)
109 target := lookup(handle)
110 if target == nil {
111 panic("unknown handle")
112 }
113 k := followKey(who, target.Addr)
114 if !state.follows.Has(k) {
115 panic("not following")
116 }
117 state.follows.Remove(k)
118 state.fans.Remove(fanKey(target.Addr, who))
119 if self.Following > 0 {
120 self.Following--
121 }
122 if target.Followers > 0 {
123 target.Followers--
124 }
125 chain.Emit("ArenaUnfollow", "from", who.String(), "to", target.Addr.String(), "handle", target.Handle)
126}
127
128// Following reports whether from follows the handle.
129func Following(from address, handle string) bool {
130 target := lookup(handle)
131 if target == nil {
132 return false
133 }
134 return state.follows.Has(followKey(from, target.Addr))
135}