Merge pull request #65 from nimbus-gui/main

feat: emoji picker, flow between pages
This commit is contained in:
Rickard Andersson 2023-08-23 22:28:22 +03:00 committed by GitHub
commit 7d4180f5cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1358 additions and 52 deletions

View File

@ -1,4 +1,4 @@
name: 'Deploy Storybook'
name: 'Deployment'
on:
push:
@ -69,3 +69,9 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
- name: Pull Vercel configuration
run: yarn vercel pull --yes --token ${{ secrets.vercel_token }}
- name: Build Vercel bundle
run: yarn vercel build --prod
- name: Deploy to Vercel
run: yarn vercel deploy --prebuilt --prod --token ${{ secrets.vercel_token }}

View File

@ -1,6 +1,14 @@
name: 'UI tests'
on: push
on:
push:
branches:
- '*'
- '!main'
permissions:
pull-requests: write
deployments: write
jobs:
cache-dependencies:
@ -22,11 +30,16 @@ jobs:
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn
build:
build-and-deploy:
runs-on: ubuntu-latest
needs: cache-dependencies
steps:
- uses: actions/checkout@v2
- name: Get PR number
id: pull_request
run: echo "::set-output name=number::$(gh pr view --json number -q .number || echo "")"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v2
with:
node-version: '18.x'
@ -42,6 +55,65 @@ jobs:
${{ runner.os }}-yarn-v3
- name: Build
run: yarn build
- name: Pull Vercel configuration
run: yarn vercel pull --yes --token ${{ secrets.vercel_token }}
- name: Build Vercel bundle
run: yarn vercel build
- name: Deploy to Vercel
run: yarn vercel deploy --prebuilt --token ${{ secrets.vercel_token }} > _vercel-deployment-url
- name: Comment on PR with deployment URL
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const deploymentUrl = fs.readFileSync('_vercel-deployment-url', 'utf8');
await github.rest.issues.createComment({
issue_number: ${{ steps.pull_request.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: `Deployed to ${deploymentUrl}`,
});
- name: Add deployment to PR
uses: actions/github-script@v6
id: deployment
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const deploymentUrl = fs.readFileSync('_vercel-deployment-url', 'utf8');
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
environment: 'preview',
transient_environment: true,
required_contexts: [],
auto_merge: false,
production_environment: false,
payload: JSON.stringify({
deploymentUrl,
}),
});
console.log("::set-output name=deployment_id::" + deployment.data.id);
- name: Add deployment status to PR
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const deploymentUrl = fs.readFileSync('_vercel-deployment-url', 'utf8');
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: ${{ steps.deployment.outputs.deployment_id }},
state: 'success',
environment_url: deploymentUrl,
log_url: deploymentUrl,
description: 'Deployed to Vercel',
environment: 'preview',
auto_inactive: true,
});
interaction-and-and-accessibility:
runs-on: ubuntu-latest
@ -70,3 +142,4 @@ jobs:
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --port 6006 --silent" \
"npx wait-on tcp:127.0.0.1:6006 && yarn test-storybook"

3
.gitignore vendored
View File

@ -33,3 +33,6 @@ dist-ssr
!.yarn/versions
/.tamagui/
/storybook-static/
# vercel
/.vercel

View File

@ -1,4 +1,4 @@
# React + TypeScript + Vite
# nimbus-gui
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

View File

@ -28,6 +28,7 @@
"@tamagui/vite-plugin": "1.36.4",
"@types/react": "18",
"@types/react-dom": "18",
"emoji-picker-react": "^4.4.11",
"expo-modules-core": "^1.5.9",
"react": "18",
"react-color": "^2.19.3",
@ -63,6 +64,7 @@
"storybook": "^7.2.0",
"storybook-addon-react-router-v6": "^2.0.5",
"typescript": "^5.0.2",
"vercel": "^32.0.1",
"vite": "^4.4.9"
},
"packageManager": "yarn@3.6.1"

View File

