简化版的mastodon web client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
12 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  6. <link rel="icon" type="image/png" href="/img/ord/icon-128.png" />
  7. <meta property="og:title" content="闭社简化版" />
  8. <meta property="og:description" content="一个提供更简洁界面的闭社web client" />
  9. <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
  10. <title>闭社简化版</title>
  11. <style>
  12. body {
  13. background: linear-gradient(-45deg, #fff calc(50% - 1px), #d0d0d0 calc(50%), #fff calc(50% + 1px) );
  14. background-size: 6px 5px;
  15. font-family: 'Noto Sans SC', sans-serif;
  16. }
  17. body.grey {
  18. background: #707070;
  19. background-size: 6px 5px;
  20. }
  21. body,
  22. pre {
  23. font-family: 'Noto Sans SC', sans-serif;
  24. }
  25. h1,
  26. h2,
  27. h3,
  28. h4,
  29. h5,
  30. h6 {
  31. font-family: 'Noto Serif SC', serif;
  32. font-weight: 300;
  33. }
  34. a,
  35. a:hover,
  36. .btn-link,
  37. .btn-link:hover {
  38. color: inherit;
  39. text-decoration: underline;
  40. display: inline-block;
  41. }
  42. .part1 {
  43. min-width: 300px;
  44. float: left;
  45. padding-right: 10px;
  46. position: relative;
  47. }
  48. .part2 {
  49. min-width: 200px;
  50. overflow: hidden;
  51. padding-left: 25px;
  52. }
  53. .qbox {
  54. border: 2px black solid;
  55. background: white;
  56. padding: 5px;
  57. color: black;
  58. margin: 5px 5px 20px;
  59. }
  60. body.grey #statuses-list .qbox {
  61. background: #b0b0b0;
  62. }
  63. .new .qbox {
  64. background: black;
  65. color: white;
  66. }
  67. .new .qbox textarea {
  68. background: black;
  69. color: white;
  70. }
  71. .qbox textarea {
  72. border: none;
  73. border-bottom: 1px solid;
  74. border-radius: 0;
  75. }
  76. body.grey #statuses-list .qbox textarea {
  77. background: #b0b0b0;
  78. }
  79. .qbox .content {
  80. margin: 15px 5px;
  81. }
  82. .liked, .reblogged {
  83. font-weight: bold;
  84. }
  85. .timeago {
  86. font-size: 0.5em;
  87. }
  88. .display_name {
  89. margin: 0;
  90. }
  91. .behind {
  92. z-index: 98;
  93. cursor: pointer;
  94. transform: translateY(5px) scale(0.98);
  95. transform-origin: top;
  96. transition-property: transform;
  97. transition-duration: 0.5s;
  98. }
  99. .front {
  100. z-index: 99;
  101. transition-property: transform;
  102. transition-duration: 0.5s;
  103. }
  104. .judge {
  105. position: absolute;
  106. top: 0;
  107. right: 0;
  108. margin: 0 0 30px 20px;
  109. width: 90%;
  110. }
  111. .new {
  112. position: relative;
  113. margin: 30px 20px 30px 0;
  114. }
  115. a.hashtag {
  116. font-weight: bold;
  117. }
  118. .comment-list-wrapper {
  119. background: #3333;
  120. padding-left: 7%;
  121. font-size: 90%;
  122. }
  123. .status-media {
  124. max-height: 500px;
  125. max-width: 100%;
  126. margin: 10px auto;
  127. }
  128. #statuses-list .invisible {
  129. font-size: 0;
  130. line-height: 0;
  131. display: inline-block;
  132. width: 0;
  133. height: 0;
  134. visibility: visible !important;
  135. }
  136. #statuses-list .ellipsis::after {
  137. content: "...";
  138. }
  139. </style>
  140. </head>
  141. <body>
  142. <div class="container" style="overflow: hidden;min-height: 100vh">
  143. <div style='padding:15px'>
  144. <h1> 闭社简化版 </h1>
  145. </div>
  146. <div class="part1">
  147. <div class="new">
  148. <form action="" onsubmit="return post_status(event, '')">
  149. <div class="form-group qbox">
  150. <textarea class="form-control" name="text" rows="5" maxlength="5000" placeholder="啥?" required="required"></textarea>
  151. <div class="form-check mt-3 mb-3">
  152. <input class="form-check-input" type="checkbox" value="" id="post-checkbox-an">
  153. <label class="form-check-label" for="post-checkbox-an">
  154. 匿名
  155. </label>
  156. </div>
  157. <button type="submit" class="btn btn-link btn-lg">发布</button>
  158. </div>
  159. </form>
  160. </div>
  161. </div>
  162. <div class="part2" id="part2">
  163. <h1>本站</h1>
  164. <button class="btn btn-link grey_theme">切换亮度</button>
  165. <div id="statuses-list">
  166. </div>
  167. <span id="loading-span">加载中..</span>
  168. </div>
  169. </div>
  170. </body>
  171. <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  172. <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
  173. <script src="https://cdn.bootcdn.net/ajax/libs/jquery-timeago/1.6.7/jquery.timeago.min.js"></script>
  174. <script src="https://cdn.bootcdn.net/ajax/libs/jquery-timeago/1.6.7/locales/jquery.timeago.zh-CN.js"></script>
  175. <script>
  176. var token;
  177. var max_id;
  178. var loading_statues;
  179. var base_api_url = "https://thu.closed.social/";
  180. function get_token() {
  181. (document.cookie || "").split("; ").forEach( (c) => {
  182. let cc = c.split("=");
  183. if (cc.shift() === "mast_token") {
  184. token = cc.join("=");
  185. }
  186. });
  187. }
  188. function get_more_statuses() {
  189. loading_statues = true;
  190. $.getJSON(
  191. `${base_api_url}api/v1/timelines/public?limit=20&local=true` + (
  192. max_id ? `&max_id=${max_id}` : ''
  193. ),
  194. function (data) {
  195. data.forEach((status) => {
  196. $('#statuses-list').append(make_status_box_html(status, true));
  197. });
  198. $('.timeago').timeago();
  199. if (data.length) {
  200. max_id = data[data.length - 1].id;
  201. loading_statues = false;
  202. }
  203. }
  204. ).fail((e) => {
  205. alert(e.responseText);
  206. });
  207. }
  208. function make_status_box_html(status) {
  209. return (`
  210. <div class="qbox" id="status-${status.id.toString()}">
  211. <img class="avatar" width="24" src="${status.account.avatar}">
  212. <small>
  213. ${status.account.acct === "mask_bot" ?
  214. "匿名用户" + /^<p>(\[[^\]]*\]):/.exec(status.content)[1] : (
  215. status.account.display_name + ' @' +status.account.acct)}
  216. </small>
  217. ${status.reblog && (status = status.reblog) &&
  218. `<small> 转发 @${status.account.acct}</small>` || ''}
  219. <div class="content">
  220. ${status.account.acct === "mask_bot" ?
  221. status.content.replace(/^<p>\[([^\]]*)\]:<br \/>/, '<p>') : status.content}
  222. </div>
  223. ${status.media_attachments.map((media) => {
  224. switch (media.type) {
  225. case 'image':
  226. return `<image class="status-media" src=${media.url}>`;
  227. case 'video':
  228. return `<video class="status-media" src=${media.url} controls></video>`;
  229. default:
  230. return '';
  231. }
  232. }).join('\n')}
  233. <div style="text-align:right;margin: 0px 0 -5px">
  234. <time class="timeago mr-1" datetime="${status.created_at}"
  235. title="${status.created_at}"></time>
  236. <small class="mr-2">${status.application && status.application.name || ''}</small>
  237. <a href="###" class="mr-3 like-btn ${status.favourited? 'liked' : ''}"
  238. onclick="like_status(event, '${status.id.toString()}')">
  239. 点赞<span class="num">${status.favourites_count}</span>
  240. </a>
  241. <a href="###" class="mr-3 reblog-btn ${status.reblogged? 'reblogged' : ''}"
  242. onclick="reblog_status(event, '${status.id.toString()}')">
  243. 转发<span class="num">${status.reblogs_count}</span>
  244. </a>
  245. <a class="mr-3 reply-btn" href="###"
  246. onclick="get_comments('${status.id.toString()}')">
  247. 回复${status.replies_count}
  248. </a>
  249. </div>
  250. <div class="collapse comment-list-wrapper" id="collapse-${status.id.toString()}">
  251. <div class="comment-list">
  252. </div>
  253. <form action="" onsubmit="return post_status(event, '${status.id}')">
  254. <div class="form-group qbox">
  255. <textarea class="form-control" rows="2" maxlength="5000" required="required" >@${status.account.acct} </textarea>
  256. <div class="form-check mt-1 mb-1">
  257. <input class="form-check-input" type="checkbox" value="" id="reply-${status.id}-checkbox-an">
  258. <label class="form-check-label" for="reply-${status.id}-checkbox-an">
  259. 匿名
  260. </label>
  261. </div>
  262. <button type="submit" class="btn btn-link">添加回复</button>
  263. </div>
  264. </form>
  265. </div>
  266. </div>
  267. `)
  268. }
  269. function get_comments(sid) {
  270. let coll = $(`#collapse-${sid}`);
  271. if (coll.hasClass('show')) {
  272. coll.collapse('hide');
  273. } else {
  274. coll.collapse('show');
  275. coll.find('> .comment-list').append("加载中..");
  276. $.getJSON(
  277. `${base_api_url}api/v1/statuses/${sid}/context`,
  278. function (data) {
  279. coll.find('> .comment-list').empty();
  280. data.descendants.forEach( (rp) => {
  281. let parent_coll = $(`#collapse-${rp.in_reply_to_id}`);
  282. parent_coll.collapse('show');
  283. parent_coll.find('> .comment-list').append(make_status_box_html(rp));
  284. });
  285. $('.timeago').timeago();
  286. }
  287. );
  288. }
  289. }
  290. function like_status(e, sid) {
  291. let target = $(e.target);
  292. if (!target.hasClass('liked')) {
  293. $.post(
  294. `${base_api_url}api/v1/statuses/${sid}/favourite`,
  295. (status) => {
  296. target.addClass('liked');
  297. target.find('.num').text(status.favourites_count)
  298. }
  299. );
  300. } else {
  301. $.post(
  302. `${base_api_url}api/v1/statuses/${sid}/unfavourite`,
  303. (status) => {
  304. target.removeClass('liked');
  305. target.find('.num').text(
  306. target.find('.num').text() - 1
  307. )
  308. }
  309. );
  310. }
  311. }
  312. function reblog_status(e, sid) {
  313. let target = $(e.target);
  314. if (!target.hasClass('reblogged')) {
  315. $.post(
  316. `${base_api_url}api/v1/statuses/${sid}/reblog`,
  317. (status) => {
  318. target.addClass('reblogged');
  319. target.find('.num').text(
  320. target.find('.num').text() - (-1)
  321. )
  322. }
  323. );
  324. } else {
  325. $.post(
  326. `${base_api_url}api/v1/statuses/${sid}/unreblog`,
  327. (status) => {
  328. target.removeClass('reblogged');
  329. target.find('.num').text(
  330. target.find('.num').text() - 1
  331. )
  332. }
  333. );
  334. }
  335. }
  336. function post_status(e, sid) {
  337. e.preventDefault();
  338. let form = $(e.target);
  339. let text = form.find('textarea').val();
  340. if (form.find('input[type=checkbox]').is(':checked')) {
  341. text += "\n匿了";
  342. }
  343. $.post(
  344. `${base_api_url}api/v1/statuses`,
  345. {'status': text, 'in_reply_to_id': sid || null, 'visibility': 'public'},
  346. (status) => {
  347. form.find('textarea').val('');
  348. if (sid) {
  349. form.prev().append(make_status_box_html(status));
  350. } else {
  351. $('#statuses-list').prepend(make_status_box_html(status));
  352. }
  353. },
  354. 'json'
  355. ).fail((e) => {
  356. alert(e.responseText);
  357. });
  358. return false;
  359. }
  360. $(document).ready(function(){
  361. get_token();
  362. console.log(token);
  363. if (!token) {
  364. location.href = `${base_api_url}oauth/authorize?client_id=Wjf6ajif5kl6rIIt_TLu7SAAluGskaQiTXZoIr44jUc&response_type=code&redirect_uri=${encodeURIComponent(location.origin + "/auth")}&scope=read+write&force_login=False`
  365. } else {
  366. $.ajaxSetup({
  367. headers : {
  368. 'Authorization' : 'Bearer ' + token
  369. }
  370. });
  371. get_more_statuses();
  372. $(window).scroll(() => {
  373. if($('#loading-span').offset().top < $(window).scrollTop() + $(window).innerHeight() * 1.5 && !loading_statues) {
  374. get_more_statuses();
  375. }
  376. });
  377. }
  378. $('.grey_theme').click((e) => {
  379. $('body').toggleClass("grey");
  380. });
  381. });
  382. </script>
  383. </html>