diff --git a/apps/website/public/assets/jobs/codex.png b/apps/website/public/assets/jobs/codex.png new file mode 100644 index 00000000..03e6e9f4 Binary files /dev/null and b/apps/website/public/assets/jobs/codex.png differ diff --git a/apps/website/public/assets/jobs/logos.png b/apps/website/public/assets/jobs/logos.png new file mode 100644 index 00000000..3c8e0386 Binary files /dev/null and b/apps/website/public/assets/jobs/logos.png differ diff --git a/apps/website/public/assets/jobs/nimbus.png b/apps/website/public/assets/jobs/nimbus.png new file mode 100644 index 00000000..24c9c59e Binary files /dev/null and b/apps/website/public/assets/jobs/nimbus.png differ diff --git a/apps/website/public/assets/jobs/vac.png b/apps/website/public/assets/jobs/vac.png new file mode 100644 index 00000000..ce90b890 Binary files /dev/null and b/apps/website/public/assets/jobs/vac.png differ diff --git a/apps/website/public/assets/jobs/waku.png b/apps/website/public/assets/jobs/waku.png new file mode 100644 index 00000000..3c8e0386 Binary files /dev/null and b/apps/website/public/assets/jobs/waku.png differ diff --git a/apps/website/src/components/feature-list.tsx b/apps/website/src/components/feature-list.tsx index 0b9c2097..2f42d344 100644 --- a/apps/website/src/components/feature-list.tsx +++ b/apps/website/src/components/feature-list.tsx @@ -7,7 +7,7 @@ import type { illustrations } from '@/config/illustrations' type Item = { title: string description: string - image: (typeof illustrations)[keyof typeof illustrations] + icon: (typeof illustrations)[keyof typeof illustrations] } type Props = { @@ -18,15 +18,15 @@ type Props = { export const FeatureList = (props: Props) => { const { list, dark = false } = props return ( -
+
- {list.map(({ title, image, description }, i) => ( + {list.map(({ title, icon, description }, i) => (
- {image.alt} + {icon.alt}
diff --git a/apps/website/src/config/routes.ts b/apps/website/src/config/routes.ts index 8ca80c1d..838d3823 100644 --- a/apps/website/src/config/routes.ts +++ b/apps/website/src/config/routes.ts @@ -38,7 +38,7 @@ export const ROUTES = { { name: 'Blog', href: '/blog' }, { name: 'Translations', href: '/' }, // { name: 'Community groups', href: '/' }, - { name: 'Jobs', href: '/' }, + { name: 'Jobs', href: '/jobs' }, ], Developers: [ { name: 'Repos', href: 'https://github.com/status-im' }, diff --git a/apps/website/src/pages/features/create-community.tsx b/apps/website/src/pages/features/create-community.tsx index c96a73b8..0b449324 100644 --- a/apps/website/src/pages/features/create-community.tsx +++ b/apps/website/src/pages/features/create-community.tsx @@ -35,37 +35,37 @@ const FEATURE_LIST = [ title: 'Decentralized', description: 'Communities are literally powered by their members running the Status Desktop app.', - image: illustrations.doge, + icon: illustrations.doge, }, { title: 'Permissionless', description: 'Nobody can stop you creating a community, because nobody controls Status’ p2p network.', - image: illustrations.mushroom, + icon: illustrations.mushroom, }, { title: 'Self-sovereign', description: 'Each community can set it’s own rules, whatever they are. And is responsible for it’s own actions. ', - image: illustrations.hand, + icon: illustrations.hand, }, { title: '100% Free to use', description: 'No paid tier. No artificially imposed limits. No commission charged on community token sales.', - image: illustrations.duck, + icon: illustrations.duck, }, { title: '100% Open source', description: 'Status itself is a community project. Anyone can build, contribute to and fork it’s source code.', - image: illustrations.flower, + icon: illustrations.flower, }, { title: '100% Freedom', description: 'Status’s mission is to protect free speech, uphold human rights and defend privacy.', - image: illustrations.megaphone, + icon: illustrations.megaphone, }, ] diff --git a/apps/website/src/pages/jobs/[slug].tsx b/apps/website/src/pages/jobs/[slug].tsx new file mode 100644 index 00000000..d8df99b9 --- /dev/null +++ b/apps/website/src/pages/jobs/[slug].tsx @@ -0,0 +1,154 @@ +import { Button, Tag, Text } from '@status-im/components' +import { cx } from 'class-variance-authority' + +import { Breadcrumbs } from '@/components/breadcrumbs' +import { AppLayout, Content } from '@/layouts/app-layout' + +import type { BreadcrumbsProps } from '@/components/breadcrumbs' +import type { GetStaticPaths, GetStaticProps, Page } from 'next' +import type React from 'react' + +type Params = { slug: string } + +const SLUG = 'senior-react-native-ui-developer' + +export const getStaticPaths: GetStaticPaths = async () => { + return { + paths: [ + { + params: { + slug: SLUG, + }, + }, + ], + fallback: false, + } +} + +export const getStaticProps: GetStaticProps = async () => { + // root + const breadcrumbs = [ + { + label: 'Jobs', + // TODO: typesafe + href: '/jobs', + }, + { + label: 'Senior React Native UI Developer', + href: `/jobs/${SLUG}`, + }, + ] + + return { + props: { + breadcrumbs, + }, + } +} + +type Props = { + breadcrumbs: BreadcrumbsProps['items'] +} + +const JobsDetailPage: Page = props => { + const { breadcrumbs } = props + + return ( + + + +
+
+ +

+ Senior React Native UI Developer +

+ + Full time, Remote (Worldwide) + +
+ + {/* CONTENT */} +
+ + The role We’re growing our mobile development team. Join us in + building a fully decentralized, censorship resistant, privacy first + group chat platform that leverages the Ethereum blockchain to enable + individuals and groups worldwide to communicate and transact with + one another freely without restriction. Status is looking for an UI + Engineer to join our mobile development team who will work closely + with Design to transform UI specifications into beautiful, smooth, + performant and near pixel perfect interactive interfaces. The ideal + person will be comfortable working on features end-to-end, has an + eye for design and visual detail, enjoys working with designers as + well as developers, and who can transform reused UI patterns such as + list items into reusable UI components. +
+
+ The ideal candidate will love finessing UI implementations to the + highest levels of quality and will care deeply about things like + code cleanliness, reusability, maintainability, performance, and UI + layout accuracy - as well as doing whatever needed to create a + best-in-class user experience for Status’s users. Willingness to + both learn and share your knowledge with others, + product-orientation, and making sure all team members are aligned + when developing new features, will make you successful in the role. + Status is a fast-paced, flat organization, working on cutting edge + blockchain and decentralized messaging technologies in a dynamic + landscape. We look forward to meeting you! +
+ + + The role We’re growing our mobile development team. Join us in + building a fully decentralized, censorship resistant, privacy first + group chat platform that leverages the Ethereum blockchain to enable + individuals and groups worldwide to communicate and transact with + one another freely without restriction. Status is looking for an UI + Engineer to join our mobile development team who will work closely + with Design to transform UI specifications into beautiful, smooth, + performant and near pixel perfect interactive interfaces. The ideal + person will be comfortable working on features end-to-end, has an + eye for design and visual detail, enjoys working with designers as + well as developers, and who can transform reused UI patterns such as + list items into reusable UI components. +
+
+ The ideal candidate will love finessing UI implementations to the + highest levels of quality and will care deeply about things like + code cleanliness, reusability, maintainability, performance, and UI + layout accuracy - as well as doing whatever needed to create a + best-in-class user experience for Status’s users. Willingness to + both learn and share your knowledge with others, + product-orientation, and making sure all team members are aligned + when developing new features, will make you successful in the role. + Status is a fast-paced, flat organization, working on cutting edge + blockchain and decentralized messaging technologies in a dynamic + landscape. We look forward to meeting you! +
+
+ + {/* FOOTER */} +
+
+ + Apply for this job + + Submit your application here +
+ +
+
+
+ ) +} + +JobsDetailPage.getLayout = function getLayout(page) { + return {page} +} + +export default JobsDetailPage diff --git a/apps/website/src/pages/jobs/index.tsx b/apps/website/src/pages/jobs/index.tsx new file mode 100644 index 00000000..b1418655 --- /dev/null +++ b/apps/website/src/pages/jobs/index.tsx @@ -0,0 +1,276 @@ +import { useRef } from 'react' + +// import codexImage from '@assets/jobs/codex.png' +import logosImage from '@assets/jobs/logos.png' +// import nimbusImage from '@assets/jobs/nimbus.png' +// import vacImage from '@assets/jobs/vac.png' +// import wakuImage from '@assets/jobs/waku.png' +import { Button, Tag, Text } from '@status-im/components' +import { ArrowDownIcon, ArrowRightIcon, ExternalIcon } from '@status-im/icons' +import Image from 'next/image' +import Link from 'next/link' + +import { FeatureList } from '@/components/feature-list' +import { illustrations } from '@/config/illustrations' +import { AppLayout, Content } from '@/layouts/app-layout' + +import type { Page } from 'next' + +// TODO FIX PHOTOS +// TODO FIX FEATURE LIST +// TODO ADD CIRCLES +// TODO CONNECT TO GREENHOUSE API (https://developers.greenhouse.io/job-board.html#retrieve-job-board) + +const OPEN_JOBS = 82 + +const JobsPage: Page = () => { + const openRolesRef = useRef(null) + + const handleScrollToOpenings = () => { + openRolesRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + + return ( + +
+
+ +
+

+ Join the +
+ decentralized +
+ movement +

+ +
+
+ + We’re hiring! + + {OPEN_JOBS} remote openings +
+ +
+
+ + {/* PHOTOS */} +
+ +
+
+ + +
+ +
+
+ +
+
+ + Status strives to develop open source software that can be used as a + secure communication tool that upholds human rights. We are building + the tools and infrastructure for the advancement of a secure, + private, and open web3. Our core software, Status, is an open + source, Ethereum-based software that gives users the power to chat, + transact, and access a revolutionary world of DApps on the + decentralized web. With the high level goals of preserving the right + to privacy, protecting messages from third parties, and safely + sending, storing and receiving cryptocurrencies with you being the + only one who holds the keys to your funds, Status is providing the + tools and infrastructure to facilitate the creation of communities, + where anyone is welcome to create, join and contribute. +
+
+ 150+ passionate individuals (known as core-contributors) work + together all around the world to facilitate Status’ mission. We care + deeply about our mission, so we have created an environment which + allows core-contributors to complete their work with freedom and + flexibility. We regularly survey the core contributors to gather + insights on their satisfaction with contributing to Status & the + most common answers regarding why they’re satisfied are: our + mission, values, flexibility, friendly environment, non-corporate + culture and our entrepreneurial spirit. +
+
+
+ +
+
+

Perks and benefits

+
+ +
+ +
+
+

Open roles

+
+ +
+ {/* JOB GROUPS */} + {[1, 2, 3].map(i => ( +
+
+

Design

+
+
+ {[ + 'Product designer', + 'Visual designer', + 'Motion designer', + 'Illustrator', + ].map(position => ( + // TODO: discuss hover state + + + {position} + +
+ Full-time +
+ + ))} +
+
+ ))} +
+
+ + {/* TODO FIX TEXTURE */} +
+
+
+

+ Open roles +
+ in our network +

+
+
+ {['Logos', 'Codex', 'Waku', 'Nimbus', 'Vac'].map(name => ( +
+
+ {`${name} + + {name} + +
+ {[1, 2, 3, 4, 5, 6].map(i => ( + + + Protocol Research Engineer + + + + ))} +
+ ))} +
+
+
+
+ ) +} + +const PERKS = [ + { + title: 'Fully remote', + description: 'Work from wherever you want, whenever you want. ', + icon: illustrations.doge, + }, + { + title: 'Unlimited vacation', + description: + 'Take all the time off you need. We need you in your best self.', + icon: illustrations.doge, + }, + { + title: 'Paid team offsites', + description: 'We meet at least once a year somewhere across the world. ', + icon: illustrations.doge, + }, + { + title: 'Hardware stipend', + description: 'Need a laptop to work? We got you covered.', + icon: illustrations.doge, + }, + { + title: 'Co-working stipend', + description: + 'We offer a $250 stipend for you to work from a co-working space.', + icon: illustrations.doge, + }, + { + title: 'Get paid in crypto', + description: 'We offer to pay salaries in SNT, ETH, USDC or fiat.', + icon: illustrations.doge, + }, + { + title: 'SNT Bonus', + description: 'We reward hard work and longevity with a bonus in SNT. ', + icon: illustrations.doge, + }, + { + title: 'Referral fee', + description: + 'We offer a $5000 SNT referral fee so bring your friends aboard.', + icon: illustrations.doge, + }, + { + title: 'Headspace subscription', + description: + 'Take care of your mind with a premium headspace account, for free.', + icon: illustrations.doge, + }, +] + +JobsPage.getLayout = function getLayout(page) { + return {page} +} + +export default JobsPage diff --git a/apps/website/src/pages/keycard.tsx b/apps/website/src/pages/keycard.tsx index 2ae86ba8..f1965c7c 100644 --- a/apps/website/src/pages/keycard.tsx +++ b/apps/website/src/pages/keycard.tsx @@ -132,36 +132,36 @@ const FEATURE_LIST = [ title: 'Top tier security', description: 'Our hardware security successfully passed Common Criteria EAL6+ certification ', - image: illustrations.doge, + icon: illustrations.doge, }, { title: 'Keys stored in the card', description: 'It’s impossible for Status or any government to extract your private keys from keycard', - image: illustrations.mushroom, + icon: illustrations.mushroom, }, { title: 'Feature 3', description: 'Here will say something more about security and how kc is revolutionary when it comes to security', - image: illustrations.hand, + icon: illustrations.hand, }, { title: 'Mobile friendly', description: 'Keycard is using NFC which is natively embedded in all mobile phones', - image: illustrations.duck, + icon: illustrations.duck, }, { title: 'No need to charge', description: 'Keycard has no battery so it’s always ready to go.', - image: illustrations.flower, + icon: illustrations.flower, }, { title: 'Easy to carry around', description: 'Its credit card form factor is small and convenient to fit in any wallet.', - image: illustrations.megaphone, + icon: illustrations.megaphone, }, ] diff --git a/apps/website/src/styles/global.css b/apps/website/src/styles/global.css index 96817d4e..1ef4927b 100644 --- a/apps/website/src/styles/global.css +++ b/apps/website/src/styles/global.css @@ -6,6 +6,9 @@ .container { @apply mx-auto max-w-[1504px] px-5 lg:px-40; } + .border-dashed-default { + @apply border-dashed border-neutral-80/20; + } } @keyframes gradient { diff --git a/packages/components/src/button/button.tsx b/packages/components/src/button/button.tsx index 2b766d62..77f49172 100644 --- a/packages/components/src/button/button.tsx +++ b/packages/components/src/button/button.tsx @@ -1,14 +1,13 @@ import { cloneElement, forwardRef } from 'react' import { styled } from '@tamagui/core' -import { Pressable } from 'react-native' +import { View } from 'react-native' import { Text } from '../text' import type { TextProps } from '../text' import type { GetVariants, MapVariant, PressableProps } from '../types' import type { Ref } from 'react' -import type { View } from 'react-native' type Variants = GetVariants @@ -94,10 +93,9 @@ const _Button = forwardRef(Button) export { _Button as Button } export type { Props as ButtonProps } -const Base = styled(Pressable, { - tag: 'button', +const Base = styled(View, { name: 'Button', - accessibilityRole: 'button', + role: 'button', display: 'flex', flexDirection: 'row', diff --git a/packages/components/src/dynamic-button/dynamic-button.tsx b/packages/components/src/dynamic-button/dynamic-button.tsx index 8c11c098..b495eff1 100644 --- a/packages/components/src/dynamic-button/dynamic-button.tsx +++ b/packages/components/src/dynamic-button/dynamic-button.tsx @@ -2,7 +2,7 @@ import { forwardRef } from 'react' import { ArrowDownIcon, MentionIcon } from '@status-im/icons' import { styled } from '@tamagui/core' -import { Pressable } from 'react-native' +import { View } from 'react-native' import { Shadow } from '../shadow' import { Text } from '../text' @@ -10,7 +10,6 @@ import { Text } from '../text' import type { GetVariants, PressableProps } from '../types' import type { ColorTokens } from '@tamagui/core' import type { Ref } from 'react' -import type { View } from 'react-native' type Variants = GetVariants @@ -50,10 +49,9 @@ const _DynamicButton = forwardRef(DynamicButton) export { _DynamicButton as DynamicButton } export type { Props as DynamicButtonProps } -const Button = styled(Pressable, { +const Button = styled(View, { name: 'DynamicButton', - tag: 'button', - accessibilityRole: 'button', + role: 'button', cursor: 'pointer', userSelect: 'none', diff --git a/packages/components/src/hooks/use-pressable-colors.tsx b/packages/components/src/hooks/use-pressable-colors.tsx index 8d4177f8..f9792a3e 100644 --- a/packages/components/src/hooks/use-pressable-colors.tsx +++ b/packages/components/src/hooks/use-pressable-colors.tsx @@ -1,7 +1,6 @@ import { useState } from 'react' import type { PressableProps } from '../types' -import type { MouseEvent } from 'react-native' import type { ColorTokens } from 'tamagui' type Config = { @@ -49,20 +48,20 @@ export const usePressableColors = ( return { color: styles[key], pressableProps: { - onHoverIn: event => { - props.onHoverIn?.(event as unknown as MouseEvent) + onHoverIn: (...args) => { + props.onHoverIn?.(...args) setHovered(true) }, - onHoverOut: event => { - props.onHoverOut?.(event as unknown as MouseEvent) + onHoverOut: (...args) => { + props.onHoverOut?.(...args) setHovered(false) }, - onPressIn: event => { - props.onPressIn?.(event) + onPressIn: (...args) => { + props.onPressIn?.(...args) setPressed(true) }, - onPressOut: event => { - props.onPressOut?.(event) + onPressOut: (...args) => { + props.onPressOut?.(...args) setPressed(false) }, } as const, diff --git a/packages/components/src/icon-button/icon-button.tsx b/packages/components/src/icon-button/icon-button.tsx index 81679000..7cb4f4c0 100644 --- a/packages/components/src/icon-button/icon-button.tsx +++ b/packages/components/src/icon-button/icon-button.tsx @@ -1,13 +1,12 @@ import { cloneElement, forwardRef } from 'react' -import { Pressable } from 'react-native' +import { View } from 'react-native' import { styled } from 'tamagui' import { usePressableColors } from '../hooks/use-pressable-colors' import type { GetVariants, PressableProps } from '../types' import type { Ref } from 'react' -import type { View } from 'react-native' type Variants = GetVariants @@ -63,10 +62,9 @@ const _IconButton = forwardRef(IconButton) export { _IconButton as IconButton } export type { Props as IconButtonProps } -const Base = styled(Pressable, { +const Base = styled(View, { name: 'IconButton', - tag: 'button', - accessibilityRole: 'button', + role: 'button', cursor: 'pointer', userSelect: 'none', diff --git a/packages/components/src/input/input.tsx b/packages/components/src/input/input.tsx index 0c9ea4d2..0b7a0712 100644 --- a/packages/components/src/input/input.tsx +++ b/packages/components/src/input/input.tsx @@ -106,7 +106,6 @@ const _Input = (props: Props, ref: Ref) => { {Boolean(onClear) && !!value && ( @@ -117,8 +116,9 @@ Tabs.Content = Content export { Tabs } export type { Props as TabsProps } -const TriggerBase = styled(Pressable, { - tag: 'button', +const TriggerBase = styled(View, { + name: 'Trigger', + role: 'button', flexDirection: 'row', alignItems: 'center', diff --git a/packages/components/src/tag/tag.tsx b/packages/components/src/tag/tag.tsx index bc9d4b09..e79bee56 100644 --- a/packages/components/src/tag/tag.tsx +++ b/packages/components/src/tag/tag.tsx @@ -1,6 +1,7 @@ import { createElement } from 'react' -import { Stack, styled } from '@tamagui/core' +import { styled } from '@tamagui/core' +import { View } from 'react-native' import { Text } from '../text' import { getCustomStyles } from './get-custom-styles' @@ -9,6 +10,7 @@ import type { TextProps } from '../text' import type { IconProps } from '@status-im/icons' import type { ColorTokens } from '@tamagui/core' import type { ComponentType } from 'react' +import type { PressableProps } from 'react-native' type Props = { size: 32 | 24 @@ -16,7 +18,7 @@ type Props = { label?: string selected?: boolean disabled?: boolean - onPress?: () => void + onPress?: PressableProps['onPress'] color?: ColorTokens | `#${string}` } @@ -54,8 +56,11 @@ const Tag = (props: Props) => { selected={selected} disabled={disabled} iconOnly={Boolean(icon && !label)} - onPress={() => onPress?.()} {...getCustomStyles(props)} + {...(onPress && { + role: 'button', + onPress, + })} > {renderIcon()} {label && ( @@ -70,11 +75,10 @@ const Tag = (props: Props) => { export { Tag } export type { Props as TagProps } -const Base = styled(Stack, { - tag: 'tag', +const Base = styled(View, { name: 'Tag', - accessibilityRole: 'button', + userSelect: 'none', display: 'flex', flexDirection: 'row', alignItems: 'center', @@ -85,7 +89,6 @@ const Base = styled(Stack, { backgroundColor: '$white-100', animation: 'fast', - cursor: 'pointer', hoverStyle: { borderColor: '$neutral-30', diff --git a/packages/components/src/types.ts b/packages/components/src/types.ts index 070b496b..66da6448 100644 --- a/packages/components/src/types.ts +++ b/packages/components/src/types.ts @@ -8,12 +8,12 @@ import type { import type { PressableProps as NativePressableProps } from 'react-native' type PressableProps = { - onHoverIn?: Exclude - onHoverOut?: NativePressableProps['onHoverOut'] - onPress?: NativePressableProps['onPress'] - onPressIn?: NativePressableProps['onPressIn'] - onPressOut?: NativePressableProps['onPressOut'] - onLongPress?: NativePressableProps['onLongPress'] + onHoverIn?: VoidFunction + onHoverOut?: VoidFunction + onPress?: VoidFunction + onPressIn?: VoidFunction + onPressOut?: VoidFunction + onLongPress?: VoidFunction delayHoverIn?: NativePressableProps['delayHoverIn'] delayHoverOut?: NativePressableProps['delayHoverOut'] delayLongPress?: NativePressableProps['delayLongPress']