index.svelte 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <script lang="typescript" context="module">
  2. import type { AppSession, PageData, PreloadContext } from "src/utils/session";
  3. import type { Option } from "@shared/common/async_utils";
  4. import type { MDText } from "./md";
  5. import type { VerifyInfo } from "./verify";
  6. export async function preload(
  7. this: PreloadContext,
  8. { path }: PageData,
  9. session: AppSession
  10. ) {
  11. const mdResult = await this.fetch("/rules/md", { credentials: "include" });
  12. const md = (await mdResult.json()) as Option<MDText, { error: string }>;
  13. const verifyReusult = await this.fetch("/rules/verify", { credentials: "include" });
  14. const verify = (await verifyReusult.json()) as VerifyInfo;
  15. return {
  16. rulesText: md.ok ? md.text : "",
  17. sitekey: verify.captchaSitekey,
  18. verified: verify.userVerified,
  19. needsCaptcha: verify.needsCaptcha
  20. };
  21. }
  22. </script>
  23. <script lang="typescript">
  24. import { Converter } from "showdown";
  25. import Icon from "svelte-awesome/components/Icon.svelte";
  26. import { faSpinner } from "@fortawesome/free-solid-svg-icons";
  27. import { fade } from "svelte/transition";
  28. import type { VerifyRequest } from "./verify";
  29. export let rulesText: string = "";
  30. export let sitekey: string = "";
  31. export let verified!: boolean;
  32. export let needsCaptcha!: boolean;
  33. let showVerify: boolean = false;
  34. const spinner = (faSpinner as unknown) as undefined;
  35. let htmlContent = new Converter().makeHtml(rulesText);
  36. enum State {
  37. None,
  38. Verify,
  39. Error,
  40. Message,
  41. }
  42. let state: State = State.None;
  43. let message: string = "";
  44. async function onVerified(e: { key: string }) {
  45. state = State.Verify;
  46. const result = await fetch("/rules/verify", {
  47. credentials: "include",
  48. method: "post",
  49. headers: {
  50. Accept: "application/json",
  51. "Content-Type": "application/json",
  52. },
  53. body: JSON.stringify({ captchaResponse: e.key } as VerifyRequest),
  54. });
  55. const opt = (await result.json()) as Option<unknown, { error: string }>;
  56. if (!opt.ok) {
  57. state = State.Error;
  58. message = opt.error;
  59. } else {
  60. state = State.Message;
  61. message = "Verification done! Welcome to the server!";
  62. }
  63. }
  64. function onExpired() {
  65. state = State.Error;
  66. message = "Captcha expired, please solve it again";
  67. }
  68. function onError(error: string) {
  69. state = State.Error;
  70. message = `${error}; Please try refreshing the page.`;
  71. }
  72. </script>
  73. <style>
  74. @import "./markdown-dark.css";
  75. .message {
  76. @apply text-white;
  77. }
  78. .error {
  79. @apply text-red-600;
  80. }
  81. .verify-button {
  82. @apply text-gray-200 font-bold;
  83. }
  84. </style>
  85. <svelte:head>
  86. <title>Rules</title>
  87. </svelte:head>
  88. <div class="viewport md-dark md-body">
  89. <div>
  90. {@html htmlContent}
  91. </div>
  92. {#if !verified}
  93. <div class="flex flex-col flex-wrap items-center">
  94. <form class="py-4">
  95. <input type="checkbox" id="show-verify" bind:checked={showVerify} disabled={state != State.None} />
  96. <label for="show-verify">I understand these rules and agree to abide to
  97. them</label>
  98. </form>
  99. {#if showVerify}
  100. {#if needsCaptcha}
  101. <h-captcha site-key={sitekey} dark on:verified={onVerified} on:expired={onExpired} on:error={onError} />
  102. {:else}
  103. <button class="verify-button" on:click={() => void onVerified({ key: "" })}>Verify</button>
  104. {/if}
  105. {/if}
  106. <div class="py-2 pointer-events-none select-none">
  107. {#if state == State.Verify}
  108. <span transition:fade={{ duration: 50 }}><Icon data={spinner} spin />
  109. Verifying</span>
  110. {:else if state == State.Error || state == State.Message}
  111. <span
  112. class:message={state == State.Message}
  113. class:error={state == State.Error}>{message}</span>
  114. {/if}
  115. </div>
  116. </div>
  117. {/if}
  118. </div>