Mercurial Hosting > linkmystyle
comparison src/lib/Shared.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 pairs = Luan.pairs or error() | |
5 local range = Luan.range or error() | |
6 local Table = require "luan:Table.luan" | |
7 local is_list = Table.is_list or error() | |
8 local is_empty = Table.is_empty or error() | |
9 local concat = Table.concat or error() | |
10 local Io = require "luan:Io.luan" | |
11 local uri = Io.uri or error() | |
12 local Html = require "luan:Html.luan" | |
13 local html_encode = Html.encode or error() | |
14 local url_encode = Html.url_encode or error() | |
15 local Parsers = require "luan:Parsers.luan" | |
16 local json_parse = Parsers.json_parse or error() | |
17 local json_string = Parsers.json_string or error() | |
18 local Thread = require "luan:Thread.luan" | |
19 local thread_run = Thread.run or error() | |
20 local sleep = Thread.sleep or error() | |
21 local Time = require "luan:Time.luan" | |
22 local Http = require "luan:http/Http.luan" | |
23 local Mail = require "luan:mail/Mail.luan" | |
24 local Config = require "site:/private/Config.luan" | |
25 local uploadcare_public_key = Config.uploadcare.public_key or error() | |
26 local Ab_test = require "site:/lib/Ab_test.luan" | |
27 local ab_test_head = Ab_test.head or error() | |
28 local User = require "site:/lib/User.luan" | |
29 local current_user = User.current or error() | |
30 local Icon = require "site:/lib/Icon.luan" | |
31 local get_user_icons = Icon.get_user_icons or error() | |
32 local Utils = require "site:/lib/Utils.luan" | |
33 local is_production = not not Utils.is_production | |
34 local base_url = Utils.base_url or error() | |
35 local to_list = Utils.to_list or error() | |
36 local Logging = require "luan:logging/Logging.luan" | |
37 local logger = Logging.logger "Shared" | |
38 | |
39 | |
40 local Shared = {} | |
41 | |
42 Shared.has_facebook = false; | |
43 | |
44 local started = Time.now() | |
45 | |
46 Shared.is_production = is_production | |
47 local is_local = Http.domain == nil | |
48 local has_reporting = true | |
49 | |
50 Shared.compressed = Utils.compressed | |
51 | |
52 local mp_id = is_production and "404d4c479de9c3070252e692375e82ca" or "bd2099a22e4118350a46b5b360d8c4da" | |
53 Shared.mp_id = mp_id | |
54 local mp_url = "https://api.mixpanel.com/track" | |
55 | |
56 local function call_mixpanel_raw(url,data) | |
57 local options = { | |
58 method = "POST" | |
59 parameters = { | |
60 data = json_string(data) | |
61 verbose = "1" | |
62 } | |
63 } | |
64 -- logger.info(options.parameters.data) | |
65 local result = uri(url,options).read_text() | |
66 result = json_parse(result) | |
67 result.error and logger.error("mixpanel: "..result.error) | |
68 end | |
69 Shared.call_mixpanel_raw = call_mixpanel_raw | |
70 | |
71 function Shared.call_mixpanel(data) | |
72 if is_list(data) then | |
73 for _, event in ipairs(data) do | |
74 event.properties.token = mp_id | |
75 end | |
76 else | |
77 data.properties.token = mp_id | |
78 end | |
79 call_mixpanel_raw(mp_url,data) | |
80 end | |
81 | |
82 | |
83 local function remove_from_path(name) | |
84 local path = Http.request.path | |
85 if path == "/index.html" then | |
86 path = "/" | |
87 end | |
88 local params = Http.request.parameters | |
89 params[name] = nil | |
90 if not is_empty(params) then | |
91 local t = {} | |
92 for name, value in pairs(params) do | |
93 t[#t+1] = url_encode(name).."="..url_encode(value) | |
94 end | |
95 path = path.."?"..concat(t,"&") | |
96 end | |
97 return path | |
98 end | |
99 | |
100 local function min_head(user) | |
101 %> | |
102 <meta name="viewport" content="width=device-width, initial-scale=1"> | |
103 <link rel="icon" href="/images/favicon.png" type="image/png"> | |
104 <style> | |
105 @import "https://fonts.googleapis.com/css?family=Bitter"; | |
106 @import "/site.css?s=<%=started%>"; | |
107 </style> | |
108 <script src="/site.js?s=<%=started%>"></script> | |
109 <% | |
110 local theme_date = user and user.theme_date | |
111 if theme_date ~= nil then | |
112 %> | |
113 <style> @import "/theme.css?user=<%=user.id%>&date=<%=theme_date%>&s=<%=started%>"; </style> | |
114 <% | |
115 end | |
116 end | |
117 | |
118 function Shared.head() | |
119 local user = current_user() | |
120 min_head(user) | |
121 local source = Http.request.parameters.source | |
122 if source ~= nil then | |
123 local path = remove_from_path('source') | |
124 %> | |
125 <script> | |
126 history.replaceState(null,null,'<%=path%>'); | |
127 </script> | |
128 <% | |
129 end | |
130 if user == nil and Http.request.cookies.source == nil then | |
131 source = source or "" | |
132 %> | |
133 <script> | |
134 mixpanel.ours.identify(); | |
135 mixpanel.ours.people.set({'source':'<%=source%>'}); | |
136 </script> | |
137 <% | |
138 Http.response.set_persistent_cookie( "source", source ) | |
139 end | |
140 %> | |
141 <style> | |
142 @import "/uploadcare/croppr.css"; | |
143 @import "/uploadcare/uploadcare.css?s=<%=started%>"; | |
144 @import "/dad.css"; | |
145 @import "/admin.css?s=<%=started%>"; | |
146 </style> | |
147 <script src="/uploadcare/croppr.js"></script> | |
148 <script src="/uploadcare/uploadcare.js?s=<%=started%>"></script> | |
149 <script> window.uploadcarePubKey = '<%=uploadcare_public_key%>'; </script> | |
150 <script src="/dad.js?v1"></script> | |
151 <% if user ~= nil then %> | |
152 <script> | |
153 window.UserEmail = '<%= user.email or error() %>'; | |
154 window.UserName = '<%= user.name or error() %>'; | |
155 </script> | |
156 <% end %> | |
157 <script src="/admin.js?s=<%=started%>"></script> | |
158 <% | |
159 ab_test_head() | |
160 end | |
161 | |
162 function Shared.pub_head(user) | |
163 %> | |
164 <script> | |
165 window.Owner = '<%= user.name %>'; | |
166 <% if user.mp_id ~= nil then %> | |
167 window.OwnerMpId = <%= json_string(user.mp_id) %>; | |
168 <% end %> | |
169 </script> | |
170 <% | |
171 min_head(user) | |
172 if has_reporting then | |
173 %> | |
174 <script async src="/reporting.js?s=<%=started%>"></script> | |
175 <% | |
176 end | |
177 end | |
178 | |
179 function Shared.page_header() | |
180 %> | |
181 <a header href="/"> | |
182 <img logo=big src="/images/logo.png"> | |
183 <img logo=small src="/images/small_logo.png"> | |
184 </a> | |
185 <% | |
186 end | |
187 | |
188 function Shared.body_header() | |
189 local user = current_user() | |
190 %> | |
191 <div header> | |
192 <a left href="/"> | |
193 <img logo=big src="/images/logo.png"> | |
194 <img logo=small src="/images/small_logo.png"> | |
195 </a> | |
196 <% if user == nil then %> | |
197 <span right login> | |
198 <a button login href="/login.html">Log in</a> | |
199 <a button register href="/register.html">Sign up</a> | |
200 </span> | |
201 <% else %> | |
202 <span right pulldown> | |
203 <img src="<%= user.get_pic_url() or "/images/user.png" %>" onclick="clickMenu(this)"> | |
204 <div pulldown_menu> | |
205 <a href="javascript:copyLink()"> | |
206 <span>linkmy.style/<%=user.name%></span> | |
207 <span copy>Copy</span> | |
208 </a> | |
209 <a href="/<%=user.name%>">My page</a> | |
210 <a href="/account.html">My account</a> | |
211 <a href="/links.html">Links</a> | |
212 <a href="/pics.html">Photos</a> | |
213 <a href="/theme.html">My theme</a> | |
214 <a href="/qr_code.html">QR code</a> | |
215 <a href="/analytics.html">Analytics</a> | |
216 <a href="javascript:logout()">Log out</a> | |
217 </div> | |
218 </span> | |
219 <input clipboard value="<%=base_url()%>/<%=user.name%>"> | |
220 <% end %> | |
221 </div> | |
222 <% | |
223 end | |
224 | |
225 function Shared.footer() | |
226 %> | |
227 <div footer> | |
228 <span> | |
229 <a href="/help.html">Help</a> | |
230 <br>support@linkmy.style | |
231 </span> | |
232 <span> | |
233 <a href="https://apps.apple.com/us/app/linkmystyle/id6475133762"><img ios src="/images/ios.svg"></a> | |
234 <a href="https://www.instagram.com/linkmy.style/"><img src="/images/icons/instagram.svg"></a> | |
235 </span> | |
236 </div> | |
237 <% | |
238 end | |
239 | |
240 function Shared.show_editable_link(link) | |
241 %> | |
242 <div link="<%=link.id%>"> | |
243 <div> | |
244 <a link pub_content href="<%=html_encode(link.url)%>" draggable=false><%=html_encode(link.title)%></a> | |
245 <img src="/images/drag_indicator.svg"> | |
246 </div> | |
247 <button type=button small onclick="editLink('<%=link.id%>')">Edit</button> | |
248 <button type=button small onclick="deleteLink1(this,'<%=link.id%>')">Delete</button> | |
249 </div> | |
250 <% | |
251 end | |
252 | |
253 function Shared.show_saved(close_url) | |
254 %> | |
255 <div saved> | |
256 <p>Your changes have been saved.</p> | |
257 <p><a href="/theme.html">Edit Theme</a></p> | |
258 <a close href="<%=close_url%>"><img src="/images/close.svg"></a> | |
259 </div> | |
260 <% | |
261 end | |
262 | |
263 function Shared.show_user_icons(user) | |
264 local icons = get_user_icons(user.id) | |
265 %> | |
266 <div list> | |
267 <% | |
268 for _, icon in ipairs(icons) do | |
269 %> | |
270 <span icon="<%=icon.id%>"> | |
271 <img src="/images/drag_indicator.svg"> | |
272 <a <%=icon.title_attr()%> href="<%=html_encode(icon.url)%>" draggable=false> | |
273 <img src="/images/icons/<%=icon.name%>.svg" draggable=false> | |
274 </a> | |
275 </span> | |
276 <% | |
277 end | |
278 %> | |
279 </div> | |
280 <button big onclick="ajax('/edit_icons.js')"><%= #icons==0 and "Add" or "Edit" %></button> | |
281 <% | |
282 end | |
283 | |
284 function Shared.js_error(field,message) | |
285 %> | |
286 showError( context.form, '<%=field%>', <%=json_string(message)%> ); | |
287 <% | |
288 end | |
289 | |
290 local send_mail = Mail.sender(Config.mail_server).send | |
291 | |
292 function Shared.send_mail_async(mail) | |
293 thread_run( function() | |
294 for i in range(1,5) do | |
295 try | |
296 send_mail(mail) | |
297 return | |
298 catch e | |
299 logger.error("send_mail_async fail "..i..":\n"..e) | |
300 sleep(10000) | |
301 end | |
302 end | |
303 error "send_mail_async failed" | |
304 end ) | |
305 end | |
306 | |
307 Shared.theme_fields = { | |
308 background_color = "#c0d1d6" | |
309 link_background_color = "#325762" | |
310 link_hover_background_color = "#243f47" | |
311 link_text_color = "#ffffff" | |
312 title_color = "#000000" | |
313 bio_color = "#000000" | |
314 icon_color = "" | |
315 link_border_radius = "12px / 50%" | |
316 link_border_color = "" | |
317 link_shadow = "" | |
318 link_shadow_color = "" | |
319 font = "Bitter" | |
320 font_url = "" | |
321 background_img_uuid = "" | |
322 background_img_filename = "" | |
323 } | |
324 | |
325 function Shared.password_input(value) | |
326 value = value or "" | |
327 %> | |
328 <div password> | |
329 <input type=password required name=password value="<%=html_encode(value)%>" placeholder="Password"> | |
330 <img show src="/images/visibility.svg" onclick="showPassword(parentNode)"> | |
331 <img hide src="/images/visibility_off.svg" onclick="hidePassword(parentNode)"> | |
332 </div> | |
333 <% | |
334 end | |
335 | |
336 function Shared.get_hashtags_list(pics) | |
337 local hashtags_set = {} | |
338 for _, pic in ipairs(pics) do | |
339 local hashtags = pic.hashtags | |
340 if hashtags ~= nil then | |
341 hashtags = to_list(hashtags) | |
342 for _, hashtag in ipairs(hashtags) do | |
343 hashtags_set[hashtag] = true | |
344 end | |
345 local classes = concat( hashtags, " " ) | |
346 pic.class = [[class="]]..classes..[["]] | |
347 end | |
348 end | |
349 local list = {} | |
350 for hashtag, v in pairs(hashtags_set) do | |
351 if v == true then | |
352 list[#list+1] = hashtag | |
353 end | |
354 end | |
355 return list, hashtags_set | |
356 end | |
357 | |
358 return Shared |