Skip to content

Commit 046aa7c

Browse files
committed
feat: re-add server side rendering support for browser extension
1 parent 9c5b77e commit 046aa7c

2 files changed

Lines changed: 261 additions & 0 deletions

File tree

src/main/kotlin/com/sakethh/linkora/presentation/routing/Routing.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,5 +281,6 @@ fun Application.configureRouting(serverConfig: ServerConfig, markdownManagerRepo
281281
syncRouting(syncRepo)
282282
multiActionRouting(multiActionRepo)
283283
tagsRouting(tagsRepo)
284+
browserExtensionRouting(foldersRepo)
284285
}
285286
}
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
package com.sakethh.linkora.presentation.routing.http
2+
3+
import Colors
4+
import com.sakethh.linkora.authenticate
5+
import com.sakethh.linkora.domain.Result
6+
import com.sakethh.linkora.domain.dto.Correlation
7+
import com.sakethh.linkora.domain.dto.IDBasedDTO
8+
import com.sakethh.linkora.domain.model.Folder
9+
import com.sakethh.linkora.domain.repository.FoldersRepo
10+
import io.ktor.http.*
11+
import io.ktor.server.response.*
12+
import io.ktor.server.routing.*
13+
import kotlinx.coroutines.runBlocking
14+
import kotlinx.html.*
15+
import kotlinx.html.stream.createHTML
16+
import sakethh.kapsule.*
17+
import sakethh.kapsule.utils.*
18+
19+
fun Routing.browserExtensionRouting(foldersRepo: FoldersRepo) {
20+
authenticate {
21+
get("/browser-extension") {
22+
call.respondText(text = createHTML().html {
23+
Surface(
24+
fonts = listOf(
25+
"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
26+
"https://fonts.googleapis.com/icon?family=Material+Icons+Outlined"
27+
),
28+
modifier = Modifier.width(400.px).height(250.px).backgroundColor(color = Colors.surfaceDark)
29+
.boxSizing(BoxSizing.BorderBox).margin(0.px).padding(0.px).custom("overflow: auto !important")
30+
) {
31+
runBlocking {
32+
BrowserExtensionUI(foldersRepo)
33+
}
34+
}
35+
}, contentType = ContentType.Text.Html)
36+
}
37+
}
38+
}
39+
40+
private suspend fun BODY.BrowserExtensionUI(foldersRepo: FoldersRepo) {
41+
val folders = (foldersRepo.getRootFolders() as Result.Success).response
42+
Column(
43+
modifier = Modifier.margin(15.px).custom(
44+
"""
45+
overflow: auto !important;
46+
overflow-x: auto !important;
47+
overflow-y: auto !important;
48+
""".trimIndent()
49+
)
50+
) {
51+
Button(
52+
modifier = Modifier.cursor(Cursor.Pointer).height(25.px).margin(bottom = 10.px)
53+
.backgroundColor(Colors.codeblockBG).color(Colors.onPrimaryContainerDark),
54+
id = "change-extension-config",
55+
onClick = { "" }) {
56+
Text(
57+
text = "Change Extension Config",
58+
fontWeight = FontWeight.Predefined.Normal,
59+
fontSize = 12.px,
60+
fontFamily = "Inter",
61+
)
62+
}
63+
Text(
64+
text = "Link address",
65+
fontFamily = "Inter",
66+
fontWeight = FontWeight.Predefined.Normal,
67+
fontSize = 10.px,
68+
color = Colors.onSurfaceDark,
69+
)
70+
Text(
71+
id = "link",
72+
text = "#LINK_PLACEHOLDER#",
73+
fontFamily = "Inter",
74+
fontWeight = FontWeight.Predefined.Medium,
75+
fontSize = 12.px,
76+
color = Colors.onSurfaceDark,
77+
modifier = Modifier.margin(bottom = 10.px)
78+
)
79+
Text(
80+
text = "Title for the link",
81+
fontFamily = "Inter",
82+
fontWeight = FontWeight.Predefined.Normal,
83+
fontSize = 14.px,
84+
color = Colors.onSurfaceDark,
85+
modifier = Modifier.margin(bottom = 5.px)
86+
)
87+
input(type = InputType.text) {
88+
id = "link_title"
89+
style = Modifier.backgroundColor(Colors.codeblockBG).color(Colors.onSurfaceDark).height(25.px)
90+
.margin(bottom = 10.px).padding(end = 15.px).fontFamily("Inter").toString()
91+
}
92+
Text(
93+
text = "Note for saving the link",
94+
fontFamily = "Inter",
95+
fontWeight = FontWeight.Predefined.Normal,
96+
fontSize = 14.px,
97+
color = Colors.onSurfaceDark,
98+
modifier = Modifier.margin(bottom = 5.px)
99+
)
100+
input(type = InputType.text) {
101+
id = "link_note"
102+
style = Modifier.backgroundColor(Colors.codeblockBG).color(Colors.onSurfaceDark).height(25.px)
103+
.margin(bottom = 10.px).padding(end = 15.px).fontFamily("Inter").toString()
104+
}
105+
Text(
106+
text = "Total root folders found: ${folders.size}",
107+
fontFamily = "Inter",
108+
fontWeight = FontWeight.Predefined.Thin,
109+
fontSize = 14.px,
110+
color = Colors.onSurfaceDark,
111+
)
112+
Spacer(modifier = Modifier.height(8.5.px))
113+
Button(
114+
modifier = Modifier.cursor(Cursor.Pointer).height(25.px).margin(bottom = 10.px)
115+
.backgroundColor(Colors.codeblockBG).color(Colors.onPrimaryContainerDark),
116+
id = "add-new-root-folder",
117+
onClick = { "" }) {
118+
Text(
119+
text = "Create a new root folder",
120+
fontWeight = FontWeight.Predefined.Medium,
121+
fontSize = 12.px,
122+
fontFamily = "Inter",
123+
)
124+
}
125+
Text(
126+
text = "Save to folder:",
127+
fontFamily = "Inter",
128+
fontWeight = FontWeight.Predefined.SemiBold,
129+
fontSize = 16.px,
130+
color = Colors.primaryDark,
131+
)
132+
Spacer(modifier = Modifier.height(10.px))
133+
Div(id = "folders-list") {
134+
FolderComponent(
135+
icon = "link", folder = Folder(
136+
id = -1, name = "Saved Links", note = "", parentFolderId = 0, isArchived = false, eventTimestamp = 0
137+
)
138+
)
139+
140+
FolderComponent(
141+
icon = "star_outline", folder = Folder(
142+
id = -2,
143+
name = "Important Links",
144+
note = "",
145+
parentFolderId = 0,
146+
isArchived = false,
147+
eventTimestamp = 0
148+
)
149+
)
150+
151+
runBlocking {
152+
folders.forEach { folder ->
153+
154+
val childFolders = (foldersRepo.getChildFolders(
155+
idBasedDTO = IDBasedDTO(
156+
id = folder.id,
157+
correlation = Correlation(id = "", clientName = ""),
158+
eventTimestamp = 1527,
159+
)
160+
) as Result.Success).response
161+
162+
FolderComponent(
163+
showDivider = childFolders.isEmpty(),
164+
folder = folder,
165+
bottomSpacing = if (childFolders.isEmpty()) 12 else 8
166+
)
167+
if (childFolders.isNotEmpty()) {
168+
ChildFoldersComponent(foldersRepo = foldersRepo, folders = childFolders)
169+
Spacer(
170+
modifier = Modifier.height(8.px)
171+
)
172+
Spacer(
173+
modifier = Modifier.fillMaxWidth(0.95).margin(start = 10.px, end = 15.px, bottom = 12.px)
174+
.padding(end = 15.px).height(0.75.px).opacity(0.15).backgroundColor(Colors.outlineDark)
175+
.borderRadius(5.px)
176+
)
177+
}
178+
}
179+
}
180+
}
181+
182+
}
183+
}
184+
185+
private suspend fun DIV.ChildFoldersComponent(
186+
marginStart: Int = 5, folders: List<Folder>, foldersRepo: FoldersRepo
187+
) {
188+
folders.forEach {
189+
Row {
190+
Spacer(
191+
modifier = Modifier.width((marginStart - 2).px).backgroundColor(Colors.primaryContainerDark).opacity(
192+
0.15
193+
)
194+
)
195+
Spacer(modifier = Modifier.width(5.px))
196+
FolderComponent(showDivider = false, folder = it, bottomSpacing = 8)
197+
}
198+
Spacer(modifier = Modifier.height(5.px))
199+
val childFolders = (foldersRepo.getChildFolders(
200+
idBasedDTO = IDBasedDTO(
201+
id = it.id,
202+
correlation = Correlation(id = "", clientName = ""),
203+
eventTimestamp = 1527,
204+
)
205+
) as Result.Success).response
206+
207+
if (childFolders.isNotEmpty()) {
208+
Spacer(modifier = Modifier.height(5.px))
209+
ChildFoldersComponent(
210+
marginStart = marginStart + 5, foldersRepo = foldersRepo, folders = childFolders
211+
)
212+
}
213+
}
214+
}
215+
216+
private fun DIV.FolderComponent(
217+
folder: Folder, showDivider: Boolean = true, icon: String = "create_new_folder", bottomSpacing: Int = 12
218+
) {
219+
Row(
220+
className = "folder-component", horizontalAlignment = HorizontalAlignment.Center
221+
) {
222+
span(classes = "material-icons-outlined") {
223+
id = "${folder.name}-${folder.id}"
224+
style = Modifier.color(
225+
Colors.primaryDark
226+
).cursor(Cursor.Pointer).blockSelection().size(24.px).toString()
227+
+icon
228+
}
229+
Row(
230+
id = folder.id.toString(),
231+
horizontalAlignment = HorizontalAlignment.Center,
232+
modifier = Modifier.cursor(Cursor.Pointer)
233+
) {
234+
Spacer(modifier = Modifier.width(8.5.px))
235+
Text(
236+
text = folder.name,
237+
fontFamily = "Inter",
238+
fontWeight = FontWeight.Predefined.Normal,
239+
fontSize = 16.px,
240+
color = Colors.primaryDark,
241+
modifier = Modifier.blockSelection()
242+
)
243+
}
244+
}
245+
if (showDivider) {
246+
Spacer(
247+
modifier = Modifier.height(12.px)
248+
)
249+
250+
Spacer(
251+
modifier = Modifier.fillMaxWidth(0.95).margin(start = 10.px, end = 15.px).padding(end = 15.px)
252+
.height(0.75.px).opacity(0.15).backgroundColor(Colors.outlineDark).borderRadius(5.px)
253+
)
254+
}
255+
Spacer(
256+
modifier = Modifier.height(bottomSpacing.px)
257+
)
258+
}
259+
260+
private fun Modifier.blockSelection() = this.custom("user-select: none;")

0 commit comments

Comments
 (0)