Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ad29253
feat: base gnopub function
divisio74 Oct 1, 2025
fb7a332
feat: UpdateLabel & SellCells
divisio74 Oct 2, 2025
394698b
Merge branch 'main' into feat/gnoPub
leohhhn Oct 2, 2025
30f9cfc
Fix: Coding style
divisio74 Oct 3, 2025
22cf13a
Add: test_file for GridRender
divisio74 Oct 3, 2025
e21b347
Fix: check CI std lib
divisio74 Oct 6, 2025
aae2178
Fix: incorrect English + svgbtn package
divisio74 Oct 6, 2025
2acd643
Fix: gnoPUB -> gnoADS
divisio74 Oct 6, 2025
8958999
Fix: Simplify condition body -> RenderGrid()
divisio74 Oct 7, 2025
9eab2e1
Global Variable and simplify RenderGrid
divisio74 Oct 7, 2025
9441d86
Fix: Repeated condition coordinates
divisio74 Oct 7, 2025
5ece175
Fix: CI check grid.gno/render.gno
divisio74 Oct 8, 2025
5ca4482
Fix: README.md cells->cell
divisio74 Oct 9, 2025
0003d77
Fix: getCoordinate
divisio74 Oct 19, 2025
7810b78
Fix: README.md
divisio74 Oct 19, 2025
49e736c
Fix: README
divisio74 Oct 19, 2025
c59e3d5
Fix: render->: and Capital Letter
divisio74 Oct 19, 2025
99f4726
Fix: Init in type.gno -> init in grid.gno
divisio74 Oct 19, 2025
e6f97af
Add: TXlinks for tutorial in Render
divisio74 Oct 20, 2025
a9d61eb
Fix: FMT CI Check for render.gno
divisio74 Oct 20, 2025
172ac4a
Fix: Name of the package
divisio74 Nov 11, 2025
57f2a12
Add: Moul/txlink import/optimisation
divisio74 Nov 11, 2025
c3ce434
Fix: Espace variable -> repeat
divisio74 Nov 11, 2025
60e1b22
Fix: FMT CI Check
divisio74 Nov 11, 2025
57aeb67
Fix: Wrong url
divisio74 Nov 12, 2025
e5b782e
Fix: Exporting function Reset
divisio74 Nov 18, 2025
851866e
Fix: Manage Render files
divisio74 Nov 18, 2025
def816b
Fix: Fmt CI Check
divisio74 Nov 18, 2025
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
26 changes: 26 additions & 0 deletions packages/r/pierre115/gnopub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## GnoPUB 📺

# Intro

I aimed to recreate an advertising space similar to the Sandbox project, but encountered several challenges when trying to paste and integrate images onto the clickable surface.

For now, the concept focuses on building an advertising grid where individual slots can be bought and sold. Once a plot is purchased, it becomes a link to the buyer’s advertisement, which can be configured using the `UpdateCell` function.
Each ad is tied to a specific cell, allowing users to own multiple spaces for different promotional content.

# Tutorial

### Step 1
Buy a cell with the function `BuyCell`
### Step 2
Set the ads and the label with the function `UpdateCell` on yours cells
### Step 3
Sell your cells with the function `SellCell`

# RoadMap

- Add determined price (`n` / `a`) * `price`

Where `n is the number of cells` and `a is the number of available cells`

- Add number of clicks

2 changes: 2 additions & 0 deletions packages/r/pierre115/gnopub/gnomod.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module = "gno.land/r/pierre115/gnopub"
gno = "0.9"
146 changes: 146 additions & 0 deletions packages/r/pierre115/gnopub/grid.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package gnopub

import (
"chain/runtime"

"gno.land/p/nt/ownable"
"gno.land/p/nt/ufmt"
)

var (
OwnableAddress = ownable.NewWithAddress("g1e7r5eglptfejknps3xv2kxv6pc8wvd3wwtw6tg")

colors = []string{"#ffffff", "#f44336", "#2196f3", "#4caf50", "#ff9800"} // blanc, rouge, bleu, vert, orange

spacingCount = 23

defaultlink = ufmt.Sprintf("/r/leon/hor:hall")

grid Grid
)

const (
cellSize = 100
defaultcolor = "#ffffff"
)

