diff --git a/apps/apps.go b/apps/apps.go index 9751a96b..2db3d0fa 100644 --- a/apps/apps.go +++ b/apps/apps.go @@ -248,14 +248,30 @@ func Preview() string { limit = len(public) } for _, a := range public[:limit] { - sb.WriteString(fmt.Sprintf(`

%s — %s

`, + priceTag := `Free` + if a.Price > 0 { + priceTag = fmt.Sprintf(`%d credits`, a.Price) + } + sb.WriteString(fmt.Sprintf(`

%s — %s %s

`, htmlpkg.EscapeString(a.Slug), htmlpkg.EscapeString(a.Slug), htmlpkg.EscapeString(a.Name), - htmlpkg.EscapeString(truncate(a.Description, 60)), + htmlpkg.EscapeString(truncate(a.Description, 50)), + priceTag, )) } - sb.WriteString(fmt.Sprintf(`

%d apps available · Build new

`, len(public))) + paidCount := 0 + for _, a := range public { + if a.Price > 0 { + paidCount++ + } + } + sb.WriteString(fmt.Sprintf(`

%d apps available`, len(public))) + if paidCount > 0 { + sb.WriteString(fmt.Sprintf(` · %d paid`, paidCount)) + } + sb.WriteString(` · Build new

`) + sb.WriteString(`

Build apps, set your price, earn 90%% of every sale.

`) return sb.String() } @@ -381,7 +397,10 @@ func handleList(w http.ResponseWriter, r *http.Request) { // HTML var sb strings.Builder - sb.WriteString(`

Small, useful apps that do one thing well. No ads, no tracking, no bloat.

`) + sb.WriteString(`

Small, useful apps that do one thing well. Build apps, set your price, earn 90% of every sale.

`) + + // Pricing filter + pricing := r.URL.Query().Get("pricing") // Tag filter tag := r.URL.Query().Get("tag") @@ -408,6 +427,24 @@ func handleList(w http.ResponseWriter, r *http.Request) { sb.WriteString(``) } + // Pricing filter pills + hasPaid := false + hasFree := false + for _, a := range list { + if a.Price > 0 { + hasPaid = true + } else { + hasFree = true + } + } + if hasPaid && hasFree { + sb.WriteString(`
`) + sb.WriteString(fmt.Sprintf(`All`, pillStyle(pricing == ""))) + sb.WriteString(fmt.Sprintf(`Free`, pillStyle(pricing == "free"))) + sb.WriteString(fmt.Sprintf(`Paid`, pillStyle(pricing == "paid"))) + sb.WriteString(`
`) + } + // Filter by tag if tag != "" { var filtered []*App @@ -419,6 +456,25 @@ func handleList(w http.ResponseWriter, r *http.Request) { list = filtered } + // Filter by pricing + if pricing == "free" { + var filtered []*App + for _, a := range list { + if a.Price == 0 { + filtered = append(filtered, a) + } + } + list = filtered + } else if pricing == "paid" { + var filtered []*App + for _, a := range list { + if a.Price > 0 { + filtered = append(filtered, a) + } + } + list = filtered + } + if len(list) == 0 { sb.WriteString(`

No apps yet. Create the first one.

`) } else { diff --git a/wallet/handlers.go b/wallet/handlers.go index ff7edfc4..33d51103 100644 --- a/wallet/handlers.go +++ b/wallet/handlers.go @@ -38,6 +38,21 @@ func WalletPage(userID string) string { sb.WriteString(`

Add Credits → · Transfer →

`) sb.WriteString(``) + // App earnings summary + var totalEarnings int + for _, tx := range transactions { + if tx.Operation == OpAppRevenue { + totalEarnings += tx.Amount + } + } + if totalEarnings > 0 { + sb.WriteString(`
`) + sb.WriteString(`

App Earnings

`) + sb.WriteString(fmt.Sprintf(`

%d credits earned from your apps (recent)

`, totalEarnings)) + sb.WriteString(`

You keep 90%% of every sale. Manage your apps →

`) + sb.WriteString(`
`) + } + if !isAdmin { // Self-hosting note sb.WriteString(`
`) @@ -80,7 +95,19 @@ func WalletPage(userID string) string { for _, tx := range transactions { typeLabel := tx.Operation - if tx.Type == TxTopup { + if tx.Operation == OpAppUse { + if appSlug, ok := tx.Metadata["app"].(string); ok { + typeLabel = "App: " + appSlug + } else { + typeLabel = "App usage" + } + } else if tx.Operation == OpAppRevenue { + if appSlug, ok := tx.Metadata["app"].(string); ok { + typeLabel = "Earned: " + appSlug + } else { + typeLabel = "App revenue" + } + } else if tx.Type == TxTopup { typeLabel = "Deposit" } else if tx.Type == TxTransfer { if tx.Amount > 0 { @@ -626,8 +653,23 @@ func handlePricing(w http.ResponseWriter, r *http.Request) { sb.WriteString(fmt.Sprintf(`%s%d`, item.Description, item.Cost)) } sb.WriteString(``) + sb.WriteString(`
`) + + // App marketplace pricing + sb.WriteString(`
`) + sb.WriteString(`

App Marketplace

`) + sb.WriteString(`

Build apps and charge per use. You set the price, you earn the revenue.

`) + sb.WriteString(``) + sb.WriteString(``) + sb.WriteString(``) + sb.WriteString(``) + sb.WriteString(``) + sb.WriteString(`
Price range0–1000 credits per use
Creator share90%
Platform fee10%
Free appsNo charge
`) + sb.WriteString(`

Build an app →

`) + sb.WriteString(`
`) + sb.WriteString(`

JSON: curl -H "Accept: application/json" /wallet/pricing

`) - sb.WriteString(``) + sb.WriteString(``) html := app.RenderHTMLForRequest("Pricing", "Platform pricing and costs", sb.String(), r) w.Write([]byte(html))