Skip to main content

max / makenotwork

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