view.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. from dataclasses import dataclass
  2. from datetime import datetime
  3. from typing import Any, Optional, List
  4. import lc.config as c
  5. class View:
  6. pass
  7. @dataclass
  8. class Pagination(View):
  9. current: int
  10. last: int
  11. def previous(self) -> Optional[dict]:
  12. if self.current > 1:
  13. return {"page": self.current - 1}
  14. return None
  15. def next(self) -> Optional[dict]:
  16. if self.current < self.last:
  17. return {"page": self.current + 1}
  18. return None
  19. @classmethod
  20. def from_total(cls, current, total) -> "Pagination":
  21. return cls(
  22. current=current,
  23. last=((total - 1) // c.app.per_page) + 1,
  24. )
  25. @dataclass
  26. class UserInvite(View):
  27. claimed: bool
  28. claimant: str
  29. token: str
  30. @dataclass
  31. class AddUser(View):
  32. token: str
  33. @dataclass
  34. class AdminPane(View):
  35. invites: List[UserInvite]
  36. @dataclass
  37. class Config(View):
  38. username: str
  39. admin_pane: Optional[AdminPane]
  40. msg: Optional[int] = None
  41. def bookmarklet_link(self):
  42. return (
  43. "javascript:(function(){window.open(`"
  44. + c.app.config.app_path
  45. + "/u/"
  46. + self.username
  47. + "/l?name=${document.title}&url=${document.URL}`);})();"
  48. )
  49. def message(self) -> Optional[str]:
  50. if self.msg == 1:
  51. return "Password changed."
  52. elif self.msg == 2:
  53. return "Mismatched new passwords; please try again."
  54. elif self.msg == 3:
  55. return "Incorrect old password; please try again."
  56. return None
  57. @dataclass
  58. class Tag(View):
  59. url: str
  60. name: str
  61. @dataclass
  62. class HierTagList:
  63. user: str
  64. tags: List[Tag]
  65. def render(self) -> str:
  66. groups: dict = {}
  67. for tag in (t.name for t in self.tags):
  68. if "/" not in tag:
  69. groups[tag] = groups.get(tag, {})
  70. else:
  71. chunks = tag.split("/")
  72. focus = groups[chunks[0]] = groups.get(chunks[0], {})
  73. for chunk in chunks[1:]:
  74. focus[chunk] = focus = focus.get(chunk, {})
  75. return "\n".join(self.render_html(k, v) for k, v in groups.items())
  76. def render_html(self, prefix: str, values: dict) -> str:
  77. link = self._render_html(prefix, values, [])
  78. return f'<span class="tag">{link}</span>'
  79. def _href(self, tag: str, init: List[str]) -> str:
  80. link = "/".join(init + [tag])
  81. return f'<a href="/u/{self.user}/t/{link}">{tag}</a>'
  82. def _render_html(self, prefix: str, values: dict, init: List[str]) -> str:
  83. if not values:
  84. return self._href(prefix, init)
  85. if len(values) == 1:
  86. k, v = values.popitem()
  87. rest = self._render_html(k, v, init + [prefix])
  88. prefix_href = self._href(prefix, init)
  89. return f"{prefix_href}/{rest}"
  90. else:
  91. fragments = []
  92. for k, v in values.items():
  93. fragments.append(self._render_html(k, v, init + [prefix]))
  94. items = ", ".join(fragments)
  95. prefix_href = self._href(prefix, init)
  96. return f"{prefix_href}/{{{items}}}"
  97. @dataclass
  98. class Link(View):
  99. id: int
  100. url: str
  101. name: str
  102. description: str
  103. private: bool
  104. tags: List[Tag]
  105. created: datetime
  106. is_mine: bool
  107. link_url: str
  108. user: str
  109. def hier_tags(self) -> str:
  110. return HierTagList(user=self.user, tags=self.tags).render()
  111. @dataclass
  112. class LinkList(View):
  113. links: List[Any]
  114. tags: List[Tag]
  115. user: str
  116. pages: Optional[Pagination] = None
  117. def hier_tags(self) -> str:
  118. return HierTagList(user=self.user, tags=self.tags).render()
  119. @dataclass
  120. class SingleLink(View):
  121. link: Any
  122. all_tags: List[Tag]
  123. @dataclass
  124. class Message(View):
  125. title: str
  126. message: str
  127. @dataclass
  128. class AddLinkDefaults(View):
  129. user: str
  130. all_tags: List[Tag]
  131. url: Optional[str] = None
  132. name: Optional[str] = None
  133. def post_url(self) -> str:
  134. return f"/u/{self.user}/l"
  135. @dataclass
  136. class Page(View):
  137. title: str
  138. content: str
  139. user: Optional[Any]
  140. @dataclass
  141. class Error(View):
  142. code: int
  143. message: str