comparison src/links.html.luan @ 0:8f4df159f06b

start public repo
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 11 Jul 2025 20:57:49 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:8f4df159f06b
1 local Luan = require "luan:Luan.luan"
2 local error = Luan.error
3 local ipairs = Luan.ipairs or error()
4 local Html = require "luan:Html.luan"
5 local html_encode = Html.encode or error()
6 local Table = require "luan:Table.luan"
7 local concat = Table.concat or error()
8 local Io = require "luan:Io.luan"
9 local Http = require "luan:http/Http.luan"
10 local Shared = require "site:/lib/Shared.luan"
11 local head = Shared.head or error()
12 local body_header = Shared.body_header or error()
13 local footer = Shared.footer or error()
14 local show_editable_link = Shared.show_editable_link or error()
15 local User = require "site:/lib/User.luan"
16 local Link = require "site:/lib/Link.luan"
17 local get_owner_links = Link.get_owner_links or error()
18 local Pic = require "site:/lib/Pic.luan"
19 local Utils = require "site:/lib/Utils.luan"
20 local to_list = Utils.to_list or error()
21 local Logging = require "luan:logging/Logging.luan"
22 local logger = Logging.logger "links.html"
23
24
25 local function hashtags(pic)
26 local hashtags = to_list(pic.hashtags)
27 for i, hashtag in ipairs(hashtags) do
28 hashtags[i] = "#"..hashtag
29 end
30 return concat(hashtags," ")
31 end
32
33 local function div_links(links,pic_id)
34 %>
35 <div links>
36 <form add onsubmit="ajaxForm('add_link.js',this)" action="javascript:">
37 <% if pic_id ~= nil then %>
38 <input type=hidden name=pic value="<%=pic_id%>">
39 <% end %>
40 <input type=text required name=title placeholder="Title">
41 <input type=url required name=url placeholder="URL">
42 <button type=submit big>Add link</button>
43 </form>
44 <div start></div>
45 <% for _, link in ipairs(links) do
46 show_editable_link(link)
47 end %>
48 </div>
49 <%
50 end
51
52 return function()
53 local user = User.current_required()
54 if user==nil then return end
55 local owner = user
56 local pic = Http.request.parameters.pic
57 if pic ~= nil then
58 pic = Pic.get_by_id(pic)
59 if pic == nil then
60 logger.warn("pic not found\n"..Http.request.raw_head)
61 Http.response.send_error(404)
62 return
63 end
64 pic.user_id == user.id or error()
65 owner = pic
66 end
67 local links = get_owner_links(owner.id)
68 local pic_id = pic and pic.id
69 Io.stdout = Http.response.text_writer()
70 %>
71 <!doctype html>
72 <html lang="en">
73 <head>
74 <% head() %>
75 <title>Link My Style</title>
76 <style>
77 form[add] {
78 margin-bottom: 40px;
79 }
80 input {
81 margin-bottom: 5px;
82 }
83 input[type="url"],
84 input[type="text"] {
85 display: block;
86 }
87 div[link] {
88 margin-bottom: 20px;
89 }
90 button[small] {
91 font-size: 12px;
92 }
93 div[delete2] p {
94 margin-bottom: 5px;
95 }
96
97 div[link] > div:first-of-type {
98 border-radius: 12px / 50%;
99 margin-bottom: 5px;
100 overflow: hidden;
101 position: relative;
102 border: 1px solid #ebebeb;
103 }
104 div[link] a {
105 border-radius: initial;
106 margin-bottom: initial;
107 }
108 div[link] a:hover {
109 background-color: #243F47;
110 }
111 div[link] img {
112 position: absolute;
113 top: 0;
114 height: 100%;
115 background-color: white;
116 opacity: 0.3;
117 padding: 4px;
118 touch-action: none;
119 }
120
121 <% if pic == nil then %>
122 div[links] {
123 margin-top: 20px;
124 }
125 <% else %>
126 div[msg] {
127 margin-top: 20px;
128 margin-left: 5%;
129 color: darkgreen;
130 <% if Http.request.parameters.saved == nil then %>
131 display: none;
132 <% end %>
133 }
134 div[body] {
135 margin-top: 40px;
136 margin-bottom: 20px;
137 }
138 div[pic] img {
139 width: 100%;
140 display: block;
141 margin-bottom: 5px;
142 }
143 div[pic] form {
144 margin-top: 20px;
145 }
146 div[field] {
147 margin-top: 1px;
148 margin-bottom: 10px;
149 }
150 div[hashtags] {
151 margin-top: 20px;
152 }
153 @media (min-width: 888px) {
154 div[body] {
155 display: flex;
156 }
157 div[pic] {
158 width: 45%;
159 margin-left: 5%;
160 }
161 div[outer_links] {
162 width: 55%;
163 margin-left: 5%;
164 margin-right: 5%;
165 }
166 }
167 @media (max-width: 887px) {
168 div[pic] {
169 display: block;
170 width: 90%;
171 margin-left: auto;
172 margin-right: auto;
173 margin-bottom: 40px;
174 }
175 }
176 <% end %>
177 </style>
178 <script>
179 'use strict';
180
181 function clearAddForm() {
182 let form = document.querySelector('form[add]');
183 form.querySelector('[name="title"]').value = '';
184 form.querySelector('[name="url"]').value = '';
185 }
186
187 function deleteLink1(button,linkId) {
188 let div = button.parentNode;
189 if( div.querySelector('div[delete2]') )
190 return;
191 let html = `
192 <div delete2>
193 <p>Are you sure that you want to delete this?</p>
194 <button delete2 small onclick="deleteLink2('${linkId}')">Yes</button>
195 <button small onclick="undelete1(this)">No</button>
196 </div>
197 ` ;
198 div.insertAdjacentHTML( 'beforeEnd', html );
199 div.scrollIntoViewIfNeeded(false);
200 }
201 function undelete1(button) {
202 button.parentNode.outerHTML = '';
203 }
204 function deleteLink2(linkId) {
205 ajax( '/delete_link.js?link='+linkId );
206 }
207 function deletePic1(button) {
208 let div = button.parentNode;
209 if( div.querySelector('div[delete2]') )
210 return;
211 let html = `
212 <div delete2>
213 <p>Are you sure that you want to delete this?</p>
214 <button delete2 small onclick="deletePic2('<%=pic_id%>')">Yes</button>
215 <button small onclick="undelete1(this)">No</button>
216 </div>
217 ` ;
218 div.insertAdjacentHTML( 'beforeEnd', html );
219 div.scrollIntoViewIfNeeded(false);
220 }
221 function deletePic2() {
222 ajax( '/delete_pic.js?pic=<%=pic_id%>' );
223 }
224
225 function editLink(linkId) {
226 ajax( '/edit_link.js?link='+linkId );
227 }
228
229 function cancel(linkId) {
230 ajax( '/cancel_edit_link.js?link='+linkId );
231 }
232
233 dad.onDropped = function(event) {
234 let dragging = event.original;
235 if( iDragging === indexOf(dragging.parentNode.querySelectorAll(dropSelector),dragging) )
236 return;
237 let linkId = dragging.getAttribute('link');
238 let prev = dragging.previousElementSibling;
239 let prevId = prev && prev.getAttribute('link');
240 ajax( '/move_link.js?link='+linkId+'&prev='+prevId );
241 };
242
243 dad.whatToDrag = function(draggable) {
244 return draggable.parentNode.parentNode;
245 };
246
247 function dragInit() {
248 dropSelector = 'div[link]';
249 let items = document.querySelectorAll('div[link] img');
250 for( let i=0; i<items.length; i++ ) {
251 let item = items[i];
252 dad.setDraggable(item);
253 dad.setDropzone(item.parentNode.parentNode);
254 }
255 }
256
257 <% if pic ~= nil then %>
258 function changePic(uuid,filename) {
259 ajax( '/change_pic.js', 'pic=<%=pic.id%>&uuid=' + uuid + '&filename=' + encodeURIComponent(filename) );
260 }
261 function startChangePic() {
262 uploadcare.cropprOptions = {};
263 uploadcare.upload(changePic);
264 }
265 <% end %>
266 </script>
267 </head>
268 <body>
269 <div full>
270 <%
271 body_header()
272 if pic == nil then
273 %>
274 <p top>Enter your title and a URL to create a link. Once saved, you can drag-and-drop the icon on the left to change the order. The order on this page will also appear on your page.</p>
275 <%
276 div_links(links,pic_id)
277 else
278 %>
279 <div back>
280 <a href="/pics.html#p-<%=pic.id%>"><img src="/images/keyboard_backspace.svg"></a>
281 </div>
282 <div msg>
283 Your image has been saved.
284 </div>
285 <div body>
286 <div pic>
287 <img <%=pic.title_attr()%> src="<%=pic.get_url()%>">
288 <div>
289 <button type=button small onclick="startChangePic()">Change</button>
290 <button type=button small onclick="deletePic1(this)">Delete</button>
291 </div>
292 <div hashtags><%=pic.hashtags_html("pics.html")%></div>
293 <form onsubmit="ajaxForm('/save_pic_title.js',this)" action="javascript:">
294 <input type=hidden name=pic value="<%=pic_id%>">
295 <label>Photo title</label>
296 <div field>
297 <input type=text required name=title placeholder="Title" value="<%= html_encode(pic.title or "") %>">
298 </div>
299 <label>Hashtags</label>
300 <div field>
301 <textarea name=hashtags placeholder="#hashtag1 #hashtag2"><%= hashtags(pic) %></textarea>
302 <div error=hashtags></div>
303 </div>
304 <div field>
305 <label clickable><input type=checkbox name=visible <%=pic.is_hidden and "" or "checked"%>> Visible</label>
306 </div>
307 <button type=submit small>Save</button>
308 <div error=success></div>
309 </form>
310 </div>
311 <div outer_links>
312 <% div_links(links,pic_id) %>
313 </div>
314 </div>
315 <%
316 end
317 %>
318 <% footer() %>
319 </div>
320 <script> dragInit(); </script>
321 </body>
322 </html>
323 <%
324 end