diff --git a/.babelrc b/.babelrc index b02645ca1..eb6d32d93 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,5 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react"], + "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"], "plugins": [ ["babel-plugin-wildcard", { "exts": ["json"], "nostrip": true, "noModifyCase": true }], [ diff --git a/.dockerignore b/.dockerignore index 0634d58ed..990a864bc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,7 +4,6 @@ .vscode .eslintrc.yml .gitignore -.sass-lint.yml .travis.yml node_modules diff --git a/.gitignore b/.gitignore index 8a30e37c5..c329861ac 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ *.swp *.vi *~ -*.sass-cache # OS or Editor folders .DS_Store diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index 110a25ab8..000000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,95 +0,0 @@ -options: - formatter: stylish -files: - include: '**/*.s+(a|c)ss' -rules: - # Extends - extends-before-mixins: 1 - extends-before-declarations: 1 - placeholder-in-extend: 1 - - # Mixins - mixins-before-declarations: 1 - - # Line Spacing - one-declaration-per-line: 1 - empty-line-between-blocks: 1 - single-line-per-selector: 1 - - # Disallows - no-attribute-selectors: 0 - no-color-hex: 0 - no-color-keywords: 1 - no-color-literals: 1 - no-combinators: 0 - no-css-comments: 1 - no-debug: 1 - no-disallowed-properties: 0 - no-duplicate-properties: 1 - no-empty-rulesets: 1 - no-extends: 0 - no-ids: 1 - no-important: 1 - no-invalid-hex: 1 - no-mergeable-selectors: 1 - no-misspelled-properties: 1 - no-qualifying-elements: 1 - no-trailing-whitespace: 1 - no-trailing-zero: 1 - no-transition-all: 1 - no-universal-selectors: 0 - no-url-domains: 1 - no-url-protocols: 1 - no-vendor-prefixes: 1 - no-warn: 1 - property-units: 0 - - # Nesting - declarations-before-nesting: 1 - force-attribute-nesting: 1 - force-element-nesting: 1 - force-pseudo-nesting: 1 - - # Name Formats - class-name-format: 1 - function-name-format: 1 - id-name-format: 0 - mixin-name-format: 1 - placeholder-name-format: 1 - variable-name-format: 1 - - # Style Guide - attribute-quotes: 1 - bem-depth: 0 - border-zero: 1 - brace-style: 1 - clean-import-paths: 1 - empty-args: 1 - hex-length: 1 - hex-notation: 1 - indentation: 1 - leading-zero: 0 - max-line-length: 0 - max-file-line-count: 0 - nesting-depth: 1 - property-sort-order: 0 - pseudo-element: 1 - quotes: 1 - shorthand-values: 1 - url-quotes: 1 - variable-for-property: 1 - zero-unit: 1 - - # Inner Spacing - space-after-comma: 1 - space-before-colon: 1 - space-after-colon: 1 - space-before-brace: 1 - space-before-bang: 1 - space-after-bang: 1 - space-between-parens: 1 - space-around-operator: 1 - - # Final Items - trailing-semicolon: 1 - final-newline: 1 diff --git a/README.md b/README.md index 860dc26d4..a1ce2eb25 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,6 @@ app │   │   └── selectors.js => Re-select selectors to select data in state │   └── ... ├── static => Static assets, directly copied to the public directory -├── styles => Stylesheets in .sass format, all included from application.sass └── router.jsx => Application router and main entry point ``` diff --git a/app/API/no_internet_error.js b/app/API/no_internet_error.js index 9aaf4ae20..137fe4633 100644 --- a/app/API/no_internet_error.js +++ b/app/API/no_internet_error.js @@ -1,14 +1,14 @@ // Used only to show a message when request fail / timeout due to connection problems + import { NO_INTERNET_ERROR } from '../constants' import { flashError } from '../state/flashes/reducer' -import store from '../state/index' export default function noInternetError() { - store.dispatch( - flashError({ - message: NO_INTERNET_ERROR, - timeLeft: 999999999999, - infoText: 'actions.reload', - }), - ) + flashError({ + title: 'TODO', + message: NO_INTERNET_ERROR, + infoText: 'actions.reload', + variant: 'destructive', + duration: 999999999999, + }) } diff --git a/app/App.jsx b/app/App.jsx index 4c51e95f7..318770060 100644 --- a/app/App.jsx +++ b/app/App.jsx @@ -1,6 +1,6 @@ // Import polyfills // Import styles -import './styles/application.sass' +import '@/styles/main.css' import { ApolloProvider } from '@apollo/client' // Import libs @@ -8,12 +8,13 @@ import React from 'react' import { I18nextProvider } from 'react-i18next' import { Configure, Index, InstantSearch } from 'react-instantsearch-dom' import { Provider as ReduxProvider } from 'react-redux' -import { polyfill as smoothSrollPolyfill } from 'smoothscroll-polyfill' +import { polyfill as smoothScrollPolyfill } from 'smoothscroll-polyfill' import { ThemeProvider } from 'styled-components' // Import APIs so they can load their configurations import GraphQLClient from './API/graphql_api' import UserProvider from './components/LoggedInUser/UserProvider' +import { ToastProvider } from './components/ui/toast' import { ENTITY_SPEAKER, ENTITY_STATEMENT, ENTITY_VIDEO } from './constants' import i18n from './i18n/i18n' import { ALGOLIA_INDEXES_NAMES, searchClient } from './lib/algolia' @@ -24,34 +25,36 @@ import store from './state' import theme from './styles/theme' // Activate polyfills -smoothSrollPolyfill() +smoothScrollPolyfill() const App = () => ( - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + ) export default App diff --git a/app/components/App/LanguageSelector.jsx b/app/components/App/LanguageSelector.jsx index 16c63d493..69ffbcf89 100644 --- a/app/components/App/LanguageSelector.jsx +++ b/app/components/App/LanguageSelector.jsx @@ -1,10 +1,17 @@ -import { Box, Flex } from '@rebass/grid' -import classNames from 'classnames' import { Map } from 'immutable' import React from 'react' import { withNamespaces } from 'react-i18next' import { Globe } from 'styled-icons/fa-solid' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { cn } from '@/lib/css-utils' + const defaultLocales = new Map({ en: 'English', fr: 'Français', @@ -21,26 +28,25 @@ export default class LanguageSelector extends React.PureComponent { const options = defaultLocales.merge(this.props.additionalOptions || {}).sortBy((v, k) => k) return ( - + ) } renderLocalesMap(localesMap) { return localesMap.entrySeq().map(([key, value]) => ( - + )) } renderIcon() { - const { value, size } = this.props + const { value } = this.props if (value === 'fr') { return '🇫🇷' } else if (value === 'en') { @@ -56,16 +62,15 @@ export default class LanguageSelector extends React.PureComponent { } else if (value === 'ru') { return '🇷🇺' } - return + return } render() { - const sizeClass = this.props.size ? `is-${this.props.size}` : null return ( - - {this.props.withIcon && {this.renderIcon()}} - {this.renderSelect()} - +
+ {this.props.withIcon &&
{this.renderIcon()}
} + {this.renderSelect()} +
) } } diff --git a/app/components/App/Layout.jsx b/app/components/App/Layout.jsx index f631c3965..391836c6d 100644 --- a/app/components/App/Layout.jsx +++ b/app/components/App/Layout.jsx @@ -2,9 +2,11 @@ import React from 'react' import { Helmet } from 'react-helmet' import { connect } from 'react-redux' +import { cn } from '@/lib/css-utils' + import { MainModalContainer } from '../Modal/MainModalContainer' +import { Toaster } from '../ui/toaster' import PublicAchievementUnlocker from '../Users/PublicAchievementUnlocker' -import { FlashMessages } from '../Utils' import BackgroundNotifier from './BackgroundNotifier' import CrashReportPage from './CrashReportPage' import Navbar from './Navbar' @@ -48,24 +50,27 @@ export default class Layout extends React.PureComponent { render() { const { locale, sidebarExpended, children } = this.props - const mainContainerClass = sidebarExpended ? undefined : 'expended' return ( -
- {this.renderMetadata()} - - - - -
- {!this.state.error ? children : } + +
+ {this.renderMetadata()} + + + +
+ {!this.state.error ? children : } +
+ +
- - -
+ + ) } diff --git a/app/components/App/Logo.jsx b/app/components/App/Logo.jsx index e891055fb..41d1ae255 100644 --- a/app/components/App/Logo.jsx +++ b/app/components/App/Logo.jsx @@ -29,10 +29,12 @@ const Image = styled.img` * The main website logo. */ const Logo = ({ borderless, height }) => ( - + C - aptain - + + aptain + + Fact diff --git a/app/components/App/Navbar.jsx b/app/components/App/Navbar.jsx index 3063d91ab..d425c79ca 100644 --- a/app/components/App/Navbar.jsx +++ b/app/components/App/Navbar.jsx @@ -1,12 +1,12 @@ import { Box, Flex } from '@rebass/grid' import { omit } from 'lodash' +import { LogIn } from 'lucide-react' import React from 'react' import { withNamespaces } from 'react-i18next' import { withResizeDetector } from 'react-resize-detector' import { Link, withRouter } from 'react-router-dom' import Popup from 'reactjs-popup' import styled, { css, withTheme } from 'styled-components' -import { HelpCircle } from 'styled-icons/boxicons-regular' import { UserCircle } from 'styled-icons/fa-regular' import { CaretDown } from 'styled-icons/fa-solid' import { themeGet } from 'styled-system' @@ -22,6 +22,7 @@ import Container from '../StyledUtils/Container' import { fadeIn } from '../StyledUtils/Keyframes' import StyledLink from '../StyledUtils/StyledLink' import { Span } from '../StyledUtils/Text' +import { Button } from '../ui/button' import ScoreTag from '../Users/ScoreTag' import UserAppellation from '../Users/UserAppellation' import UserMenu from '../Users/UserMenu' @@ -65,7 +66,7 @@ const UserMenuEntry = styled((props) => {isAuthenticated ? ( - - - - + - +
  {title} - +
)} @@ -260,29 +254,17 @@ const Navbar = ({
) : ( - - - {t('menu.login')} - - - {t('menu.extension')} - - - {t('menu.signup')} +
+ + + + + - +
)} )} diff --git a/app/components/App/Sidebar.jsx b/app/components/App/Sidebar.jsx index 87aaf6d54..9e0c0a71c 100644 --- a/app/components/App/Sidebar.jsx +++ b/app/components/App/Sidebar.jsx @@ -1,7 +1,7 @@ import { Query } from '@apollo/client/react/components' -import { Flex } from '@rebass/grid' import classNames from 'classnames' import { capitalize, get } from 'lodash' +import { CircleHelp, Flag, Heart, ListVideo, Puzzle, Users } from 'lucide-react' import React from 'react' import { withNamespaces } from 'react-i18next' import { connect } from 'react-redux' @@ -27,7 +27,6 @@ import UserLanguageSelector from '../LoggedInUser/UserLanguageSelector' import { withLoggedInUser } from '../LoggedInUser/UserProvider' import ExternalLinkNewTab from '../Utils/ExternalLinkNewTab' import ProgressBar from '../Utils/ProgressBar' -import RawIcon from '../Utils/RawIcon' import ReputationGuard from '../Utils/ReputationGuard' import Tag from '../Utils/Tag' @@ -42,6 +41,7 @@ const WhiteStar = styled(Star)` const DailyGainText = styled.p` color: #858585; ` + @connect((state) => ({ sidebarExpended: state.UserPreferences.sidebarExpended }), { toggleSidebar, closeSidebar, @@ -62,15 +62,19 @@ export default class Sidebar extends React.PureComponent { } } - MenuLink({ title, iconName, customLink, className, children, ...props }) { - const classes = classNames(className, { 'link-with-icon': !!iconName }) + MenuLink({ title, className, children, ...props }) { + const classes = classNames( + 'flex items-center px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md hover:text-gray-900', + { 'bg-gray-100': props.isActive }, + className, + ) return ( - {iconName && } - {customLink ? children : {children}} + {children} ) } @@ -92,82 +95,99 @@ export default class Sidebar extends React.PureComponent { render() { const { sidebarExpended, className, t, isAuthenticated } = this.props return ( - -
-

{t('menu.language')}

- +
+

+ {t('menu.language')} +

+ {isAuthenticated ? ( -

{t('menu.yourProfile')}

+

+ {t('menu.yourProfile')} +

{this.renderMenuProfile()}
) : null} -

{t('menu.factChecking')}

+

+ {t('menu.factChecking')} +

{this.renderMenuContent()} -

{t('menu.other')}

-
    -
  • - +

    + {t('menu.other')} +

    +
      + + + {t('menu.help')} + + + + {t('menu.contact')} + + + {t('menu.extension')} - - {t('menu.forum')}  - + + {t('menu.forum')} + - + {t('menu.donation')}  - + - - {t('menu.help')} - - - - - - {t('menu.contact')} -
    -

    {t('menu.followus')}

    -
    - {' '} -     +

    + {t('menu.followus')} +

    +
    -     + -     + -     + -     + -     +
    - + ) } @@ -184,8 +204,8 @@ export default class Sidebar extends React.PureComponent { const dailyGain = get(data, 'loggedInUser.todayReputationGain', 0) return ( - - +
    +
    - +
    {`${t('menu.dailyGain')} ${dailyGain}/${MAX_DAILY_REPUTATION_GAIN}`} - +
    ) }} @@ -207,7 +227,7 @@ export default class Sidebar extends React.PureComponent { renderMenuProfile() { return ( -
      +
      • {this.renderDailyGainGauge()}
      ) @@ -216,8 +236,9 @@ export default class Sidebar extends React.PureComponent { renderMenuContent() { const t = this.props.t return ( -
        - +
          + + {capitalize(t('entities.videoFactChecking'))} @@ -229,7 +250,8 @@ export default class Sidebar extends React.PureComponent { {({ data }) => { const pendingCount = get(data, 'loggedInUser.actions_pending_moderation', 0) return ( - + + {t('menu.moderation')} {Boolean(pendingCount) && {pendingCount}} diff --git a/app/components/Comments/CommentAction.jsx b/app/components/Comments/CommentAction.jsx index 9ab4f6c3a..74295dc94 100644 --- a/app/components/Comments/CommentAction.jsx +++ b/app/components/Comments/CommentAction.jsx @@ -1,16 +1,13 @@ import classNames from 'classnames' import React from 'react' -import Button from '../Utils/Button' import { Icon } from '../Utils/Icon' +import { Button } from '../ui/button' +import { cn } from '@/lib/css-utils' -const CommentAction = ({ className, onClick, title, iconName, disabled }) => ( - ) diff --git a/app/components/Comments/CommentForm.jsx b/app/components/Comments/CommentForm.jsx index 71a8393cf..7983dd3d0 100644 --- a/app/components/Comments/CommentForm.jsx +++ b/app/components/Comments/CommentForm.jsx @@ -30,6 +30,7 @@ import Button from '../Utils/Button' import CloseButton from '../Utils/CloseButton' import ExternalLinkNewTab from '../Utils/ExternalLinkNewTab' import { CommentDisplay } from './CommentDisplay' +import { Input } from '../ui/input' const SubmitButton = styled((props) => ( +
) } + +export default FieldWithButton diff --git a/app/components/FormUtils/TextareaAutosize.jsx b/app/components/FormUtils/TextareaAutosize.jsx index 9829aa533..f11b445d1 100644 --- a/app/components/FormUtils/TextareaAutosize.jsx +++ b/app/components/FormUtils/TextareaAutosize.jsx @@ -2,13 +2,8 @@ import { omit } from 'lodash' import React from 'react' import styled from 'styled-components' import * as styledSystem from 'styled-system' - -const TextArea = styled.textarea` - resize: none !important; - ${styledSystem.minHeight} - ${styledSystem.maxHeight} - ${styledSystem.height} -` +import { Textarea } from '../ui/textarea' +import { cn } from '@/lib/css-utils' /** * A textarea that grows with its content. @@ -46,8 +41,8 @@ export default class TextareaAutosize extends React.PureComponent { render() { return ( -