// Cell structure init
func init() {
grid = Grid{
Width: 5,
Height: 5,
Cells: make(map[string]Cell),
}
}

// Function to validate the coordinates
func getCoordinate(x, y int) string {
if x < 0 || x >= grid.Width || y < 0 || y >= grid.Height {
return ufmt.Sprintf("Invalid coordinate : X=%d, Y=%d", x, y)
}
return ""
}

// Function to sell a cell
func SellCell(_ realm, x, y int) string {
if err := getCoordinate(x, y); err != "" {
return err
}

key := ufmt.Sprintf("%d,%d", x, y)
caller := runtime.PreviousRealm().Address()
cell, exists := grid.Cells[key]

if exists && cell.Owner == caller {
grid.Cells[key] = Cell{
X: x,
Y: y,
Color: "",
Owner: "",
Url: "",
Label: "",
}
return ufmt.Sprintf("The cell was successfully sold.", x, y)
}

return "You are not the owner of this cell."
}

// Function to update the Ads and the Label of yours cells
func UpdateCell(_ realm, x, y int, url, label string) string {
if err := getCoordinate(x, y); err != "" {
return err
}

key := ufmt.Sprintf("%d,%d", x, y)
caller := runtime.PreviousRealm().Address()
cell, exists := grid.Cells[key]

if exists && cell.Owner == caller {
grid.Cells[key] = Cell{
X: x,
Y: y,
Color: cell.Color,
Owner: cell.Owner,
Url: url,
Label: label,
}
return ufmt.Sprintf("The cell Url was successfully Updated!")
}

return "You are not the owner of this cell."
}

// Cell clicked function if the cells is not owned
func BuyCell(_ realm, x, y int) string {
if err := getCoordinate(x, y); err != "" {
return err
}

key := ufmt.Sprintf("%d,%d", x, y)
caller := runtime.PreviousRealm().Address()

// If the cells is not owned, buy it
cell, exists := grid.Cells[key]
if !exists || cell.Owner == "" {
grid.Cells[key] = Cell{
X: x,
Y: y,
Color: colors[1], // red at first
Owner: caller,
Url: "",
Label: "",
}
return ufmt.Sprintf("The cell was successfully (%d,%d) purchased!", x, y)
}

// if it is the person who own it, change the color
if cell.Owner == caller {
currentColorIndex := 0
for i, c := range colors {
if c == cell.Color {
currentColorIndex = i
break
}
}
nextColorIndex := (currentColorIndex + 1) % len(colors)
cell.Color = colors[nextColorIndex]
grid.Cells[key] = cell
return ufmt.Sprintf("The cells (%d,%d) was updated! New color: %s", x, y, getColorName(cell.Color))
}

return "This cell is owned."
}

// Reset grid (admin only)
func Reset() string {
if !isAuthorized(runtime.PreviousRealm().Address()) {
panic("unauthorized")
}
grid.Cells = make(map[string]Cell)
return "The grid was successfully reset."
}

func isAuthorized(addr address) bool {
return addr == OwnableAddress.Owner()
}
27 changes: 27 additions & 0 deletions packages/r/pierre115/gnopub/grid_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package gnopub

import (
"strings"
"testing"
)

// Test for basic grid rendering
func TestRenderGrid(t *testing.T) {
got := renderGrid()

if got == "" {
t.Fatal("RenderGrid output is empty")
}

if !strings.Contains(got, "svg") {
t.Error("expected at least one SVG element in render output")
}

if !strings.Contains(got, "BuyCell") {
t.Error("expected 'BuyCells' tutorial step in render output")
}

if !strings.Contains(got, "available") {
t.Error("expected 'available' label for unowned cells")
}
}
16 changes: 16 additions & 0 deletions packages/r/pierre115/gnopub/helpers.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package gnopub

func getColorName(color string) string {
switch color {
case "#f44336":
return "Red"
case "#2196f3":
return "Blue"
case "#4caf50":
return "Green"
case "#ff9800":
return "Orange"
default:
return "White"
}
}
115 changes: 115 additions & 0 deletions packages/r/pierre115/gnopub/render.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package gnopub

import (
"chain/runtime"
"strings"

"gno.land/p/leon/svgbtn"
"gno.land/p/moul/txlink"
"gno.land/p/nt/ufmt"
)

