admin.gno
4.27 Kb · 140 lines
1package radio
2
3import (
4 "chain"
5 "strings"
6
7 "gno.land/p/nym-alexiscolin000/gnoradio/role/v0"
8 "gno.land/p/nym-alexiscolin000/gnoradio/text/v0"
9 "gno.land/r/nym-alexiscolin000/gnoradio/data"
10)
11
12// The radio admin (curator and moderator) is a p/role held by this realm,
13// passed in two steps. Each change is mirrored to radio/config "admin", so
14// the next release keeps it, and the mirror is the authority: a change the
15// previous release made after this one's init counts.
16
17// adminAddr is the admin ("" once renounced).
18func adminAddr() address {
19 h, ok := data.Get(cConfig, "admin")
20 return role.Mirror(admin, h, ok)
21}
22
23// syncAdmin adopts the mirror when the previous release changed it after
24// this one's init (during the release's delay).
25func syncAdmin() {
26 h, ok := data.Get(cConfig, "admin")
27 admin = role.Adopt(admin, "admin", h, ok)
28}
29
30func onlyAdmin(cur realm) {
31 syncAdmin()
32 if admin == nil || !admin.Is(0, cur) {
33 panic("radio: admin only")
34 }
35 noPayment()
36}
37
38// Admin is the radio admin ("" once renounced).
39func Admin() address { return adminAddr() }
40
41// PendingAdmin is the address the admin role is offered to ("" if none).
42func PendingAdmin() address { return admin.Pending() }
43
44// Ready tells the data realm that this release is on chain (see
45// data.Propose). Anyone may call it.
46func Ready(cur realm) {
47 noPayment()
48 data.Ready(cross(cur))
49}
50
51// OfferAdmin offers the admin role to an address (offering it to the admin
52// cancels the offer); AcceptAdmin takes it. Each change is mirrored to the
53// data realm, so the next release keeps the admin.
54func OfferAdmin(cur realm, to address) {
55 onlyAdmin(cur)
56 admin.Offer(0, cur, to)
57}
58
59func AcceptAdmin(cur realm) {
60 noPayment()
61 syncAdmin()
62 if admin == nil {
63 panic("radio: nothing offered to you")
64 }
65 admin.Accept(0, cur)
66 data.Set(cross(cur), cConfig, "admin", admin.Holder().String())
67}
68
69// RenounceAdmin gives the admin role up for good, for this release and the
70// next ones.
71func RenounceAdmin(cur realm) {
72 onlyAdmin(cur)
73 admin.Renounce(0, cur)
74 data.Set(cross(cur), cConfig, "admin", "")
75}
76
77// DropSlot silences a track in one station's rotation (moderation of last
78// resort). It stays silent through Refresh until RestoreSlot. Radio admin only.
79func DropSlot(cur realm, stationID, trackID int) {
80 onlyAdmin(cur)
81 t := rd()
82 st := t.station(stationID)
83 i, ok := st.slot(trackID)
84 if st.id == MainStation {
85 ok = trackID >= 1 && trackID <= t.synced() // Main simulcasts: the drop flag alone silences it there
86 }
87 if !ok {
88 panic("radio: track not in this station's rotation")
89 }
90 ts := now()
91 if !t.has(cDropped, dropKey(st.id, trackID)) {
92 t.set(cDropped, dropKey(st.id, trackID), "1")
93 t.setFlowAt(fDrops, 4, t.flowAt(fDrops, 4)+1)
94 }
95 if st.id != MainStation {
96 st.setDuration(i, 0, ts)
97 }
98 release(cur, st.dropSlots(trackID, ts))
99 save(cur, t)
100 chain.Emit("SlotDropped", "station", text.Itoa(st.id), "track", text.Itoa(trackID))
101}
102
103// RestoreSlot undoes DropSlot: the track plays again on that station if the
104// catalog still allows it. Radio admin only.
105func RestoreSlot(cur realm, stationID, trackID int) {
106 onlyAdmin(cur)
107 mustStation(stationID)
108 t := rd()
109 if !t.del(cDropped, dropKey(stationID, trackID)) {
110 panic("radio: this slot was not dropped")
111 }
112 t.setFlowAt(fDrops, 4, t.flowAt(fDrops, 4)-1)
113 refresh(cur, t, trackID)
114 save(cur, t)
115 chain.Emit("SlotRestored", "station", text.Itoa(stationID), "track", text.Itoa(trackID))
116}
117
118// Unqueue removes a track's programmed slots from a station; one on air ends
119// now. Radio admin (curator) only.
120func Unqueue(cur realm, stationID, trackID int) {
121 onlyAdmin(cur)
122 t := rd()
123 st := t.station(stationID)
124 release(cur, st.dropSlots(trackID, now()))
125 save(cur, t)
126 chain.Emit("Unqueued", "station", text.Itoa(st.id), "track", text.Itoa(trackID))
127}
128
129// Render points gnoweb visitors to the GnoRadio site; a retired release says
130// which realm took over.
131func Render(_ string) string {
132 home := strings.TrimPrefix(strings.TrimSuffix(self, "radio/v1"), "gno.land") + "home/v1"
133 d := strings.TrimPrefix(dataPath, "gno.land")
134 out := "# GnoRadio · radio\n\nThis contract holds GnoRadio's radio rules; its data lives in [" + d + "](" + d +
135 "). The site lives at [" + home + "](" + home + ").\n"
136 if w := data.Writer("radio"); w != self {
137 out += "\nThis release is retired: the radio is now `" + w + "`.\n"
138 }
139 return out
140}