Skip to content
This repository has been archived by the owner on Oct 10, 2023. It is now read-only.

Commit

Permalink
Resolve: Introduce Tailwind (#2337)
Browse files Browse the repository at this point in the history
- [x] Introduce Tailwind
- [x] Add colors / breakpoints to tailwind config
- [x] Add theme switch for tailwind
- [x] Style `ImportKeystore` as an example (needed by #1601) using Tailwind and in order to remove its styled-components.
- [x] Quick fix: readJSON + `ImportKeystore.stories.tsx`
  • Loading branch information
veado authored Jul 30, 2022
1 parent 9d1f85d commit 03308cc
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 107 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"@types/secp256k1": "^4.0.3",
"@types/styled-components": "^5.1.25",
"assert": "^2.0.0",
"autoprefixer": "^10.4.8",
"browserify-fs": "^1.0.0",
"buffer": "^6.0.3",
"chalk": "^5.0.1",
Expand All @@ -177,13 +178,15 @@
"nodemon": "^2.0.16",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"postcss": "^8.4.14",
"prettier": "^2.6.2",
"process": "^0.11.10",
"react-error-overlay": "^6.0.11",
"rimraf": "^3.0.2",
"source-map-explorer": "^2.5.2",
"storybook": "^7.0.0-alpha.13",
"stream-browserify": "^3.0.0",
"tailwindcss": "^3.1.7",
"testcafe": "^1.18.6",
"testcafe-browser-provider-electron": "^0.0.18",
"testcafe-react-selectors": "^4.1.5",
Expand Down
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}
6 changes: 3 additions & 3 deletions src/main/api/keystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ export const loadKeystore = async () => {
const migrateLegacyAccount = (): TE.TaskEither<Error, KeystoreAccounts> =>
FP.pipe(
exists(LEGACY_KEYSTORE_FILE),
// Switch to error if no file exists
// Switch to error if file does not exists
TE.fromPredicate(
(v) => !!v,
() => Error('Keystore file does not exist')
() => Error(`${LEGACY_KEYSTORE_FILE} file does not exist`)
),
// get keystore content
// read keystore from disc
TE.chain(() => readJSON(LEGACY_KEYSTORE_FILE)),
// decode keystore content
TE.chain(FP.flow(keystoreIO.decode, E.mapLeft(mapIOErrors), TE.fromEither)),
Expand Down
17 changes: 13 additions & 4 deletions src/main/utils/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ export const readFile: (path: string, encoding: string) => TE.TaskEither<Error,
/**
* Reads a JSON file.
*/
export const readJSON: (path: string, options?: fs.ReadOptions) => TE.TaskEither<Error, string> = TE.taskify<
export const readJSON: (path: string, options?: fs.ReadOptions) => TE.TaskEither<Error, unknown> = TE.taskify<
string,
fs.ReadOptions | undefined,
Error,
string
unknown
>(fs.readJSON)

/**
Expand All @@ -47,8 +47,17 @@ export const writeFile: (path: string, data: string, options?: fs.WriteFileOptio
* Writes a JSON file.
* If the parent directory does not exist, it will be created.
*/
export const writeJSON: (path: string, data: object, options?: fs.WriteOptions) => TE.TaskEither<Error, void> =
TE.taskify<string, object, fs.WriteOptions | undefined, Error, void>(fs.outputJSON)
export const writeJSON: (
path: string,
data: boolean | number | string | object | null,
options?: fs.WriteOptions
) => TE.TaskEither<Error, void> = TE.taskify<
string,
boolean | number | string | object | null,
fs.WriteOptions | undefined,
Error,
void
>(fs.outputJSON)

export const exists: (path: string) => TE.TaskEither<Error, boolean> = TE.taskify<string, Error, boolean>(fs.pathExists)

Expand Down
15 changes: 5 additions & 10 deletions src/renderer/components/wallet/keystore/ImportKeystore.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as RD from '@devexperts/remote-data-ts'
import { ComponentMeta, StoryFn } from '@storybook/react'
import { Keystore } from '@xchainjs/xchain-crypto'
import * as Rx from 'rxjs'

import { MOCK_KEYSTORE } from '../../../../shared/mock/wallet'
import { ImportKeystore as Component, Props } from './ImportKeystore'

const initialImportKeystore = () => Rx.of(RD.initial)
Expand All @@ -15,7 +15,7 @@ export const Default = Template.bind({})

const meta: ComponentMeta<typeof Component> = {
component: Component,
title: 'Wallet/Keystore',
title: 'Wallet/ImportKeystore',
argTypes: {
loadKeystore$: {
control: {
Expand All @@ -25,7 +25,7 @@ const meta: ComponentMeta<typeof Component> = {
intitial: initialLoadKeystore,
loading: () => Rx.of(RD.pending),
error: () => Rx.of(RD.failure(Error('load keystore error'))),
success: () => Rx.of(RD.success({} as Keystore))
success: () => Rx.of(RD.success(MOCK_KEYSTORE))
}
}
},
Expand All @@ -37,7 +37,7 @@ const meta: ComponentMeta<typeof Component> = {
intitial: initialImportKeystore,
loading: () => Rx.of(RD.pending),
error: () => Rx.of(RD.failure(Error('import keystore error'))),
success: () => Rx.of(RD.success({}))
success: () => Rx.of(RD.success(undefined))
}
}
}
Expand All @@ -48,12 +48,7 @@ const meta: ComponentMeta<typeof Component> = {
},
decorators: [
(Story) => (
<div
style={{
display: 'flex',
flexDirection: 'column',
width: '300px'
}}>
<div className="flex items-center w-full">
<Story />
</div>
)
Expand Down
85 changes: 59 additions & 26 deletions src/renderer/components/wallet/keystore/ImportKeystore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { useIntl } from 'react-intl'
import { KeystoreClientStates } from '../../../hooks/useKeystoreClientStates'
import { useSubscriptionState } from '../../../hooks/useSubscriptionState'
import { ImportKeystoreLD, ImportKeystoreParams, LoadKeystoreLD } from '../../../services/wallet/types'
import { InnerForm } from '../../shared/form/Form.styles'
import { Spin } from '../../shared/loading'
import { InputPassword } from '../../uielements/input'
import * as Styled from './Keystore.styles'
import { Button } from '../../uielements/button'
import { InputPassword, Input } from '../../uielements/input'
import { Label } from '../../uielements/label'

export type Props = {
clientStates: KeystoreClientStates
Expand All @@ -36,27 +38,30 @@ export const ImportKeystore: React.FC<Props> = (props): JSX.Element => {
RD.RemoteData<Error, void>
>(RD.initial)

const walletId = useMemo(() => new Date().getTime(), [])

const submitForm = useCallback(
({ password }: Store) => {
FP.pipe(
loadKeystoreState,
RD.map((keystore) => {
const id = new Date().getTime()
// TODO (@veado) Get name from form
const name = `wallet-${id}`
subscribeImportKeystoreState(importKeystore$({ keystore, password, id, name }))
const name = `wallet-${walletId}`
subscribeImportKeystoreState(importKeystore$({ keystore, password, id: walletId, name }))
return true
})
)
},
[importKeystore$, loadKeystoreState, subscribeImportKeystoreState]
[importKeystore$, loadKeystoreState, subscribeImportKeystoreState, walletId]
)

const uploadKeystore = () => {
subscribeLoadKeystoreState(loadKeystore$())
}

const renderError = useCallback((msg: string) => <Styled.ErrorLabel>{msg}</Styled.ErrorLabel>, [])
const renderError = (msg: string) => (
<Label className="mb-[20px] uppercase text-error0 dark:text-error0 text-center">{msg}</Label>
)

const renderImportError = useMemo(
() =>
Expand All @@ -69,7 +74,7 @@ export const ImportKeystore: React.FC<Props> = (props): JSX.Element => {
() => <></>
)
),
[importKeystoreState, intl, renderError]
[importKeystoreState, intl]
)

const renderLoadError = useMemo(
Expand All @@ -83,7 +88,7 @@ export const ImportKeystore: React.FC<Props> = (props): JSX.Element => {
() => <></>
)
),
[loadKeystoreState, intl, renderError]
[loadKeystoreState, intl]
)

const renderClientError = useMemo(
Expand All @@ -97,37 +102,65 @@ export const ImportKeystore: React.FC<Props> = (props): JSX.Element => {
() => <></>
)
),
[clientStates, renderError]
[clientStates]
)

return (
<>
<Styled.Form form={form} onFinish={submitForm} labelCol={{ span: 24 }}>
<InnerForm className="w-full p-[30px] pt-[15px]" labelCol={{ span: 24 }} form={form} onFinish={submitForm}>
{renderLoadError}
{renderImportError}
{renderClientError}
<Spin
spinning={RD.isPending(importKeystoreState) || RD.isPending(loadKeystoreState)}
tip={intl.formatMessage({ id: 'common.loading' })}>
<Form.Item>
<Styled.Title>{intl.formatMessage({ id: 'wallet.imports.keystore.title' })}</Styled.Title>
<Styled.KeystoreButton onClick={uploadKeystore}>
<div className="flex flex-col items-center">
{/* title */}
<Label className="w-full mb-10" size="big" align="center" textTransform="uppercase">
{intl.formatMessage({ id: 'wallet.imports.keystore.title' })}
</Label>
{/* import button */}
<Button
className="mw-[100%] py-[5xp] px-[10px] mb-[50px] h-[35px] cursor-pointer text-[14px]"
typevalue="outline"
sizevalue="normal"
onClick={uploadKeystore}>
{RD.isSuccess(loadKeystoreState) ? <CheckCircleTwoTone twoToneColor="#50e3c2" /> : <UploadOutlined />}
{intl.formatMessage({ id: 'wallet.imports.keystore.select' })}
</Styled.KeystoreButton>
</Form.Item>
<Styled.PasswordContainer>
<Styled.PasswordItem name="password">
<InputPassword size="large" placeholder={intl.formatMessage({ id: 'common.password' }).toUpperCase()} />
</Styled.PasswordItem>
</Styled.PasswordContainer>
</Button>
{/* password */}
<div className="w-full flex flex-col items-center">
<Form.Item
className="w-full !max-w-[380px] uppercase text-text0 dark:text-text0d"
name="password"
label={intl.formatMessage({ id: 'common.password' })}>
<InputPassword className="!text-[16px]" size="large" />
</Form.Item>
</div>
{/* name */}
<div className="w-full flex flex-col items-center">
<Form.Item
className="w-full !max-w-[380px] uppercase text-text0 dark:text-text0d"
name="name"
label={'wallet name'}>
<Input className="!text-[16px]" size="large" placeholder={`wallet-${walletId}`} />
</Form.Item>
</div>
</div>
</Spin>
<Form.Item style={{ display: 'grid', justifyContent: 'flex-end' }}>
<Styled.SubmitButton disabled={!RD.isSuccess(loadKeystoreState) || RD.isPending(importKeystoreState)}>
{/* submit button */}
<div className="flex flex-col items-center">
<Button
className="min-w-[150px] mt-[50px]"
sizevalue="xnormal"
type="primary"
htmlType="submit"
round="true"
disabled={!RD.isSuccess(loadKeystoreState) || RD.isPending(importKeystoreState)}>
{intl.formatMessage({ id: 'wallet.action.import' })}
</Styled.SubmitButton>
</Form.Item>
</Styled.Form>
</Button>
</div>
</InnerForm>
</>
)
}
62 changes: 0 additions & 62 deletions src/renderer/components/wallet/keystore/Keystore.styles.ts

This file was deleted.

6 changes: 5 additions & 1 deletion src/renderer/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

/*
Since `createGlobalStyle` of `styled-components` re-loads fonts in some cases (and for unknown reasons),
which ends up in flickering texts, we do defined all fonts and other "global" styles here
Expand All @@ -7,7 +11,7 @@ which ends up in flickering texts, we do defined all fonts and other "global" st
font-family: 'MainFontRegular';
font-style: normal;
font-weight: normal;
src: local('MainFontBold'), url(./assets/fonts/Exo2-Regular.otf) format('opentype');
src: local('MainFontRegular'), url(./assets/fonts/Exo2-Regular.otf) format('opentype');
}
@font-face {
font-family: 'MainFontBold';
Expand Down
13 changes: 13 additions & 0 deletions src/renderer/views/app/AppView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useWalletContext } from '../../contexts/WalletContext'
import { unionChains } from '../../helpers/fp/array'
import { rdAltOnPending } from '../../helpers/fpHelpers'
import { useMimirHalt } from '../../hooks/useMimirHalt'
import { useTheme } from '../../hooks/useTheme'
import { DEFAULT_MIMIR_HALT } from '../../services/thorchain/const'
import { MimirHalt, MimirHaltRD } from '../../services/thorchain/types'
import { View } from '../View'
Expand All @@ -39,11 +40,23 @@ export const AppView: React.FC = (): JSX.Element => {
const { locale$ } = useI18nContext()
const currentLocale = useObservableState(locale$, DEFAULT_LOCALE)

const { isLight } = useTheme()

// locale
useEffect(() => {
// Needed to update Electron native menu according to the selected locale
window.apiLang.update(currentLocale)
}, [currentLocale])

// Add/remove `dark` selector depending on selected theme (needed for tailwind)
useEffect(() => {
if (isLight) {
document.documentElement.classList.remove('dark')
} else {
document.documentElement.classList.add('dark')
}
}, [isLight])

const {
service: {
apiEndpoint$,
Expand Down
Loading

0 comments on commit 03308cc

Please sign in to comment.