Settings.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import React, { Component, Fragment, FC, useEffect, useState } from "react";
  2. import Router from "next/router";
  3. import { bindActionCreators } from "redux";
  4. import { connect } from "react-redux";
  5. import styled from "styled-components";
  6. import cookie from "js-cookie";
  7. import axios from "axios";
  8. import { Flex } from "reflexbox/styled-components";
  9. import SettingsWelcome from "./SettingsWelcome";
  10. import SettingsDomain from "./SettingsDomain";
  11. import SettingsPassword from "./SettingsPassword";
  12. import SettingsBan from "./SettingsBan";
  13. import SettingsApi from "./SettingsApi";
  14. import Modal from "../Modal";
  15. import { fadeIn } from "../../helpers/animations";
  16. import {
  17. deleteCustomDomain,
  18. generateApiKey,
  19. getUserSettings,
  20. setCustomDomain,
  21. showDomainInput,
  22. banUrl
  23. } from "../../actions";
  24. // TODO: types
  25. interface Props {
  26. auth: {
  27. admin: boolean;
  28. isAuthenticated: boolean;
  29. user: string;
  30. };
  31. apiLoading?: boolean;
  32. deleteCustomDomain: any;
  33. domainLoading?: boolean;
  34. banUrl: any;
  35. setCustomDomain: any;
  36. generateApiKey: any;
  37. getUserSettings: any;
  38. settings: {
  39. apikey: string;
  40. customDomain: string;
  41. homepage: string;
  42. domainInput: boolean;
  43. };
  44. showDomainInput: any;
  45. }
  46. const Wrapper = styled(Flex).attrs({
  47. width: 600,
  48. maxWidth: "90%",
  49. flexDirection: "column",
  50. alignItems: "flex-start",
  51. pb: 80
  52. })`
  53. position: relative;
  54. animation: ${fadeIn} 0.8s ease;
  55. hr {
  56. width: 100%;
  57. height: 1px;
  58. outline: none;
  59. border: none;
  60. background-color: #e3e3e3;
  61. margin: 24px 0;
  62. @media only screen and (max-width: 768px) {
  63. margin: 12px 0;
  64. }
  65. }
  66. a {
  67. margin: 32px 0 0;
  68. color: #2196f3;
  69. text-decoration: none;
  70. :hover {
  71. color: #2196f3;
  72. border-bottom: 1px dotted #2196f3;
  73. }
  74. }
  75. `;
  76. const Settings: FC<Props> = props => {
  77. const [modal, setModal] = useState(false);
  78. const [password, setPassword] = useState({ message: "", error: "" });
  79. // useEffect(() => {
  80. // // FIXME: probably should be moved somewhere else
  81. // if (!props.auth.isAuthenticated) {
  82. // Router.push("/login");
  83. // } else {
  84. // props.getUserSettings();
  85. // }
  86. // }, []);
  87. const handleCustomDomain = e => {
  88. e.preventDefault();
  89. if (props.domainLoading) return null;
  90. const customDomain = e.currentTarget.elements.customdomain.value;
  91. const homepage = e.currentTarget.elements.homepage.value;
  92. return props.setCustomDomain({ customDomain, homepage });
  93. };
  94. const showModal = () => {
  95. setModal(true);
  96. };
  97. const closeModal = () => {
  98. setModal(false);
  99. };
  100. const deleteDomain = () => {
  101. showModal();
  102. props.deleteCustomDomain();
  103. };
  104. const changePassword = e => {
  105. e.preventDefault();
  106. const form = e.target;
  107. const password = form.elements.password.value;
  108. if (password.length < 8) {
  109. setPassword(s => ({
  110. ...s,
  111. error: "Password must be at least 8 chars long."
  112. }));
  113. setTimeout(() => {
  114. setPassword(s => ({ ...s, error: "" }));
  115. }, 1500);
  116. return;
  117. }
  118. return axios
  119. .post(
  120. "/api/auth/changepassword",
  121. { password },
  122. { headers: { Authorization: cookie.get("token") } }
  123. )
  124. .then(res => {
  125. setPassword(s => ({ ...s, message: res.data.message }));
  126. setTimeout(() => {
  127. setPassword(s => ({ ...s, message: "" }));
  128. }, 1500);
  129. form.reset();
  130. })
  131. .catch(err => {
  132. setPassword(s => ({ ...s, error: err.response.data.error }));
  133. setTimeout(() => {
  134. setPassword(s => ({ ...s, error: "" }));
  135. }, 1500);
  136. form.reset();
  137. });
  138. };
  139. return (
  140. <Wrapper>
  141. <SettingsWelcome user={props.auth.user} />
  142. <hr />
  143. {props.auth.admin && (
  144. <Fragment>
  145. <SettingsBan />
  146. <hr />
  147. </Fragment>
  148. )}
  149. <SettingsDomain
  150. handleCustomDomain={handleCustomDomain}
  151. loading={props.domainLoading}
  152. settings={props.settings}
  153. showDomainInput={props.showDomainInput}
  154. showModal={showModal}
  155. />
  156. <hr />
  157. <SettingsPassword />
  158. <hr />
  159. <SettingsApi />
  160. <Modal show={modal} close={closeModal} handler={deleteDomain}>
  161. Are you sure do you want to delete the domain?
  162. </Modal>
  163. </Wrapper>
  164. );
  165. };
  166. Settings.defaultProps = {
  167. apiLoading: false,
  168. domainLoading: false
  169. };
  170. const mapStateToProps = ({
  171. auth,
  172. loading: { api: apiLoading, domain: domainLoading },
  173. settings
  174. }) => ({
  175. auth,
  176. apiLoading,
  177. domainLoading,
  178. settings
  179. });
  180. const mapDispatchToProps = dispatch => ({
  181. banUrl: bindActionCreators(banUrl, dispatch),
  182. deleteCustomDomain: bindActionCreators(deleteCustomDomain, dispatch),
  183. setCustomDomain: bindActionCreators(setCustomDomain, dispatch),
  184. generateApiKey: bindActionCreators(generateApiKey, dispatch),
  185. getUserSettings: bindActionCreators(getUserSettings, dispatch),
  186. showDomainInput: bindActionCreators(showDomainInput, dispatch)
  187. });
  188. export default connect(mapStateToProps, mapDispatchToProps)(Settings);