func Render(path string) string {
out := "# GnoADS 📺\n\n"
out += "### ADS Grid \n\n"

// Path handling
if path == "stats" {
return out + getStats() + "\n\n[← Retour](/r/gnopixel)"
}

// Main grid
out += renderGrid() + "\n\n"

// Tutorial
out += "## Tuto 📚\n\n"

out += "### Step 1: Buy a cell\n\n"
out += "Click on a white cell in the grid or use the BuyCell function:\n\n"
out += svgbtn.Button(120, 35, "#4caf50", "#ffffff", "Buy Cell (0,0)", "/r/pierre115/gnoads$help&func=BuyCell&x=0&y=0") + "\n\n"
out += "Replace x and y with your desired coordinates.\n\n"

out += "### Step 2: Update your cell\n\n"
out += "Set the ads URL and label for your cell:\n\n"
out += svgbtn.Button(120, 35, "#4caf50", "#ffffff", "Update Cell (0,0)", "/r/pierre115/gnoads$help&func=UpdateCell&x=0&y=0&url=https://github.com/gnolang/hackerspace/issues/122&label=test") + "\n\n"
out += "- `x`, `y`: coordinates of your cell\n"
out += "- `url`: your advertisement URL (must start with http)\n"
out += "- `label`: text to display\n\n"

out += "### Step 3: Change color\n\n"
out += "Click again on your owned cell to cycle through colors:\n\n"
out += svgbtn.Button(120, 35, "#4caf50", "#ffffff", "Change Color (0,0)", "/r/pierre115/gnoads$help&func=BuyCell&x=0&y=0") + "\n\n"

out += "### Step 4: Sell your cell\n\n"
out += "Release your cell back to the grid:\n\n"
out += svgbtn.Button(120, 35, "#4caf50", "#ffffff", "Sell Cell (0,0)", "/r/pierre115/gnoads$help&func=SellCell&x=0&y=0") + "\n\n"

// Utility buttons
out += "---\n\n"
out += "## Admin Pannel 🛠️\n\n"
out += svgbtn.DangerButton(100, 30, "Reset", "/r/gnopixel$help&func=reset") + "\n\n"

// Stats
out += getStats()

return out
}

// Render the grid
func renderGrid() string {
out := ""

// Grid cells
for y := 0; y < grid.Height; y++ {
for x := 0; x < grid.Width; x++ {
key := ufmt.Sprintf("%d,%d", x, y)
color := defaultcolor
link := defaultlink
// If the cell is owned, use its color
if cell, ok := grid.Cells[key]; ok {
color = cell.Color
caller := runtime.PreviousRealm().Address()

if cell.Url != "" {
link = cell.Url
}

// If the cell is owned by someone else, link to their pub if set
if cell.Owner != "" && cell.Owner != caller {
out += svgbtn.Button(cellSize, cellSize, color, "#333333", cell.Label, link)
out += strings.Repeat("&nbsp;", spacingCount)
continue
}
}
link = txlink.NewLink("BuyCell").
AddArgs("x", ufmt.Sprintf("%d", x), "y", ufmt.Sprintf("%d", y)).
URL()
out += svgbtn.Button(cellSize, cellSize, color, "#333333", "available", link)
out += strings.Repeat("&nbsp;", spacingCount)
}
out += "\n\n"
}

return out
}

// Stats of the grid (per cells owners)
func getStats() string {
stats := make(map[address]int)
for _, cell := range grid.Cells {
if cell.Owner != "" {
stats[cell.Owner]++
}
}

out := "## Stats 📊:\n"
if len(stats) == 0 {
out += "No cells used.\n"
return out
}

for owner, count := range stats {
out += ufmt.Sprintf("- %s: %d cell(s)\n", owner.String(), count)
}
return out
}
18 changes: 18 additions & 0 deletions packages/r/pierre115/gnopub/type.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gnopub

// Cell structure
type Cell struct {
X int
Y int
Color string // Colors of the cells if owned
Owner address // Address of the owner
Url string // Url of the ads
Label string // Pseudo of the owner / name of the ads
}

// Global grid variable
type Grid struct {
Width int
Height int
Cells map[string]Cell
}
Loading