Shortener.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import React, { FC, useState } from 'react';
  2. import { bindActionCreators } from 'redux';
  3. import { connect } from 'react-redux';
  4. import styled from 'styled-components';
  5. import { Flex } from 'reflexbox/styled-components';
  6. import ShortenerResult from './ShortenerResult';
  7. import ShortenerTitle from './ShortenerTitle';
  8. import ShortenerInput from './ShortenerInput';
  9. import {
  10. createShortUrl,
  11. setShortenerFormError,
  12. showShortenerLoading,
  13. } from '../../actions';
  14. import { fadeIn } from '../../helpers/animations';
  15. // TODO: types
  16. interface Props {
  17. isAuthenticated: boolean;
  18. domain: string;
  19. createShortUrl: any;
  20. shortenerError: string;
  21. shortenerLoading: boolean;
  22. setShortenerFormError: any;
  23. showShortenerLoading: any;
  24. url: {
  25. isShortened: boolean;
  26. list: any[];
  27. };
  28. }
  29. const Wrapper = styled(Flex).attrs({
  30. width: 800,
  31. maxWidth: '98%',
  32. flex: '0 0 auto',
  33. flexDirection: 'column',
  34. mt: 16,
  35. mb: 40,
  36. })`
  37. position: relative;
  38. padding-bottom: 125px;
  39. animation: ${fadeIn} 0.8s ease-out;
  40. @media only screen and (max-width: 800px) {
  41. padding: 0 8px 96px;
  42. }
  43. `;
  44. const ResultWrapper = styled(Flex).attrs({
  45. justifyContent: 'center',
  46. alignItems: 'flex-start',
  47. })`
  48. position: relative;
  49. height: 96px;
  50. @media only screen and (max-width: 448px) {
  51. height: 72px;
  52. }
  53. `;
  54. const Shortener: FC<Props> = props => {
  55. const [copied, setCopied] = useState(false);
  56. const copyHandler = () => {
  57. setCopied(true);
  58. setTimeout(() => setCopied(false), 1500);
  59. };
  60. const handleSubmit = e => {
  61. e.preventDefault();
  62. const { isAuthenticated } = props;
  63. props.showShortenerLoading();
  64. const shortenerForm: any = document.getElementById('shortenerform');
  65. const {
  66. target: originalUrl,
  67. customurl: customurlInput,
  68. password: pwd,
  69. } = shortenerForm.elements; // FIXME: types
  70. const target = originalUrl.value.trim();
  71. const customurl = customurlInput && customurlInput.value.trim();
  72. const password = pwd && pwd.value;
  73. const options = isAuthenticated && { customurl, password };
  74. shortenerForm.reset();
  75. if (process.env.NODE_ENV === 'production' && !isAuthenticated) {
  76. // FIXME: types bro
  77. (window as any).grecaptcha.execute((window as any).captchaId);
  78. const getCaptchaToken = () => {
  79. setTimeout(() => {
  80. if ((window as any).isCaptchaReady) {
  81. const reCaptchaToken = (window as any).grecaptcha.getResponse(
  82. (window as any).captchaId
  83. );
  84. (window as any).isCaptchaReady = false;
  85. (window as any).grecaptcha.reset((window as any).captchaId);
  86. return props.createShortUrl({
  87. target,
  88. reCaptchaToken,
  89. ...options,
  90. });
  91. }
  92. return getCaptchaToken();
  93. }, 200);
  94. };
  95. return getCaptchaToken();
  96. }
  97. return props.createShortUrl({ target, ...options });
  98. };
  99. return (
  100. <Wrapper>
  101. <ResultWrapper>
  102. {!props.shortenerError &&
  103. (props.url.isShortened || props.shortenerLoading) ? (
  104. <ShortenerResult
  105. copyHandler={copyHandler}
  106. loading={props.shortenerLoading}
  107. url={props.url}
  108. isCopied={copied}
  109. />
  110. ) : (
  111. <ShortenerTitle />
  112. )}
  113. </ResultWrapper>
  114. <ShortenerInput
  115. isAuthenticated={props.isAuthenticated}
  116. handleSubmit={handleSubmit}
  117. setShortenerFormError={props.setShortenerFormError}
  118. domain={props.domain}
  119. />
  120. </Wrapper>
  121. );
  122. };
  123. // TODO: check if needed
  124. // shouldComponentUpdate(nextProps, nextState) {
  125. // const {
  126. // isAuthenticated,
  127. // domain,
  128. // shortenerError,
  129. // shortenerLoading,
  130. // url: { isShortened },
  131. // } = props;
  132. // return (
  133. // isAuthenticated !== nextProps.isAuthenticated ||
  134. // shortenerError !== nextProps.shortenerError ||
  135. // isShortened !== nextProps.url.isShortened ||
  136. // shortenerLoading !== nextProps.shortenerLoading ||
  137. // domain !== nextProps.domain ||
  138. // state.isCopied !== nextState.isCopied
  139. // );
  140. // }
  141. const mapStateToProps = ({
  142. auth: { isAuthenticated },
  143. error: { shortener: shortenerError },
  144. loading: { shortener: shortenerLoading },
  145. settings: { customDomain: domain },
  146. url,
  147. }) => ({
  148. isAuthenticated,
  149. domain,
  150. shortenerError,
  151. shortenerLoading,
  152. url,
  153. });
  154. const mapDispatchToProps = dispatch => ({
  155. createShortUrl: bindActionCreators(createShortUrl, dispatch),
  156. setShortenerFormError: bindActionCreators(setShortenerFormError, dispatch),
  157. showShortenerLoading: bindActionCreators(showShortenerLoading, dispatch),
  158. });
  159. export default connect(
  160. mapStateToProps,
  161. mapDispatchToProps
  162. )(Shortener);