diff --git a/.babelrc b/.babelrc deleted file mode 100644 index b4f37e542..000000000 --- a/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": [ - [ - "styled-components", - { "ssr": true, "displayName": true, "preprocess": false } - ], - "inline-react-svg" - ] -} diff --git a/.docker.env b/.docker.env index a714b3486..00c318567 100644 --- a/.docker.env +++ b/.docker.env @@ -18,12 +18,6 @@ DB_USER= DB_PASSWORD= DB_SSL=false -# ONLY NEEDED FOR MIGRATION !!1! -# Neo4j database credential details -NEO4J_DB_URI=bolt://localhost -NEO4J_DB_USERNAME=neo4j -NEO4J_DB_PASSWORD=pass - # Redis host and port REDIS_HOST=redis REDIS_PORT=6379 @@ -65,15 +59,6 @@ RECAPTCHA_SECRET_KEY= # Get it from https://developers.google.com/safe-browsing/v4/get-started GOOGLE_SAFE_BROWSING_KEY= -# Google Analytics tracking ID for universal analytics. -# Example: UA-XXXX-XX -GOOGLE_ANALYTICS= -GOOGLE_ANALYTICS_UNIVERSAL= - -# Google Analytics tracking ID for universal analytics -# This one is used for links -# GOOGLE_ANALYTICS_UNIVERSAL= - # Your email host details to use to send verification emails. # More info on http://nodemailer.com/ # Mail from example "Kutt ". Leave empty to use MAIL_USER diff --git a/.eslintrc b/.eslintrc index ec3abc974..fb86bb317 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,40 +1,22 @@ { "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:prettier/recommended" + "next/core-web-vitals", + "plugin:@typescript-eslint/recommended", + "prettier" ], - "parser": "@typescript-eslint/parser", "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", "project": ["./tsconfig.json", "./client/tsconfig.json"] }, - "plugins": ["@typescript-eslint"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { - "eqeqeq": ["warn", "always", { "null": "ignore" }], - "no-useless-return": "warn", - "no-var": "warn", - "no-console": "warn", - "no-unused-vars": "off", - "max-len": ["warn", { "comments": 80 }], - "no-param-reassign": 0, - "require-atomic-updates": 0, - "@typescript-eslint/interface-name-prefix": "off", - "@typescript-eslint/no-unused-vars": "off", // "warn" for production - "@typescript-eslint/no-explicit-any": "off", // "warn" for production - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/camelcase": "off", - "@typescript-eslint/no-object-literal-type-assertion": "off", - "@typescript-eslint/no-parameter-properties": "off", - "@typescript-eslint/explicit-function-return-type": "off" + "@typescript-eslint/no-explicit-any": ["off"] }, "env": { "es6": true, "browser": true, - "node": true, - "mocha": true - }, - "globals": { - "assert": true + "node": true }, "settings": { "react": { diff --git a/.example.env b/.example.env index 5fd7047ae..f739887d0 100644 --- a/.example.env +++ b/.example.env @@ -18,12 +18,6 @@ DB_USER= DB_PASSWORD= DB_SSL=false -# ONLY NEEDED FOR MIGRATION !!1! -# Neo4j database credential details -NEO4J_DB_URI=bolt://localhost -NEO4J_DB_USERNAME=neo4j -NEO4J_DB_PASSWORD=pass - # Redis host and port REDIS_HOST=127.0.0.1 REDIS_PORT=6379 @@ -68,15 +62,6 @@ RECAPTCHA_SECRET_KEY= # Get it from https://developers.google.com/safe-browsing/v4/get-started GOOGLE_SAFE_BROWSING_KEY= -# Google Analytics tracking ID for universal analytics. -# Example: UA-XXXX-XX -GOOGLE_ANALYTICS= -GOOGLE_ANALYTICS_UNIVERSAL= - -# Google Analytics tracking ID for universal analytics -# This one is used for links -# GOOGLE_ANALYTICS_UNIVERSAL= - # Your email host details to use to send verification emails. # More info on http://nodemailer.com/ # Mail from example "Kutt ". Leave empty to use MAIL_USER diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..a0a990b74 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run lint:nofix diff --git a/MIGRATION.md b/MIGRATION.md deleted file mode 100644 index 2723ec9d6..000000000 --- a/MIGRATION.md +++ /dev/null @@ -1,44 +0,0 @@ -# Migrate database from Neo4j to Postgres - -As explained in issue #197, Kutt is ditching Neo4j in favor of Postgres in version 2. But what happens to old data? Well, I have created migration scripts that you can use to transfer data from your Neo4j database to your new Postgres database. - -### 🚧 IMPORTANT: v2 is still in beta (but somehow more stable than v1) - -## General recommendations - -- Importing Neo4j data into local Neo4j database and migrate from there would speed things up. -- Use local Postgres database (where app lives), because using a remote database server will be way slower. If you're doing this locally, you can import data from local database to the remote one after migration has finished. I used this command to move data: - -## 1. Set up a Postgres database - -Set up a Postgres database, either on your own server or using a SaaS service. - -## 2. Pull and run Kutt's new version - -Right now version 2 is in beta. Therefore, pull from `develop` branch and create and fill the `.env` file based on `.example.env`. - -**NOTE**: Run the app at least once and let it create and initialize tables in the database. You just need to do `npm run dev` and wait for it to create tables. Then check your database to make sure tables have been created. (If your production database is separate, you need to initialize it too). - -## 3. Migrate data using scripts - -First, do `npm run build` to build the files. Now if you check `production-server/migration` folder you will fine 4 files. You can now run these scripts one by one. - -**NOTE:** that the order of running the scripts is important. - -**NOTE:** Step 4 is going to take a good chunk of time. - -**NOTE:** If step 4 fails at any stage, you should delete links and visits data from the database and try again. - -``` -// 1. Migrate data: Hosts -node production-server/migration/01_hosts.js - -// 2. Migrate data: Users -node production-server/migration/02_users.js - -// 3. Migrate data: Domains -node production-server/migration/03_domains.js - -// 4. Migrate data: Links -node production-server/migration/04_links.js -``` diff --git a/README.md b/README.md index ccc9310d0..f7bec4023 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,6 @@ _Contributions and bug reports are welcome._ [![GitHub license](https://img.shields.io/github/license/thedevs-network/kutt.svg)](https://github.com/thedevs-network/kutt/blob/develop/LICENSE) [![Twitter](https://img.shields.io/twitter/url/https/github.com/thedevs-network/kutt/.svg?style=social)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fthedevs-network%2Fkutt%2F) -## Migration from v1 - -The new version of Kutt is here. In version 2, we used TypeScript and we moved from Neo4j to PostgreSQL database in favor of performance and we're working on adding new features. - -If you're coming from v1, refer to [MIGRATION.md](MIGRATION.md) to migrate data from Neo4j to PostgreSQL. - ## Table of Contents - [Key Features](#key-features) diff --git a/client/components/ALink.tsx b/client/components/ALink.tsx index d9b5f216d..9b3d2eba1 100644 --- a/client/components/ALink.tsx +++ b/client/components/ALink.tsx @@ -1,6 +1,8 @@ -import { Box, BoxProps } from "reflexbox/styled-components"; +import { FC } from "react"; +import { Box, BoxProps } from "rebass/styled-components"; import styled, { css } from "styled-components"; import { ifProp } from "styled-tools"; +import Link from "next/link"; interface Props extends BoxProps { href?: string; @@ -8,10 +10,9 @@ interface Props extends BoxProps { target?: string; rel?: string; forButton?: boolean; + isNextLink?: boolean; } -const ALink = styled(Box).attrs({ - as: "a" -})` +const StyledBox = styled(Box)` cursor: pointer; color: #2196f3; border-bottom: 1px dotted transparent; @@ -28,6 +29,20 @@ const ALink = styled(Box).attrs({ )} `; +export const ALink: FC = (props) => { + if (props.isNextLink) { + const { href, target, title, rel, ...rest } = props; + return ( + + + + ); + } + return ; +}; + +ALink.displayName = "ALink"; + ALink.defaultProps = { pb: "1px", forButton: false diff --git a/client/components/Animation.ts b/client/components/Animation.ts index 29e0895c6..87a2dd20b 100644 --- a/client/components/Animation.ts +++ b/client/components/Animation.ts @@ -1,5 +1,5 @@ import { fadeInVertical } from "../helpers/animations"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import styled from "styled-components"; import { prop } from "styled-tools"; import { FC } from "react"; @@ -10,7 +10,7 @@ interface Props extends React.ComponentProps { } const Animation: FC = styled(Flex)` - animation: ${props => fadeInVertical(props.offset)} + animation: ${(props) => fadeInVertical(props.offset)} ${prop("duration", "0.3s")} ease-out; `; diff --git a/client/components/AppWrapper.tsx b/client/components/AppWrapper.tsx index 7da4c390d..4ce6d6dce 100644 --- a/client/components/AppWrapper.tsx +++ b/client/components/AppWrapper.tsx @@ -1,7 +1,6 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import React, { useEffect } from "react"; import styled from "styled-components"; -import Router from "next/router"; import { useStoreState, useStoreActions } from "../store"; import PageLoading from "./PageLoading"; @@ -22,11 +21,11 @@ const Wrapper = styled(Flex)` `; const AppWrapper = ({ children }: { children: any }) => { - const isAuthenticated = useStoreState(s => s.auth.isAuthenticated); - const logout = useStoreActions(s => s.auth.logout); - const fetched = useStoreState(s => s.settings.fetched); - const loading = useStoreState(s => s.loading.loading); - const getSettings = useStoreActions(s => s.settings.getSettings); + const isAuthenticated = useStoreState((s) => s.auth.isAuthenticated); + const logout = useStoreActions((s) => s.auth.logout); + const fetched = useStoreState((s) => s.settings.fetched); + const loading = useStoreState((s) => s.loading.loading); + const getSettings = useStoreActions((s) => s.settings.getSettings); const isVerifyEmailPage = typeof window !== "undefined" && @@ -36,7 +35,7 @@ const AppWrapper = ({ children }: { children: any }) => { if (isAuthenticated && !fetched && !isVerifyEmailPage) { getSettings().catch(() => logout()); } - }, [isVerifyEmailPage]); + }, [isAuthenticated, fetched, isVerifyEmailPage, getSettings, logout]); return ( props.color || "#333"}; + fill: ${(props) => props.color || "#333"}; @media only screen and (max-width: 768px) { width: 13px; diff --git a/client/components/Features.tsx b/client/components/Features.tsx index 2a6d450ec..fe13697bd 100644 --- a/client/components/Features.tsx +++ b/client/components/Features.tsx @@ -1,11 +1,10 @@ import React from "react"; -import styled from "styled-components"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import FeaturesItem from "./FeaturesItem"; import { ColCenterH } from "./Layout"; import { Colors } from "../consts"; -import Text, { H3 } from "./Text"; +import { H3 } from "./Text"; const Features = () => ( { - const { isAuthenticated } = useStoreState(s => s.auth); + const { isAuthenticated } = useStoreState((s) => s.auth); useEffect(() => { showRecaptcha(); @@ -27,7 +27,7 @@ const Footer: FC = () => { {!isAuthenticated && } Made with love by{" "} - + The Devs .{" | "} @@ -39,11 +39,11 @@ const Footer: FC = () => { GitHub {" | "} - + Terms of Service {" | "} - + Report Abuse {publicRuntimeConfig.CONTACT_EMAIL && ( diff --git a/client/components/Header.tsx b/client/components/Header.tsx index 2d4fb4417..22dde75f5 100644 --- a/client/components/Header.tsx +++ b/client/components/Header.tsx @@ -1,9 +1,9 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import getConfig from "next/config"; import React, { FC } from "react"; import Router from "next/router"; import useMedia from "use-media"; -import Link from "next/link"; +import Image from "next/image"; import { DISALLOW_REGISTRATION } from "../consts"; import { useStoreState } from "../store"; @@ -35,6 +35,7 @@ const LogoImage = styled.div` text-decoration: none; color: inherit; transition: border-color 0.2s ease-out; + padding: 0; } @media only screen and (max-width: 488px) { @@ -43,47 +44,41 @@ const LogoImage = styled.div` } } - img { - width: 18px; - margin-right: 11px; + span { + margin-right: 10px !important; } `; const Header: FC = () => { - const { isAuthenticated } = useStoreState(s => s.auth); + const { isAuthenticated } = useStoreState((s) => s.auth); const isMobile = useMedia({ maxWidth: 640 }); const login = !isAuthenticated && (
  • - - - - - + + +
  • ); const logout = isAuthenticated && (
  • - - - Log out - - + + Log out +
  • ); const settings = isAuthenticated && (
  • - - - - - + + +
  • ); @@ -102,27 +97,36 @@ const Header: FC = () => { alignItems={["flex-start", "stretch"]} > - { + onClick={(e) => { e.preventDefault(); if (window.location.pathname !== "/") Router.push("/"); }} + forButton + isNextLink > - + kutt logo {publicRuntimeConfig.SITE_NAME} - +
    + {!isMobile && (
  • {
  • - - - Report - - + + Report +
  • )} @@ -152,15 +159,20 @@ const Header: FC = () => { as="ul" style={{ listStyle: "none" }} > -
  • - - - + {isMobile && ( +
  • + + Report - - -
  • + + + )} {logout} {settings} {login} diff --git a/client/components/Icon/Icon.tsx b/client/components/Icon/Icon.tsx index 9bd53a284..00da045cc 100644 --- a/client/components/Icon/Icon.tsx +++ b/client/components/Icon/Icon.tsx @@ -1,4 +1,4 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import styled, { css } from "styled-components"; import { prop, ifProp } from "styled-tools"; import React, { FC } from "react"; diff --git a/client/components/Input.tsx b/client/components/Input.tsx index 0c51aef74..4f004395f 100644 --- a/client/components/Input.tsx +++ b/client/components/Input.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { Flex, BoxProps } from "reflexbox/styled-components"; +import React from "react"; +import { Flex, BoxProps } from "rebass/styled-components"; import styled, { css, keyframes } from "styled-components"; import { withProp, prop, ifProp } from "styled-tools"; import { FC } from "react"; @@ -41,7 +41,7 @@ export const TextInput = styled(Flex).attrs({ } ::placeholder { - font-size: ${withProp("placeholderSize", s => s[0] || 14)}px; + font-size: ${withProp("placeholderSize", (s) => s[0] || 14)}px; letter-spacing: 0.05em; color: #888; } @@ -50,7 +50,7 @@ export const TextInput = styled(Flex).attrs({ ::placeholder { font-size: ${withProp( "placeholderSize", - s => s[3] || s[2] || s[1] || s[0] || 16 + (s) => s[3] || s[2] || s[1] || s[0] || 16 )}px; } } @@ -61,14 +61,14 @@ export const TextInput = styled(Flex).attrs({ ::placeholder { font-size: ${withProp( "placeholderSize", - s => s[2] || s[1] || s[0] || 15 + (s) => s[2] || s[1] || s[0] || 15 )}px; } } @media screen and (min-width: 40em) { ::placeholder { - font-size: ${withProp("placeholderSize", s => s[1] || s[0] || 15)}px; + font-size: ${withProp("placeholderSize", (s) => s[1] || s[0] || 15)}px; } } `; @@ -211,8 +211,11 @@ const CheckboxBox = styled(Flex).attrs({ )} `; -interface CheckboxProps extends ChecknoxInputProps, BoxProps { +interface CheckboxProps + extends ChecknoxInputProps, + Omit { label: string; + value?: boolean | string; } export const Checkbox: FC = ({ diff --git a/client/components/Layout.tsx b/client/components/Layout.tsx index 9a6a6d044..af1c77696 100644 --- a/client/components/Layout.tsx +++ b/client/components/Layout.tsx @@ -1,34 +1,34 @@ import React from "react"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import { FC } from "react"; type Props = React.ComponentProps; -export const Col: FC = props => ( +export const Col: FC = (props) => ( ); -export const RowCenterV: FC = props => ( +export const RowCenterV: FC = (props) => ( ); -export const RowCenterH: FC = props => ( +export const RowCenterH: FC = (props) => ( ); -export const RowCenter: FC = props => ( +export const RowCenter: FC = (props) => ( ); -export const ColCenterV: FC = props => ( +export const ColCenterV: FC = (props) => ( ); -export const ColCenterH: FC = props => ( +export const ColCenterH: FC = (props) => ( ); -export const ColCenter: FC = props => ( +export const ColCenter: FC = (props) => ( = ({ index, link, setDeleteModal }) => { - const isAdmin = useStoreState(s => s.auth.isAdmin); - const ban = useStoreActions(s => s.links.ban); - const edit = useStoreActions(s => s.links.edit); + const isAdmin = useStoreState((s) => s.auth.isAdmin); + const ban = useStoreActions((s) => s.links.ban); + const edit = useStoreActions((s) => s.links.edit); const [banFormState, { checkbox }] = useFormState(); const [editFormState, { text, label, password }] = useFormState( { @@ -182,7 +181,7 @@ const Row: FC = ({ index, link, setDeleteModal }) => { }; const toggleEdit = () => { - setShowEdit(s => !s); + setShowEdit((s) => !s); if (showEdit) editFormState.reset(); setEditMessage(""); }; @@ -280,16 +279,19 @@ const Row: FC = ({ index, link, setDeleteModal }) => { )} {link.visit_count > 0 && ( - - - - - + + + )} = ({ index, link, setDeleteModal }) => { Are you sure do you want to ban the link{" "} - "{removeProtocol(link.link)}"? + "{removeProtocol(link.link)}"? @@ -546,9 +548,9 @@ interface Form { } const LinksTable: FC = () => { - const isAdmin = useStoreState(s => s.auth.isAdmin); - const links = useStoreState(s => s.links); - const { get, remove } = useStoreActions(s => s.links); + const isAdmin = useStoreState((s) => s.auth.isAdmin); + const links = useStoreState((s) => s.links); + const { get, remove } = useStoreActions((s) => s.links); const [tableMessage, setTableMessage] = useState("No links to show."); const [deleteModal, setDeleteModal] = useState(-1); const [deleteLoading, setDeleteLoading] = useState(false); @@ -562,12 +564,12 @@ const LinksTable: FC = () => { const linkToDelete = links.items[deleteModal]; useEffect(() => { - get(options).catch(err => + get(options).catch((err) => setTableMessage(err?.response?.data?.error || "An error occurred.") ); - }, [options.limit, options.skip, options.all]); + }, [options, get]); - const onSubmit = e => { + const onSubmit = (e) => { e.preventDefault(); get(options); }; @@ -596,7 +598,7 @@ const LinksTable: FC = () => { flexShrink={1} > - {["10", "25", "50"].map(c => ( + {["10", "25", "50"].map((c) => ( { Are you sure do you want to delete the link{" "} - "{removeProtocol(linkToDelete.link)}"? + "{removeProtocol(linkToDelete.link)}"? {deleteLoading ? ( diff --git a/client/components/Modal.tsx b/client/components/Modal.tsx index b4a9ace26..8c1ccf7d7 100644 --- a/client/components/Modal.tsx +++ b/client/components/Modal.tsx @@ -1,4 +1,4 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import styled from "styled-components"; import React, { FC } from "react"; import ReactDOM from "react-dom"; @@ -27,7 +27,7 @@ const Wrapper = styled.div` const Modal: FC = ({ children, id, show, closeHandler, ...rest }) => { if (!show) return null; - const onClickOutside = e => { + const onClickOutside = (e) => { if (e.target.id === id) closeHandler(); }; diff --git a/client/components/NeedToLogin.tsx b/client/components/NeedToLogin.tsx index c2e8c5675..f23128a1e 100644 --- a/client/components/NeedToLogin.tsx +++ b/client/components/NeedToLogin.tsx @@ -1,7 +1,7 @@ import React from "react"; import Link from "next/link"; import styled from "styled-components"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import { Button } from "./Button"; import { fadeIn } from "../helpers/animations"; @@ -65,13 +65,11 @@ const NeedToLogin = () => ( Manage links, set custom <b>domains</b> and view <b>stats</b>. - - - - + + - + callout image
    ); diff --git a/client/components/PageLoading.tsx b/client/components/PageLoading.tsx index b6550fa5a..a61846498 100644 --- a/client/components/PageLoading.tsx +++ b/client/components/PageLoading.tsx @@ -1,4 +1,4 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import React from "react"; import { Colors } from "../consts"; diff --git a/client/components/ReCaptcha.tsx b/client/components/ReCaptcha.tsx index 66d7ac7c4..097084f25 100644 --- a/client/components/ReCaptcha.tsx +++ b/client/components/ReCaptcha.tsx @@ -1,4 +1,4 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import getConfig from "next/config"; import React from "react"; diff --git a/client/components/Settings/SettingsApi.tsx b/client/components/Settings/SettingsApi.tsx index 6fb34182c..5af05296f 100644 --- a/client/components/Settings/SettingsApi.tsx +++ b/client/components/Settings/SettingsApi.tsx @@ -1,5 +1,5 @@ import { CopyToClipboard } from "react-copy-to-clipboard"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import React, { FC, useState } from "react"; import styled from "styled-components"; @@ -32,13 +32,13 @@ const SettingsApi: FC = () => { const [copied, setCopied] = useCopy(); const [message, setMessage] = useMessage(1500); const [loading, setLoading] = useState(false); - const apikey = useStoreState(s => s.settings.apikey); - const generateApiKey = useStoreActions(s => s.settings.generateApiKey); + const apikey = useStoreState((s) => s.settings.apikey); + const generateApiKey = useStoreActions((s) => s.settings.generateApiKey); const onSubmit = async () => { if (loading) return; setLoading(true); - await generateApiKey().catch(err => setMessage(errorMessage(err))); + await generateApiKey().catch((err) => setMessage(errorMessage(err))); setLoading(false); }; diff --git a/client/components/Settings/SettingsChangeEmail.tsx b/client/components/Settings/SettingsChangeEmail.tsx index c64006774..bf0803494 100644 --- a/client/components/Settings/SettingsChangeEmail.tsx +++ b/client/components/Settings/SettingsChangeEmail.tsx @@ -1,6 +1,6 @@ import { useFormState } from "react-use-form-state"; import React, { FC, useState } from "react"; -import { Flex } from "reflexbox"; +import { Flex } from "rebass"; import axios from "axios"; import { getAxiosConfig } from "../../utils"; @@ -22,7 +22,7 @@ const SettingsChangeEmail: FC = () => { withIds: true }); - const onSubmit = async e => { + const onSubmit = async (e) => { e.preventDefault(); if (loading) return; setLoading(true); diff --git a/client/components/Settings/SettingsDomain.tsx b/client/components/Settings/SettingsDomain.tsx index ac4de494f..b8597880e 100644 --- a/client/components/Settings/SettingsDomain.tsx +++ b/client/components/Settings/SettingsDomain.tsx @@ -1,5 +1,5 @@ import { useFormState } from "react-use-form-state"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import React, { FC, useState } from "react"; import styled from "styled-components"; import getConfig from "next/config"; @@ -27,10 +27,10 @@ const Td = styled(Flex).attrs({ as: "td", py: 12, px: 3 })` `; const SettingsDomain: FC = () => { - const { saveDomain, deleteDomain } = useStoreActions(s => s.settings); + const { saveDomain, deleteDomain } = useStoreActions((s) => s.settings); const [domainToDelete, setDomainToDelete] = useState(null); const [deleteLoading, setDeleteLoading] = useState(false); - const domains = useStoreState(s => s.settings.domains); + const domains = useStoreState((s) => s.settings.domains); const [message, setMessage] = useMessage(2000); const [loading, setLoading] = useState(false); const [modal, setModal] = useState(false); @@ -39,7 +39,7 @@ const SettingsDomain: FC = () => { homepage: string; }>(null, { withIds: true }); - const onSubmit = async e => { + const onSubmit = async (e) => { e.preventDefault(); setLoading(true); @@ -59,7 +59,7 @@ const SettingsDomain: FC = () => { const onDelete = async () => { setDeleteLoading(true); - await deleteDomain(domainToDelete.id).catch(err => + await deleteDomain(domainToDelete.id).catch((err) => setMessage(errorMessage(err, "Couldn't delete the domain.")) ); setMessage("Domain has been deleted successfully.", "green"); @@ -91,7 +91,7 @@ const SettingsDomain: FC = () => { - {domains.map(d => ( + {domains.map((d) => ( {d.address} @@ -174,7 +174,10 @@ const SettingsDomain: FC = () => { Are you sure do you want to delete the domain{" "} - "{domainToDelete && domainToDelete.address}"? + + "{domainToDelete && domainToDelete.address}" + + ? {deleteLoading ? ( diff --git a/client/components/Settings/SettingsPassword.tsx b/client/components/Settings/SettingsPassword.tsx index 281843e80..301fce9ba 100644 --- a/client/components/Settings/SettingsPassword.tsx +++ b/client/components/Settings/SettingsPassword.tsx @@ -1,5 +1,5 @@ import { useFormState } from "react-use-form-state"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import React, { FC, useState } from "react"; import axios from "axios"; @@ -20,7 +20,7 @@ const SettingsPassword: FC = () => { { withIds: true } ); - const onSubmit = async e => { + const onSubmit = async (e) => { e.preventDefault(); if (loading) return; if (!formState.validity.password) { @@ -61,7 +61,7 @@ const SettingsPassword: FC = () => { { + validate: (value) => { const val = value.trim(); if (!val || val.length < 8) { return "Password must be at least 8 chars."; diff --git a/client/components/Shortener.tsx b/client/components/Shortener.tsx index d7b5f489a..6f1f82faf 100644 --- a/client/components/Shortener.tsx +++ b/client/components/Shortener.tsx @@ -1,6 +1,6 @@ import { CopyToClipboard } from "react-copy-to-clipboard"; import { useFormState } from "react-use-form-state"; -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import React, { useState } from "react"; import styled from "styled-components"; import getConfig from "next/config"; @@ -62,27 +62,26 @@ interface Form { const defaultDomain = publicRuntimeConfig.DEFAULT_DOMAIN; const Shortener = () => { - const { isAuthenticated } = useStoreState(s => s.auth); - const domains = useStoreState(s => s.settings.domains); - const submit = useStoreActions(s => s.links.submit); + const { isAuthenticated } = useStoreState((s) => s.auth); + const domains = useStoreState((s) => s.settings.domains); + const submit = useStoreActions((s) => s.links.submit); const [link, setLink] = useState(null); const [message, setMessage] = useMessage(3000); const [loading, setLoading] = useState(false); const [copied, setCopied] = useCopy(); - const [formState, { raw, password, text, select, label }] = useFormState< - Form - >( - { showAdvanced: false }, - { - withIds: true, - onChange(e, stateValues, nextStateValues) { - if (stateValues.showAdvanced && !nextStateValues.showAdvanced) { - formState.clear(); - formState.setField("target", stateValues.target); + const [formState, { raw, password, text, select, label }] = + useFormState
    ( + { showAdvanced: false }, + { + withIds: true, + onChange(e, stateValues, nextStateValues) { + if (stateValues.showAdvanced && !nextStateValues.showAdvanced) { + formState.clear(); + formState.setField("target", stateValues.target); + } } } - } - ); + ); const submitLink = async (reCaptchaToken?: string) => { try { @@ -97,7 +96,7 @@ const Shortener = () => { setLoading(false); }; - const onSubmit = async e => { + const onSubmit = async (e) => { e.preventDefault(); if (loading) return; setCopied(false); @@ -232,7 +231,7 @@ const Shortener = () => { { + onChange: () => { if (!isAuthenticated) { setMessage( "You need to log in or sign up to use advanced options." @@ -270,7 +269,7 @@ const Shortener = () => { width={[1, 210, 240]} options={[ { key: defaultDomain, value: "" }, - ...domains.map(d => ({ + ...domains.map((d) => ({ key: d.address, value: d.address })) diff --git a/client/components/Table.ts b/client/components/Table.ts index c075e3526..e60027939 100644 --- a/client/components/Table.ts +++ b/client/components/Table.ts @@ -1,4 +1,4 @@ -import { Flex } from "reflexbox/styled-components"; +import { Flex } from "rebass/styled-components"; import styled, { css } from "styled-components"; import { ifProp, prop } from "styled-tools"; diff --git a/client/components/Text.tsx b/client/components/Text.tsx index dd27213a5..395fc66d5 100644 --- a/client/components/Text.tsx +++ b/client/components/Text.tsx @@ -1,6 +1,6 @@ import React from "react"; import { switchProp, ifNotProp, ifProp } from "styled-tools"; -import { Box, BoxProps } from "reflexbox/styled-components"; +import { Box, BoxProps } from "rebass/styled-components"; import styled, { css } from "styled-components"; import { FC, CSSProperties } from "react"; @@ -58,10 +58,10 @@ Text.defaultProps = { export default Text; -export const H1: FC = props => ; -export const H2: FC = props => ; -export const H3: FC = props => ; -export const H4: FC = props => ; -export const H5: FC = props => ; -export const H6: FC = props => ; -export const Span: FC = props => ; +export const H1: FC = (props) => ; +export const H2: FC = (props) => ; +export const H3: FC = (props) => ; +export const H4: FC = (props) => ; +export const H5: FC = (props) => ; +export const H6: FC = (props) => ; +export const Span: FC = (props) => ; diff --git a/client/components/__tests__/footer.test.tsx b/client/components/__tests__/footer.test.tsx deleted file mode 100644 index 5bcbcf461..000000000 --- a/client/components/__tests__/footer.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react"; -import { StoreProvider } from "easy-peasy"; -import { initializeStore } from "../../store"; -import Footer from "../Footer"; -import getConfig from "next/config"; - -describe("