Skip to main content

max / makenotwork

11.7 KB · 237 lines History Blame Raw
1 {% extends "base.html" %}
2 {%- import "partials/carousel.html" as carousel -%}
3
4 {% block title %}{{ project.title }} - {{ creator_username }}{% endblock %}
5 {% block body_attrs %} class="padded-page project-page"{% endblock %}
6
7 {% block head %}
8 {# Tier 0 creator theme: primitive-layer override; semantic layer derives from it. #}
9 <style id="creator-theme">{{ theme_css|safe }}</style>
10 <meta property="og:title" content="{{ project.title }} by {{ creator_username }}">
11 <meta property="og:description" content="{{ project.description }}">
12 <meta property="og:type" content="website">
13 <meta property="og:url" content="{{ host_url }}/p/{{ project.slug }}">
14 <link rel="canonical" href="{{ host_url }}/p/{{ project.slug }}">
15 <meta property="og:site_name" content="Makenotwork">
16 {% if project.cover_image_url.is_some() %}<meta name="twitter:card" content="summary_large_image">{% else %}<meta name="twitter:card" content="summary">{% endif %}
17 <meta name="twitter:title" content="{{ project.title }} by {{ creator_username }}">
18 <meta name="twitter:description" content="{{ project.description }}">
19 {% if let Some(img) = project.cover_image_url %}
20 <meta property="og:image" content="{{ img }}">
21 <meta name="twitter:image" content="{{ img }}">
22 {% else %}
23 <meta property="og:image" content="{{ host_url }}/static/images/og-card.png">
24 <meta name="twitter:image" content="{{ host_url }}/static/images/og-card.png">
25 {% endif %}
26 <script type="application/ld+json">
27 {
28 "@context": "https://schema.org",
29 "@type": "CollectionPage",
30 "name": "{{ project.title_json()|safe }}",
31 "description": "{{ project.description_json()|safe }}",
32 "url": "{{ host_url }}/p/{{ project.slug }}",
33 "author": {
34 "@type": "Person",
35 "name": "{{ creator_username }}",
36 "url": "{{ host_url }}/u/{{ creator_username }}"
37 },
38 "numberOfItems": {{ project.item_count }},
39 "isPartOf": {
40 "@type": "WebSite",
41 "name": "Makenotwork",
42 "url": "{{ host_url }}"
43 }
44 }
45 </script>
46 <link rel="alternate" type="application/rss+xml" title="{{ project.title }} - RSS Feed" href="/p/{{ project.slug }}/rss">
47 {% endblock %}
48
49 {% block content %}
50 {% include "partials/site_header.html" %}
51
52 <div class="container">
53 <header class="store-header">
54 {% if let Some(img_url) = project.cover_image_url %}
55 <img src="{{ img_url }}" alt="{{ project.title }}" class="store-cover">
56 {% endif %}
57 <h1 class="store-title">{{ project.title }}<span class="dot">.</span></h1>
58 <div class="store-meta">
59 by <a href="/u/{{ creator_username }}">{{ creator_username }}</a> &middot;
60 <span>{{ project.item_count }} items</span>
61 </div>
62 <p class="store-description">{{ project.description }}</p>
63 <div class="store-actions">
64 {% if is_owner %}
65 <a href="/dashboard/project/{{ project.slug }}" class="btn-secondary">Edit Project</a>
66 {% endif %}
67 {% if session_user.is_some() %}
68 {% if is_following %}
69 <button class="btn-secondary follow-btn is-selected"
70 hx-delete="/api/follow/project/{{ project_id }}"
71 hx-swap="outerHTML">Following ({{ follower_count }})</button>
72 {% else %}
73 <button class="btn-secondary follow-btn"
74 hx-post="/api/follow/project/{{ project_id }}"
75 hx-swap="outerHTML">Follow ({{ follower_count }})</button>
76 {% endif %}
77 {% else if follower_count > 0 %}
78 <span class="follower-count-muted">{{ follower_count }} followers</span>
79 {% endif %}
80 <a href="/p/{{ project.slug }}/rss" class="btn-secondary">RSS Feed</a>
81 {% if has_blog_posts %}
82 <a href="/p/{{ project.slug }}/blog" class="btn-secondary">Blog</a>
83 {% endif %}
84 {% for repo in &git_repos %}
85 <a href="{{ repo.1 }}" class="btn-secondary">Git ({{ repo.0 }})</a>
86 {% endfor %}
87 {% if let Some(url) = community_url %}
88 <a href="{{ url }}" class="btn-secondary">Community</a>
89 {% endif %}
90 {% include "partials/tip_button.html" %}
91 </div>
92 </header>
93
94 {% if !gallery.is_empty() %}
95 <section class="project-gallery">
96 {% call carousel::carousel("project-gallery", gallery) %}{% endcall %}
97 </section>
98 {% endif %}
99
100 {% if !sections.is_empty() %}
101 <section class="project-sections">
102 <div class="section-tabs">
103 {% for section in sections %}
104 <button class="section-tab{% if loop.first %} is-selected{% endif %}"
105 data-tab="section-{{ section.slug }}"
106 data-action="onSwitchSectionTab" data-arg="section-{{ section.slug }}">{{ section.title }}</button>
107 {% endfor %}
108 </div>
109 {% for section in sections %}
110 <div class="section-panel{% if loop.first %} active{% endif %}" id="section-{{ section.slug }}">
111 {{ section.body_html|safe }}
112 </div>
113 {% endfor %}
114 </section>
115 <script src="/static/page-project.js?v=0623" defer></script>
116 {% endif %}
117
118 <section class="items-section">
119 <div class="items-header">
120 <h2 class="section-header">Available Items</h2>
121 <div class="view-controls">
122 <button type="button" class="view-btn" data-view="list" title="List view">|||</button>
123 <button type="button" class="view-btn is-selected" data-view="grid" title="Grid view">:::</button>
124 </div>
125 </div>
126 <div class="items-container items-grid-view" id="items-container">
127 <div class="items-grid">
128 {% for item in items %}
129 <div class="item-card">
130 {# The cover if there is one, the item type if there is not.
131 The type label is a placeholder, styled at 0.3 opacity,
132 and it used to be all this slot ever showed: `thumbnail`
133 is the type by construction in conversions.rs, so a
134 storefront full of uploaded artwork rendered a grid of
135 grey boxes reading "Plugin". #}
136 <a href="{% if item.can_access %}/i/{{ item.id }}{% else %}/purchase/{{ item.id }}{% endif %}" class="item-thumbnail{% if item.cover().is_none() %} item-thumbnail-empty{% endif %}">{% if let Some(img) = item.cover() %}<img src="{{ img }}" alt="{{ item.title }}" loading="lazy">{% else %}{{ item.thumbnail }}{% endif %}</a>
137 <div class="item-content">
138 <h3 class="item-title"><a class="unstyled-link" href="{% if item.can_access %}/i/{{ item.id }}{% else %}/purchase/{{ item.id }}{% endif %}">{{ item.title }}</a></h3>
139 <div class="item-meta">{{ item.item_type }}{% if item.bundle_item_count > 0 %} ({{ item.bundle_item_count }} items){% endif %} &middot; {{ item.release_date }}</div>
140 <div class="item-tags">
141 {% for tag in item.tags %}
142 <a href="/discover?tag={{ tag.slug }}" class="tag unstyled-link">{{ tag.name }}</a>
143 {% endfor %}
144 </div>
145 <p class="item-description">{{ item.description }}</p>
146 <div class="item-footer">
147 <div class="item-price">{{ item.price }}</div>
148 <div class="item-stats">{{ item.sales_count }} sales</div>
149 </div>
150 {% if item.can_access %}
151 <a href="/l/{{ item.id }}" class="btn-secondary">View in library</a>
152 {% else if item.is_free %}
153 <button class="btn-primary"
154 hx-post="/api/library/add/{{ item.id }}"
155 hx-swap="outerHTML">Add to Library</button>
156 {% else if item.pwyw_enabled %}
157 <a href="/purchase/{{ item.id }}" class="btn-primary">Pay What You Want</a>
158 {% else %}
159 <a href="/purchase/{{ item.id }}" class="btn-primary">Buy Once</a>
160 {% endif %}
161 </div>
162 </div>
163 {% endfor %}
164 </div>
165
166 <!-- List View -->
167 <div class="items-list">
168 {% for item in items %}
169 <a href="{% if item.can_access %}/i/{{ item.id }}{% else %}/purchase/{{ item.id }}{% endif %}" class="list-item">
170 <div class="list-item-info">
171 <span class="list-item-title">{{ item.title }}</span>
172 <span class="list-item-meta">{{ item.item_type }}</span>
173 </div>
174 <div class="list-item-price">{{ item.price }}</div>
175 </a>
176 {% endfor %}
177 </div>
178 </div>
179 </section>
180
181 {% if !subscription_tiers.is_empty() %}
182 <section class="tiers-section">
183 <h2 class="section-header">Membership</h2>
184 <div class="tiers-grid">
185 {% for tier in subscription_tiers %}
186 <div class="card--bordered tier-card">
187 <div class="tier-name">{{ tier.name }}</div>
188 <div class="tier-price">{{ tier.price }}</div>
189 {% if !tier.description.is_empty() %}
190 <p class="tier-description">{{ tier.description }}</p>
191 {% endif %}
192 {% if has_subscription %}
193 <div class="tier-active-badge">Subscribed</div>
194 {% else if session_user.is_some() %}
195 <form method="post" action="/stripe/subscribe/{{ tier.id }}">
196 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
197 <details class="promo-details">
198 <summary>Have a promo code?</summary>
199 <input type="text" name="promo_code" placeholder="e.g. TRIAL14" class="promo-input">
200 </details>
201 <button class="btn-primary" type="submit" data-loading-text="Redirecting to Stripe...">Subscribe</button>
202 </form>
203 {% else %}
204 <a href="/login" class="btn-primary">Log in to Subscribe</a>
205 {% endif %}
206 </div>
207 {% endfor %}
208 </div>
209 </section>
210 {% endif %}
211
212 <footer class="store-footer">
213 <p>Powered by <a href="/">Makenot<span class="dot">.</span>work</a> &middot; Fair distribution for creatives of all kinds</p>
214 <p class="store-footer-links">
215 <a href="/p/{{ project.slug }}" data-copy-link>Copy link</a> &middot;
216 <a href="/policy">Policy</a> &middot;
217 {% if session_user.is_some() %}
218 <a href="#" data-prevent data-action="show" data-target="report-modal">Report</a>
219 {% else %}
220 <a href="/login">Report</a>
221 {% endif %}
222 </p>
223 </footer>
224 </div>
225
226 {% if session_user.is_some() %}
227 {% let report_target_type = "project" %}
228 {% let report_target_id = project_id %}
229 {% let report_has_labels = true %}
230 {% include "partials/report_modal.html" %}
231 {% endif %}
232 {% endblock %}
233
234 {% block scripts %}
235 <script src="/static/page-project-2.js?v=0623" defer></script>
236 {% endblock %}
237