@ -1,4 +1,4 @@
import { TamaguiProvider } from 'tamagui'
import { TamaguiProvider, Theme } from 'tamagui'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { Provider as StatusProvider } from '@status-im/components'
import './App.css'
@ -8,9 +8,9 @@ import DeviceHealthCheck from './pages/DeviceHealthCheck/DeviceHealthCheck'
import ConnectDevicePage from './pages/ConnectDevicePage/ConnectDevicePage'
import DeviceSyncStatus from './pages/DeviceSyncStatus/DeviceSyncStatus'
import PairDevice from './pages/PairDevice/PairDevice'
import { Provider as ReduxProvider } from 'react-redux'
import { useSelector } from 'react-redux'
import PinnedNotification from './components/General/PinnedNottification'
import store from './redux/store'
import { RootState } from './redux/store'
import CreateLocalNodePage from './pages/CreateLocalNodePage/CreateLocalNodePage'
const router = createBrowserRouter([
@ -36,16 +36,19 @@ const router = createBrowserRouter([
},
{ path: '/create-local-node', element: <CreateLocalNodePage /> },
])
function App() {
const theme = useSelector((state: RootState) => state.theme)
return (
<ReduxProvider store={store}>
<TamaguiProvider config={config}>
<StatusProvider>
<TamaguiProvider config={config}>
<StatusProvider>
<Theme name={theme}>
<PinnedNotification />
<RouterProvider router={router} />
</StatusProvider>
</TamaguiProvider>
</ReduxProvider>
</Theme>
</StatusProvider>
</TamaguiProvider>
)
}

View File

@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react'
import EmojiPickerDialog from './EmojiPickerDialog'
const meta = {
title: 'General/EmojiPickerDialog',
component: EmojiPickerDialog,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof EmojiPickerDialog>
export default meta
type Story = StoryObj<typeof meta>
export const DefaultColors: Story = {
args: {emojiStyle: 'TWITTER'},
}

View File

@ -0,0 +1,66 @@
import EmojiPicker, {
EmojiStyle,
Theme,
EmojiClickData,
SuggestionMode,
Categories,
} from 'emoji-picker-react'
import { useState } from 'react'
import { Stack } from 'tamagui'
type EmojiStyleType = 'FACEBOOK' | 'APPLE' | 'GOOGLE' | 'TWITTER' | 'NATIVE'
function EmojiPickerDialog({ emojiStyle }: { emojiStyle: EmojiStyleType }) {
const [selectedEmoji, setSelectedEmoji] = useState<string>('')
console.log(selectedEmoji)
function onClick(emojiData: EmojiClickData) {
setSelectedEmoji(emojiData.unified)
}
return (
<Stack position="absolute" zIndex={1} left={120}>
{/* <XStack>
Your selected Emoji is:
{selectedEmoji ? (
<Emoji unified={selectedEmoji} emojiStyle={EmojiStyle.APPLE} size={22} />
) : null}
</XStack> */}
<EmojiPicker
onEmojiClick={onClick}
autoFocusSearch={false}
theme={Theme.AUTO}
height={350}
width="100%"
emojiVersion="1"
lazyLoadEmojis={false}
previewConfig={{ showPreview: false }}
suggestedEmojisMode={SuggestionMode.RECENT}
skinTonesDisabled
searchPlaceHolder="Search emojis"
emojiStyle={EmojiStyle[emojiStyle]}
categories={[
{
name: 'People',
category: Categories.SMILEYS_PEOPLE,
},
{ name: 'Animals and Nature', category: Categories.ANIMALS_NATURE },
{
name: 'Fun and Games',
category: Categories.ACTIVITIES,
},
{
name: 'Flags',
category: Categories.FLAGS,
},
{
name: 'Yum Yum',
category: Categories.FOOD_DRINK,
},
{ name: 'Objects', category: Categories.OBJECTS },
]}
/>
</Stack>
)
}
export default EmojiPickerDialog

View File

@ -4,7 +4,7 @@ import { RootState } from '../../redux/store'
function PinnedNotification() {
const pinnedMessage = useSelector((state: RootState) => state.pinnedMessage.pinnedMessage)
console.log(pinnedMessage)
return (
<>
{pinnedMessage && pinnedMessage.pinned && (

View File

@ -2,19 +2,52 @@ import { Tag } from '@status-im/components'
import { XStack } from 'tamagui'
import './TagContainer.css'
import { ConnectionIcon, AddSmallIcon, SwapIcon } from '@status-im/icons'
import { useNavigate } from 'react-router'
type TagContainerProps = {
selectedTag: 'pair' | 'create' | 'connect'
}
const TagContainer = ({ selectedTag }: TagContainerProps) => {
const navigate = useNavigate()
const onPressConnect = () => {
navigate('/connect-device')
}
const onPressPair = () => {
navigate('/pair-device')
}
const onPressCreate = () => {
navigate('/create-local-node')
}
return (
<XStack space={'$2'} alignItems="center" className="tag-container">
{selectedTag === 'connect' ? (
<Tag selected={selectedTag === 'connect'} icon={ConnectionIcon} label="Connect" size={32} />
<Tag
selected={selectedTag === 'connect'}
icon={ConnectionIcon}
label="Connect"
size={32}
onPress={onPressConnect}
/>
) : null}
<Tag selected={selectedTag === 'pair'} icon={SwapIcon} label="Pair" size={32} />
<Tag selected={selectedTag === 'create'} icon={AddSmallIcon} label="Create" size={32} />
<Tag
selected={selectedTag === 'pair'}
icon={SwapIcon}
label="Pair"
size={32}
onPress={onPressPair}
/>
<Tag
selected={selectedTag === 'create'}
icon={AddSmallIcon}
label="Create"
size={32}
onPress={onPressCreate}
/>
</XStack>
)
}

View File

@ -1,6 +1,7 @@
import { ReactNode } from 'react'
import './layout.css'
import NimbusLogoMark from '../Logos/NimbusLogoMark'
import { useTheme } from 'tamagui'
type PageWrapperShadowProps = {
breadcrumbBar?: ReactNode
@ -15,8 +16,10 @@ const PageWrapperShadow = ({
rightImageLogo,
children,
}: PageWrapperShadowProps) => {
const theme = useTheme()
return (
<div className="layout">
<div className="layout" style={{ backgroundColor: theme.background.val }}>
<section className="layout-left">
{breadcrumbBar}
<div className="container">

View File

@ -1,10 +1,14 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider as ReduxProvider } from 'react-redux'
import App from './App.tsx'
import './index.css'
import store from './redux/store.tsx'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
<ReduxProvider store={store}>
<App />
</ReduxProvider>
</React.StrictMode>,
)

View File

@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react'
import ConnectDevicePage from './ConnectDevicePage'
import { withRouter } from 'storybook-addon-react-router-v6'
const meta = {
title: 'Pages/ConnectDevicePage',
@ -10,6 +11,7 @@ const meta = {
},
tags: ['autodocs'],
argTypes: {},
decorators: [withRouter],
} satisfies Meta<typeof ConnectDevicePage>
export default meta

View File

@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react'
import CreateLocalNodePage from './CreateLocalNodePage'
import { withRouter } from 'storybook-addon-react-router-v6'
const meta = {
title: 'Pages/CreateLocalNodePage',
@ -10,6 +11,7 @@ const meta = {
},
tags: ['autodocs'],
argTypes: {},
decorators: [withRouter],
} satisfies Meta<typeof CreateLocalNodePage>
export default meta

View File

@ -7,9 +7,11 @@ import Header from '../../components/General/Header'
import Titles from '../../components/General/Titles'
import LabelInputField from '../../components/General/LabelInputField'
import ColorPicker from '../../components/General/ColorPicker'
import EmojiPickerDialog from '../../components/General/EmojiPickerDialog'
const CreateLocalNodePage = () => {
const [autoConnectChecked, setAutoConnectChecked] = useState(false)
const [isEmojiDialogOpen, setIsEmojiDialogOpen] = useState(false)
return (
<PageWrapperShadow rightImageSrc="./background-images/day-night-bg.png" rightImageLogo={true}>
@ -31,7 +33,18 @@ const CreateLocalNodePage = () => {
</Text>
<XStack my={10}>
<Avatar type="account" size={80} name="Device Avatar" />
<Avatar type="icon" size={32} icon={<ReactionIcon size={20} />} />
<Avatar
type="icon"
size={32}
icon={
<ReactionIcon
size={20}
onClick={() => setIsEmojiDialogOpen(prev => !prev)}
style={{ cursor: 'pointer' }}
/>
}
/>
{isEmojiDialogOpen && <EmojiPickerDialog emojiStyle="TWITTER" />}
</XStack>
</YStack>
<YStack>

View File

@ -1,4 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react'
import { withRouter } from 'storybook-addon-react-router-v6'
import LandingPage from './LandingPage'
@ -10,6 +11,7 @@ const meta = {
},
tags: ['autodocs'],
argTypes: {},
decorators: [withRouter],
} satisfies Meta<typeof LandingPage>
export default meta

View File

@ -6,8 +6,15 @@ import NimbusLogo from '../../components/Logos/NimbusLogo'
import { NodeIcon } from '@status-im/icons'
import { Button as StatusButton, Text } from '@status-im/components'
import QuickStartBar from '../../components/General/QuickStartBar/QuickStartBar'
import { useNavigate } from 'react-router'
const LandingPage = () => {
const navigate = useNavigate()
const getStartedHanlder = () => {
navigate('/pair-device')
}
function LandingPage() {
return (
<>
<PageWrapperShadow rightImageSrc="./background-images/landing-page-bg.png">
@ -15,17 +22,20 @@ function LandingPage() {
<XStack pt={'70px'}>
<NimbusLogo />
</XStack>
<YStack style={{ width: '100%', margin: '30vh 0 4vh' }}>
<Title>Light and performant clients, for all Ethereum validators.</Title>
<Text size={15} weight="regular">
<Title color="$textPrimary">
Light and performant clients, for all Ethereum validators.
</Title>
<Text size={15} weight="regular" color="$textPrimary">
<strong>Nimbus Nodes</strong> allows you to take control and ownership of the services
you wish to run in a completely trustless and decentralized manner.
</Text>
</YStack>
<XStack>
<StatusButton icon={<NodeIcon size={20} />}>Get Started</StatusButton>
<StatusButton icon={<NodeIcon size={20} />} onPress={getStartedHanlder}>
Get Started
</StatusButton>
</XStack>
</YStack>
</PageWrapperShadow>

View File

@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react'
import SyncStatus from './SyncStatus'
import { withRouter } from 'storybook-addon-react-router-v6'
const meta = {
title: 'Pair Device/SyncStatus',
@ -10,6 +11,7 @@ const meta = {
},
tags: ['autodocs'],
argTypes: {},
decorators: [withRouter],
} satisfies Meta<typeof SyncStatus>
export default meta

View File

@ -7,6 +7,7 @@ import Icon from '../../components/General/Icon'
import ConnectionIcon from '/icons/connection.svg'
import { convertSecondsToTimerFormat } from '../../utilities'
import { RefreshIcon } from '@status-im/icons'
import { useNavigate } from 'react-router'
type SyncStatusProps = {
isPairing: boolean
@ -20,11 +21,13 @@ const SyncStatus = ({
changeSetIsAwaitingPairing,
}: SyncStatusProps) => {
const [elapsedTime, setElapsedTime] = useState(0)
const navigate = useNavigate()
const resetTimer = () => {
setElapsedTime(0)
changeSetIsAwaitingPairing(false)
}
useEffect(() => {
let timer: ReturnType<typeof setTimeout>
@ -42,7 +45,11 @@ const SyncStatus = ({
return () => clearInterval(timer)
}, [isPairing, elapsedTime])
const timer = convertSecondsToTimerFormat(elapsedTime) // Assuming you've imported the convertSecondsToTimerFormat function
const timer = convertSecondsToTimerFormat(elapsedTime)
const connectViaIpHandler = () => {
navigate('/connect-device');
};
return (
<YStack space={'$2'}>
@ -84,7 +91,7 @@ const SyncStatus = ({
)}
{isAwaitingPairing && (
<XStack>
<Button icon={<Icon src={ConnectionIcon} />} size={40}>
<Button icon={<Icon src={ConnectionIcon} />} size={40} onPress={connectViaIpHandler} >
Connect via IP
</Button>
</XStack>

View File

@ -1,11 +1,13 @@
import { configureStore } from '@reduxjs/toolkit'
import deviceHealthReducer from './deviceHealthCheck/slice'
import pinnedMessageReducer from './PinnedMessage/slice'
import themeReducer from './theme/slice'
const store = configureStore({
reducer: {
deviceHealth: deviceHealthReducer,
pinnedMessage: pinnedMessageReducer,
theme: themeReducer,
},
})

17
src/redux/theme/slice.ts Normal file
View File

@ -0,0 +1,17 @@
import { createSlice } from '@reduxjs/toolkit'
const initialState: 'light' | 'dark' = 'light'
const themeSlice = createSlice({
name: 'theme',
initialState,
reducers: {
setTheme: (_, action) => {
return action.payload
},
},
})
export const { setTheme } = themeSlice.actions
export default themeSlice.reducer

1085
yarn.lock

File diff suppressed because it is too large Load Diff