auth.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import styles from "./auth.module.scss";
  2. import { IconButton } from "./button";
  3. import { useState, useEffect } from "react";
  4. import { useNavigate } from "react-router-dom";
  5. import { Path, SAAS_CHAT_URL } from "../constant";
  6. import { useAccessStore } from "../store";
  7. import Locale from "../locales";
  8. import Delete from "../icons/close.svg";
  9. import Arrow from "../icons/arrow.svg";
  10. import Logo from "../icons/logo.svg";
  11. import { useMobileScreen } from "@/app/utils";
  12. import BotIcon from "../icons/bot.svg";
  13. import { getClientConfig } from "../config/client";
  14. import LeftIcon from "@/app/icons/left.svg";
  15. import { safeLocalStorage } from "@/app/utils";
  16. import {
  17. trackSettingsPageGuideToCPaymentClick,
  18. trackAuthorizationPageButtonToCPaymentClick,
  19. } from "../utils/auth-settings-events";
  20. const storage = safeLocalStorage();
  21. export function AuthPage() {
  22. const navigate = useNavigate();
  23. const accessStore = useAccessStore();
  24. const goHome = () => navigate(Path.Home);
  25. const goChat = () => navigate(Path.Chat);
  26. const goSaas = () => {
  27. trackAuthorizationPageButtonToCPaymentClick();
  28. window.location.href = SAAS_CHAT_URL;
  29. };
  30. const resetAccessCode = () => {
  31. accessStore.update((access) => {
  32. access.openaiApiKey = "";
  33. access.accessCode = "";
  34. });
  35. }; // Reset access code to empty string
  36. useEffect(() => {
  37. if (getClientConfig()?.isApp) {
  38. navigate(Path.Settings);
  39. }
  40. // eslint-disable-next-line react-hooks/exhaustive-deps
  41. }, []);
  42. return (
  43. <div className={styles["auth-page"]}>
  44. <TopBanner></TopBanner>
  45. <div className={styles["auth-header"]}>
  46. <IconButton
  47. icon={<LeftIcon />}
  48. text={Locale.Auth.Return}
  49. onClick={() => navigate(Path.Home)}
  50. ></IconButton>
  51. </div>
  52. <div className={`no-dark ${styles["auth-logo"]}`}>
  53. <BotIcon />
  54. </div>
  55. <div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
  56. <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
  57. <input
  58. className={styles["auth-input"]}
  59. type="password"
  60. placeholder={Locale.Auth.Input}
  61. value={accessStore.accessCode}
  62. onChange={(e) => {
  63. accessStore.update(
  64. (access) => (access.accessCode = e.currentTarget.value),
  65. );
  66. }}
  67. />
  68. {!accessStore.hideUserApiKey ? (
  69. <>
  70. <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
  71. <input
  72. className={styles["auth-input"]}
  73. type="password"
  74. placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
  75. value={accessStore.openaiApiKey}
  76. onChange={(e) => {
  77. accessStore.update(
  78. (access) => (access.openaiApiKey = e.currentTarget.value),
  79. );
  80. }}
  81. />
  82. <input
  83. className={styles["auth-input-second"]}
  84. type="password"
  85. placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
  86. value={accessStore.googleApiKey}
  87. onChange={(e) => {
  88. accessStore.update(
  89. (access) => (access.googleApiKey = e.currentTarget.value),
  90. );
  91. }}
  92. />
  93. </>
  94. ) : null}
  95. <div className={styles["auth-actions"]}>
  96. <IconButton
  97. text={Locale.Auth.Confirm}
  98. type="primary"
  99. onClick={goChat}
  100. />
  101. <IconButton
  102. text={Locale.Auth.SaasTips}
  103. onClick={() => {
  104. goSaas();
  105. }}
  106. />
  107. </div>
  108. </div>
  109. );
  110. }
  111. function TopBanner() {
  112. const [isHovered, setIsHovered] = useState(false);
  113. const [isVisible, setIsVisible] = useState(true);
  114. const isMobile = useMobileScreen();
  115. useEffect(() => {
  116. // 检查 localStorage 中是否有标记
  117. const bannerDismissed = storage.getItem("bannerDismissed");
  118. // 如果标记不存在,存储默认值并显示横幅
  119. if (!bannerDismissed) {
  120. storage.setItem("bannerDismissed", "false");
  121. setIsVisible(true); // 显示横幅
  122. } else if (bannerDismissed === "true") {
  123. // 如果标记为 "true",则隐藏横幅
  124. setIsVisible(false);
  125. }
  126. }, []);
  127. const handleMouseEnter = () => {
  128. setIsHovered(true);
  129. };
  130. const handleMouseLeave = () => {
  131. setIsHovered(false);
  132. };
  133. const handleClose = () => {
  134. setIsVisible(false);
  135. storage.setItem("bannerDismissed", "true");
  136. };
  137. if (!isVisible) {
  138. return null;
  139. }
  140. return (
  141. <div
  142. className={styles["top-banner"]}
  143. onMouseEnter={handleMouseEnter}
  144. onMouseLeave={handleMouseLeave}
  145. >
  146. <div className={`${styles["top-banner-inner"]} no-dark`}>
  147. <Logo className={styles["top-banner-logo"]}></Logo>
  148. <span>
  149. {Locale.Auth.TopTips}
  150. <a
  151. href={SAAS_CHAT_URL}
  152. rel="stylesheet"
  153. onClick={() => {
  154. trackSettingsPageGuideToCPaymentClick();
  155. }}
  156. >
  157. {Locale.Settings.Access.SaasStart.ChatNow}
  158. <Arrow style={{ marginLeft: "4px" }} />
  159. </a>
  160. </span>
  161. </div>
  162. {(isHovered || isMobile) && (
  163. <Delete className={styles["top-banner-close"]} onClick={handleClose} />
  164. )}
  165. </div>
  166. );
  167. }