home.gno

7.91 Kb ยท 251 lines
  1package home
  2
  3import (
  4	"std"
  5	"strconv"
  6	"strings"
  7
  8	"gno.land/p/demo/avl"
  9	"gno.land/p/demo/mux"
 10	"gno.land/p/demo/ufmt"
 11	"gno.land/p/moul/addrset"
 12	"gno.land/p/moul/md"
 13	"gno.land/r/leon/hof"
 14	"gno.land/r/mouss/config"
 15)
 16
 17// Profile represents my personal profile information.
 18type Profile struct {
 19	AboutMe   string
 20	Avatar    string
 21	Email     string
 22	Github    string
 23	LinkedIn  string
 24	Followers *addrset.Set // Set of followers addresses.
 25}
 26
 27// Recipe represents a cooking recipe with its details.
 28type Recipe struct {
 29	Name         string
 30	Origin       string
 31	Author       std.Address
 32	Ingredients  string
 33	Instructions string
 34	Tips         string
 35}
 36
 37const (
 38	realmURL = "/r/mouss/home"
 39	rec      = realmURL + ":recipe/"
 40	gnoArt   = `
 41        -==++.                                                                  
 42	     *@@@@=                                     @-                          -@
 43	    #@@@@@:       -==-.-- :-::===:   .-++-.     @-   .===:.- .-.-==-   .===:=@
 44       #@@@@@@@:    -@@%**%@@ #@@#*#@@- *@@**@@*    @-  +%=::-*@ +@=-:-@* +%=::-*@
 45      +@%#**#%@@    %@+   :@@ *@+   #@=+@%    %@+   @= :@:    -@ +%    +%.@:    -@
 46      -:       -    *@%:..+@@ *@+   #@=-@@:  :@@=   @- .@=    =@ +@    *%.@=    =@
 47      --:==+=-:=.    =%@%#*@@ *@+   #@+ =%@%%@%= #* %#=.:%*===*@ +%    +% -%*===*@
 48      -++++=++++.    =-:::*@#  .     .    .::.   ..  ::   .::  .  .         .::  .
 49       .-=+++=:     .*###%#=                                                      
 50	      ::                                                                      
 51`
 52)
 53
 54var (
 55	router          = mux.NewRouter()
 56	profile         Profile
 57	recipes         = avl.NewTree()
 58	margheritaPizza *Recipe
 59)
 60
 61// init initializes the router with the home page and recipe routes
 62// sets up my profile information, and my recipe
 63// and registers the home page in the hall of fame.
 64func init() {
 65	router.HandleFunc("", renderHomepage)
 66	router.HandleFunc("recipe/", renderRecipes)
 67	router.HandleFunc("recipe/{name}", renderRecipe)
 68	profile = Profile{
 69		AboutMe:   "๐Ÿ‘‹ I'm Mustapha, a contributor to gno.land project from France. I'm passionate about coding, exploring new technologies, and contributing to open-source projects. Besides my tech journey, I'm also a pizzaiolo ๐Ÿ• who loves cooking and savoring good food.",
 70		Avatar:    "https://github.com/mous1985/assets/blob/master/avatar.png?raw=true",
 71		Email:     "mustapha.benazzouz@outlook.fr",
 72		Github:    "https://github.com/mous1985",
 73		LinkedIn:  "https://www.linkedin.com/in/mustapha-benazzouz-88646887/",
 74		Followers: &addrset.Set{},
 75	}
 76	margheritaPizza = &Recipe{
 77		Name:         "Authentic Margherita Pizza ๐ŸคŒ",
 78		Origin:       "Naples, ๐Ÿ‡ฎ๐Ÿ‡น",
 79		Author:       config.OwnableMain.Owner(),
 80		Ingredients:  "  1kg 00 flour\n 500ml water\n 3g fresh yeast\n 20g sea salt\n San Marzano tomatoes\n Fresh buffalo mozzarella\n Fresh basil\n Extra virgin olive oil",
 81		Instructions: " Mix flour and water until incorporated\n Add yeast and salt, knead for 20 minutes\n Let rise for 2 hours at room temperature\n Divide into 250g balls\n Cold ferment for 24-48 hours\n Shape by hand, being gentle with the dough\n Top with crushed tomatoes, torn mozzarella, and basil\n Cook at 450ยฐC for 60-90 seconds",
 82		Tips:         "Use a pizza steel or stone preheated for at least 1 hour. The dough should be soft and extensible. For best results, cook in a wood-fired oven.",
 83	}
 84	hof.Register()
 85}
 86
 87// AddRecipe adds a new recipe in recipe page by users
 88func AddRecipe(name, origin, ingredients, instructions, tips string) string {
 89	if err := validateRecipe(name, ingredients, instructions); err != nil {
 90		panic(err)
 91	}
 92	recipe := &Recipe{
 93		Name:         name,
 94		Origin:       origin,
 95		Author:       std.PreviousRealm().Address(),
 96		Ingredients:  ingredients,
 97		Instructions: instructions,
 98		Tips:         tips,
 99	}
100	recipes.Set(name, recipe)
101	return "Recipe added successfully"
102}
103
104func UpdateAboutMe(about string) error {
105	if !config.IsAuthorized(std.PreviousRealm().Address()) {
106		panic(config.ErrUnauthorized)
107	}
108	profile.AboutMe = about
109	return nil
110}
111
112func UpdateAvatar(avatar string) error {
113	if !config.IsAuthorized(std.PreviousRealm().Address()) {
114		panic(config.ErrUnauthorized)
115	}
116	profile.Avatar = avatar
117	return nil
118}
119
120// validateRecipe checks if the provided recipe details are valid.
121func validateRecipe(name, ingredients, instructions string) error {
122	if name == "" {
123		return ufmt.Errorf("recipe name cannot be empty")
124	}
125	if len(ingredients) == 0 {
126		return ufmt.Errorf("ingredients cannot be empty")
127	}
128	if len(instructions) == 0 {
129		return ufmt.Errorf("instructions cannot be empty")
130	}
131	return nil
132}
133
134// Follow allows a users to follow my home page.
135// If the caller is admin it returns error.
136func Follow() error {
137	caller := std.PreviousRealm().Address()
138
139	if caller == config.OwnableMain.Owner() {
140		return ufmt.Errorf("you cannot follow yourself")
141	}
142	if profile.Followers.Add(caller) {
143		return nil
144	}
145	return ufmt.Errorf("you are already following")
146
147}
148
149// Unfollow allows a user to unfollow my home page.
150func Unfollow() error {
151	caller := std.PreviousRealm().Address()
152
153	if profile.Followers.Remove(caller) {
154		return nil
155	}
156	return ufmt.Errorf("you are not following")
157}
158
159// renderRecipes renders the list of recipes.
160func renderRecipes(res *mux.ResponseWriter, req *mux.Request) {
161	var out string
162	out += Header()
163	out += "## World Kitchen\n\n------\n\n"
164
165	// Link to margarita pizza recipe
166	out += "### Available Recipes:\n\n"
167	out += "* " + md.Link(margheritaPizza.Name, rec+"margheritaPizza") + "By : " + string(margheritaPizza.Author) + "\n"
168
169	// The list of all other recipes with clickable links
170	if recipes.Size() > 0 {
171		recipes.Iterate("", "", func(key string, value interface{}) bool {
172			recipe := value.(*Recipe)
173			out += "* " + md.Link(recipe.Name, rec+recipe.Name) + " By : " + recipe.Author.String() + "\n"
174			return false // continue iterating
175		})
176		out += "\n------\n\n"
177	} else {
178		out += "\nNo additional recipes yet. Be the first to add one!\n"
179	}
180	res.Write(out)
181}
182
183// renderRecipe renders the recipe details.
184func renderRecipe(res *mux.ResponseWriter, req *mux.Request) {
185	name := req.GetVar("name")
186	if name == "margheritaPizza" {
187		res.Write(margheritaPizza.Render())
188		return
189	}
190	value, exists := recipes.Get(name)
191	if !exists {
192		res.Write("Recipe not found")
193		return
194	}
195	recipe := value.(*Recipe)
196	res.Write(recipe.Render())
197}
198
199func (r Recipe) Render() string {
200	var out string
201	out += Header()
202	out += md.H2(r.Name)
203	out += md.Bold("Author:") + "\n" + r.Author.String() + "\n\n"
204	out += md.Bold("Origin:") + "\n" + r.Origin + "\n\n"
205	out += md.Bold("Ingredients:") + "\n" + md.BulletList(strings.Split(r.Ingredients, "\n")) + "\n\n"
206	out += md.Bold("Instructions:") + "\n" + md.OrderedList(strings.Split(r.Instructions, "\n")) + "\n\n"
207	if r.Tips != "" {
208		out += md.Italic("๐Ÿ’ก Tips:"+"\n"+r.Tips) + "\n\n"
209	}
210	out += md.HorizontalRule() + "\n"
211	return out
212}
213
214func renderHomepage(res *mux.ResponseWriter, req *mux.Request) {
215	var out string
216	out += Header()
217	out += profile.Render()
218	res.Write(out)
219}
220
221func (p Profile) Render() string {
222	var out string
223	out += md.H1("Welcome to my Homepage") + "\n\n" + md.HorizontalRule() + "\n\n"
224	out += "```\n"
225	out += gnoArt
226	out += "```\n------"
227	out += md.HorizontalRule() + "\n\n" + md.H2("About Me") + "\n\n"
228	out += md.Image("avatar", p.Avatar) + "\n\n"
229	out += p.AboutMe + "\n\n" + md.HorizontalRule() + "\n\n"
230	out += md.H3("Contact") + "\n\n"
231	out += md.BulletList([]string{
232		"Email: " + p.Email,
233		"GitHub: " + md.Link("@mous1985", p.Github),
234		"LinkedIn: " + md.Link("Mustapha", p.LinkedIn),
235	})
236	out += "\n\n" + md.Bold("๐Ÿ‘ค Followers: ") + strconv.Itoa(p.Followers.Size())
237	return out
238}
239
240func Header() string {
241	navItems := []string{
242		md.Link("Home", realmURL),
243		md.Link("World Kitchen", rec),
244		md.Link("Hackerspace", "https://github.com/gnolang/hackerspace/issues/86#issuecomment-2535795751"),
245	}
246	return strings.Join(navItems, " | ") + "\n\n" + md.HorizontalRule() + "\n\n"
247}
248
249func Render(path string) string {
250	return router.Render(path)
251}