diff --git a/crabfit-frontend/package.json b/crabfit-frontend/package.json index 0ce5dd3..4e2cfe0 100644 --- a/crabfit-frontend/package.json +++ b/crabfit-frontend/package.json @@ -22,7 +22,7 @@ "i18next-http-backend": "^1.2.4", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-hook-form": "^6.15.4", + "react-hook-form": "^7.8.1", "react-i18next": "^11.8.15", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", diff --git a/crabfit-frontend/public/i18n/en/home.json b/crabfit-frontend/public/i18n/en/home.json index fce306a..98db888 100644 --- a/crabfit-frontend/public/i18n/en/home.json +++ b/crabfit-frontend/public/i18n/en/home.json @@ -48,10 +48,13 @@ "availabilities": "Availabilities entered", "content": { "p1": "Crab Fit helps you fit your event around everyone's schedules. Simply create an event above and send the link to everyone that is participating. Results update live and you will be able to see a heat-map of when everyone is free.<1/><2>Learn more about how to Crab Fit2>.", - "p2": "Create a lot of Crab Fits? Get the <1>Chrome extension1> or <3>Firefox extension3> for your browser! You can also download the <5>Android app5> to Crab Fit on the go.", "p3": "Created by <1>Ben Grant1>, Crab Fit is the modern-day solution to your group event planning debates.", "p4": "The code for Crab Fit is open source, if you find any issues or want to contribute, you can visit the <1>repository1>. By using Crab Fit you agree to the <3>privacy policy3>.", - "p5": "Consider donating below if it helped you out so it can stay free for everyone. 🦀" - } + "p5": "Consider donating below if it helped you out so Crab Fit can stay free for everyone. 🦀" + }, + "chrome_extension": "Get the Chrome Extension", + "firefox_extension": "Get the Firefox Extension", + "safari_extension": "Get the Safari Extension", + "android_app": "Download the Android app" } } diff --git a/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts b/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts index c499db6..330b3cc 100644 --- a/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts +++ b/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts @@ -206,7 +206,7 @@ export const Person = styled.button` font: inherit; font-size: 15px; border-radius: 3px; - border: 2px solid ${props => props.theme.text}; + border: 1px solid ${props => props.theme.text}; color: ${props => props.theme.text}; font-weight: 500; background: transparent; diff --git a/crabfit-frontend/src/components/Button/Button.tsx b/crabfit-frontend/src/components/Button/Button.tsx index b17ee4e..19c40b7 100644 --- a/crabfit-frontend/src/components/Button/Button.tsx +++ b/crabfit-frontend/src/components/Button/Button.tsx @@ -1,13 +1,16 @@ import { Pressable } from './buttonStyle'; -const Button = ({ href, type = 'button', ...props }) => ( +const Button = ({ href, type = 'button', icon, children, ...props }) => ( + > + {icon} + {children} + ); export default Button; diff --git a/crabfit-frontend/src/components/Button/buttonStyle.ts b/crabfit-frontend/src/components/Button/buttonStyle.ts index 8cce43f..766769a 100644 --- a/crabfit-frontend/src/components/Button/buttonStyle.ts +++ b/crabfit-frontend/src/components/Button/buttonStyle.ts @@ -20,6 +20,12 @@ export const Pressable = styled.button` transform-style: preserve-3d; margin-bottom: 5px; + & svg, & img { + height: 1.2em; + width: 1.2em; + margin-right: .5em; + } + ${props => props.size && ` padding: 0; height: ${props.size}; @@ -39,7 +45,7 @@ export const Pressable = styled.button` transition: transform 150ms cubic-bezier(0, 0, 0.58, 1), box-shadow 150ms cubic-bezier(0, 0, 0.58, 1); } - &:hover { + &:hover, &:focus { transform: translate(0, 1px); &::before { transform: translate3d(0, 4px, -1em); @@ -102,7 +108,7 @@ export const Pressable = styled.button` } `} - ${props => props.alt && ` + ${props => props.secondary && ` background: transparent; border: 1px solid ${props.primaryColor || props.theme.mode === 'light' ? props.theme.primaryDark : props.theme.primaryLight}; color: ${props.primaryColor || props.theme.mode === 'light' ? props.theme.primaryDark : props.theme.primaryLight}; @@ -111,7 +117,7 @@ export const Pressable = styled.button` &::before { content: none; } - &:hover, &:active { + &:hover, &:active, &:focus { transform: none; } `} diff --git a/crabfit-frontend/src/components/CalendarField/CalendarField.tsx b/crabfit-frontend/src/components/CalendarField/CalendarField.tsx index 5957d0a..1927aa6 100644 --- a/crabfit-frontend/src/components/CalendarField/CalendarField.tsx +++ b/crabfit-frontend/src/components/CalendarField/CalendarField.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef, forwardRef } from 'react'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; import isToday from 'dayjs/plugin/isToday'; @@ -47,13 +47,13 @@ const calculateMonth = (month, year, weekStart) => { return dates; }; -const CalendarField = ({ +const CalendarField = forwardRef(({ label, subLabel, id, - register, + setValue, ...props -}) => { +}, ref) => { const weekStart = useSettingsStore(state => state.weekStart); const locale = useLocaleUpdateStore(state => state.locale); const { t } = useTranslation('home'); @@ -88,6 +88,8 @@ const CalendarField = ({ _setMode(newMode); }; + useEffect(() => setValue(props.name, type ? JSON.stringify(selectedDays) : JSON.stringify(selectedDates)), [type, selectedDays, selectedDates, setValue, props.name]); + useEffect(() => { if (dayjs.Ls.hasOwnProperty(locale) && weekStart !== dayjs.Ls[locale].weekStart) { dayjs.updateLocale(locale, { weekStart }); @@ -102,7 +104,7 @@ const CalendarField = ({ @@ -211,8 +213,8 @@ const CalendarField = ({ {(weekStart ? [...dayjs.weekdaysShort().filter((_,i) => i !== 0), dayjs.weekdaysShort()[0]] : dayjs.weekdaysShort()).map((name, i) => i !== 0), dayjs.weekdaysShort()[0]] : dayjs.weekdaysShort())[dayjs().day()-weekStart] === name} - title={(weekStart ? [...dayjs.weekdaysShort().filter((_,i) => i !== 0), dayjs.weekdaysShort()[0]] : dayjs.weekdaysShort())[dayjs().day()-weekStart] === name ? t('form.dates.tooltips.today') : ''} + isToday={(weekStart ? [...dayjs.weekdaysShort().filter((_,i) => i !== 0), dayjs.weekdaysShort()[0]] : dayjs.weekdaysShort())[dayjs().day()-weekStart === -1 ? 6 : dayjs().day()-weekStart] === name} + title={(weekStart ? [...dayjs.weekdaysShort().filter((_,i) => i !== 0), dayjs.weekdaysShort()[0]] : dayjs.weekdaysShort())[dayjs().day()-weekStart === -1 ? 6 : dayjs().day()-weekStart] === name ? t('form.dates.tooltips.today') : ''} selected={selectedDays.includes(((i + weekStart) % 7 + 7) % 7)} selecting={selectingDays.includes(((i + weekStart) % 7 + 7) % 7)} mode={mode} @@ -257,6 +259,6 @@ const CalendarField = ({ )} ); -}; +}); export default CalendarField; diff --git a/crabfit-frontend/src/components/Donate/Donate.tsx b/crabfit-frontend/src/components/Donate/Donate.tsx index 69d352d..05581a5 100644 --- a/crabfit-frontend/src/components/Donate/Donate.tsx +++ b/crabfit-frontend/src/components/Donate/Donate.tsx @@ -18,7 +18,6 @@ const Donate = () => { const { t } = useTranslation('common'); const firstLinkRef = useRef(); - const buttonRef = useRef(); const modalRef = useRef(); const [isOpen, _setIsOpen] = useState(false); @@ -117,7 +116,6 @@ const Donate = () => { href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD&amount=5" target="_blank" rel="noreferrer" - ref={buttonRef} style={{ whiteSpace: 'nowrap' }} >{t('donate.button')} diff --git a/crabfit-frontend/src/components/Error/Error.tsx b/crabfit-frontend/src/components/Error/Error.tsx index 6cf8770..0d51039 100644 --- a/crabfit-frontend/src/components/Error/Error.tsx +++ b/crabfit-frontend/src/components/Error/Error.tsx @@ -6,7 +6,7 @@ const Error = ({ open = true, ...props }) => ( - + {children} diff --git a/crabfit-frontend/src/components/GoogleCalendar/GoogleCalendar.tsx b/crabfit-frontend/src/components/GoogleCalendar/GoogleCalendar.tsx index 2ec5bf0..a210359 100644 --- a/crabfit-frontend/src/components/GoogleCalendar/GoogleCalendar.tsx +++ b/crabfit-frontend/src/components/GoogleCalendar/GoogleCalendar.tsx @@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next'; import { Button, Center } from 'components'; import { Loader } from '../Loading/loadingStyle'; import { - LoginButton, CalendarList, CheckboxInput, CheckboxLabel, @@ -101,11 +100,10 @@ const GoogleCalendar = ({ timeZone, timeMin, timeMax, onImport }) => { onClick={() => signIn()} isLoading={signedIn === undefined} primaryColor="#4286F5" - secondaryColor="#3367BD"> - - - {t('event:you.google_cal.login')} - + secondaryColor="#3367BD" + icon={} + > + {t('event:you.google_cal.login')} ) : ( diff --git a/crabfit-frontend/src/components/GoogleCalendar/googleCalendarStyle.ts b/crabfit-frontend/src/components/GoogleCalendar/googleCalendarStyle.ts index 2afc26f..ca7c0f8 100644 --- a/crabfit-frontend/src/components/GoogleCalendar/googleCalendarStyle.ts +++ b/crabfit-frontend/src/components/GoogleCalendar/googleCalendarStyle.ts @@ -1,16 +1,5 @@ import styled from '@emotion/styled'; -export const LoginButton = styled.div` - display: flex; - align-items: center; - justify-content: center; - - & img { - height: 1em; - margin-right: .8em; - } -`; - export const CalendarList = styled.div` width: 100%; & > div { diff --git a/crabfit-frontend/src/components/Legend/legendStyle.ts b/crabfit-frontend/src/components/Legend/legendStyle.ts index 1542359..7ca7f67 100644 --- a/crabfit-frontend/src/components/Legend/legendStyle.ts +++ b/crabfit-frontend/src/components/Legend/legendStyle.ts @@ -28,7 +28,7 @@ export const Bar = styled.div` border-radius: 3px; overflow: hidden; margin: 0 8px; - border: 2px solid ${props => props.theme.text}; + border: 1px solid ${props => props.theme.text}; @media (max-width: 400px) { width: 100%; diff --git a/crabfit-frontend/src/components/OutlookCalendar/OutlookCalendar.tsx b/crabfit-frontend/src/components/OutlookCalendar/OutlookCalendar.tsx index 2edcf88..c2eeae5 100644 --- a/crabfit-frontend/src/components/OutlookCalendar/OutlookCalendar.tsx +++ b/crabfit-frontend/src/components/OutlookCalendar/OutlookCalendar.tsx @@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next'; import { Button, Center } from 'components'; import { Loader } from '../Loading/loadingStyle'; import { - LoginButton, CalendarList, CheckboxInput, CheckboxLabel, @@ -168,11 +167,10 @@ const OutlookCalendar = ({ timeZone, timeMin, timeMax, onImport }) => { onClick={() => signIn()} isLoading={client === undefined} primaryColor="#0364B9" - secondaryColor="#02437D"> - - - {t('event:you.outlook_cal')} - + secondaryColor="#02437D" + icon={} + > + {t('event:you.outlook_cal')} ) : ( diff --git a/crabfit-frontend/src/components/Recents/recentsStyle.ts b/crabfit-frontend/src/components/Recents/recentsStyle.ts index 5b4ca72..4d1e465 100644 --- a/crabfit-frontend/src/components/Recents/recentsStyle.ts +++ b/crabfit-frontend/src/components/Recents/recentsStyle.ts @@ -2,7 +2,6 @@ import styled from '@emotion/styled'; export const Recent = styled.a` text-decoration: none; - color: inherit; display: flex; align-items: center; justify-content: space-between; @@ -20,6 +19,7 @@ export const Recent = styled.a` font-weight: 400; opacity: .8; white-space: nowrap; + color: ${props => props.theme.text}; } &:hover .name { diff --git a/crabfit-frontend/src/components/SelectField/SelectField.tsx b/crabfit-frontend/src/components/SelectField/SelectField.tsx index f6ff1c1..8771c72 100644 --- a/crabfit-frontend/src/components/SelectField/SelectField.tsx +++ b/crabfit-frontend/src/components/SelectField/SelectField.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from 'react'; import { Wrapper, StyledLabel, @@ -5,7 +6,7 @@ import { StyledSelect, } from './selectFieldStyle'; -const SelectField = ({ +const SelectField = forwardRef(({ label, subLabel, id, @@ -13,17 +14,16 @@ const SelectField = ({ inline = false, small = false, defaultOption, - register, ...props -}) => ( +}, ref) => ( {label && {label}} {subLabel && {subLabel}} {defaultOption && {defaultOption}} @@ -38,6 +38,6 @@ const SelectField = ({ )} -); +)); export default SelectField; diff --git a/crabfit-frontend/src/components/TextField/TextField.tsx b/crabfit-frontend/src/components/TextField/TextField.tsx index 97602b5..e096583 100644 --- a/crabfit-frontend/src/components/TextField/TextField.tsx +++ b/crabfit-frontend/src/components/TextField/TextField.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from 'react'; import { Wrapper, StyledLabel, @@ -5,19 +6,18 @@ import { StyledInput, } from './textFieldStyle'; -const TextField = ({ +const TextField = forwardRef(({ label, subLabel, id, inline = false, - register, ...props -}) => ( +}, ref) => ( {label && {label}} {subLabel && {subLabel}} - + -); +)); export default TextField; diff --git a/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx b/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx index 8493e73..c45e429 100644 --- a/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx +++ b/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef, forwardRef } from 'react'; import dayjs from 'dayjs'; import { useSettingsStore, useLocaleUpdateStore } from 'stores'; @@ -14,13 +14,13 @@ import { const times = ['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24']; -const TimeRangeField = ({ +const TimeRangeField = forwardRef(({ label, subLabel, id, - register, + setValue, ...props -}) => { +}, ref) => { const timeFormat = useSettingsStore(state => state.timeFormat); const locale = useLocaleUpdateStore(state => state.locale); @@ -38,6 +38,8 @@ const TimeRangeField = ({ } }, [rangeRef]); + useEffect(() => setValue(props.name, JSON.stringify({start, end})), [start, end, setValue, props.name]); + const handleMouseMove = e => { if (isStartMoving.current || isEndMoving.current) { let step = Math.round(((e.pageX - rangeRect.current.left) / rangeRect.current.width) * 24); @@ -60,8 +62,8 @@ const TimeRangeField = ({ @@ -139,6 +141,6 @@ const TimeRangeField = ({ ); -}; +}); export default TimeRangeField; diff --git a/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts b/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts index 6575d2f..f7a11f8 100644 --- a/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts +++ b/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts @@ -35,14 +35,17 @@ export const StyledLabel = styled.label` export const Option = styled.div` flex: 1; + position: relative; `; export const HiddenInput = styled.input` height: 0; width: 0; position: absolute; - left: -1000px; + top: 0; + left: 0; opacity: 0; + appearance: none; &:checked + label { color: ${props => props.theme.background}; diff --git a/crabfit-frontend/src/pages/Create/Create.tsx b/crabfit-frontend/src/pages/Create/Create.tsx index 4f7d239..e7430ac 100644 --- a/crabfit-frontend/src/pages/Create/Create.tsx +++ b/crabfit-frontend/src/pages/Create/Create.tsx @@ -39,7 +39,7 @@ dayjs.extend(timezone); dayjs.extend(customParseFormat); const Create = ({ offline }) => { - const { register, handleSubmit } = useForm({ + const { register, handleSubmit, setValue } = useForm({ defaultValues: { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, }, @@ -196,36 +196,34 @@ const Create = ({ offline }) => { label={t('home:form.name.label')} subLabel={t('home:form.name.sublabel')} type="text" - name="name" id="name" - register={register} + {...register('name')} /> diff --git a/crabfit-frontend/src/pages/Event/Event.tsx b/crabfit-frontend/src/pages/Event/Event.tsx index 39e2bf3..5f4ce82 100644 --- a/crabfit-frontend/src/pages/Event/Event.tsx +++ b/crabfit-frontend/src/pages/Event/Event.tsx @@ -51,7 +51,7 @@ const Event = (props) => { const { t } = useTranslation(['common', 'event']); - const { register, handleSubmit } = useForm(); + const { register, handleSubmit, setFocus } = useForm(); const { id } = props.match.params; const { offline } = props; const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone); @@ -329,20 +329,18 @@ const Event = (props) => { { e.preventDefault(); if (user) { setTab('you'); - } + } else { + setFocus('name'); + } }} selected={tab === 'you'} disabled={!user} diff --git a/crabfit-frontend/src/pages/Event/eventStyle.ts b/crabfit-frontend/src/pages/Event/eventStyle.ts index 60080e6..2ca35b1 100644 --- a/crabfit-frontend/src/pages/Event/eventStyle.ts +++ b/crabfit-frontend/src/pages/Event/eventStyle.ts @@ -109,7 +109,7 @@ export const Tab = styled.a` color: ${props => props.theme.text}; padding: 8px 18px; background-color: ${props => props.theme.primaryBackground}; - border: 1px solid ${props => props.theme.primaryLight}; + border: 1px solid ${props => props.theme.primary}; border-bottom: 0; margin: 0 4px; border-top-left-radius: 5px; diff --git a/crabfit-frontend/src/pages/Home/Home.tsx b/crabfit-frontend/src/pages/Home/Home.tsx index 3dec4fe..2e3f606 100644 --- a/crabfit-frontend/src/pages/Home/Home.tsx +++ b/crabfit-frontend/src/pages/Home/Home.tsx @@ -34,9 +34,11 @@ import { StatNumber, StatLabel, OfflineMessage, + ButtonArea, } from './homeStyle'; import api from 'services'; +import { detect_browser } from 'utils'; import logo from 'res/logo.svg'; import timezones from 'res/timezones.json'; @@ -46,7 +48,7 @@ dayjs.extend(timezone); dayjs.extend(customParseFormat); const Home = ({ offline }) => { - const { register, handleSubmit } = useForm({ + const { register, handleSubmit, setValue } = useForm({ defaultValues: { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, }, @@ -58,6 +60,7 @@ const Home = ({ offline }) => { personCount: null, version: 'loading...', }); + const [browser, setBrowser] = useState(undefined); const { push } = useHistory(); const { t } = useTranslation(['common', 'home']); @@ -73,6 +76,7 @@ const Home = ({ offline }) => { fetch(); document.title = 'Crab Fit'; + setBrowser(detect_browser()); }, []); const onSubmit = async data => { @@ -173,36 +177,34 @@ const Home = ({ offline }) => { label={t('home:form.name.label')} subLabel={t('home:form.name.sublabel')} type="text" - name="name" id="name" - register={register} + {...register('name')} /> @@ -224,15 +226,37 @@ const Home = ({ offline }) => { {t('home:about.events')} - {stats.personCount ?? '500+'} + {stats.personCount ?? '550+'} {t('home:about.availabilities')} Crab Fit helps you fit your event around everyone's schedules. Simply create an event above and send the link to everyone that is participating. Results update live and you will be able to see a heat-map of when everyone is free.Learn more about how to Crab Fit. - {/* eslint-disable-next-line */} - Create a lot of Crab Fits? Get the Chrome extension or Firefox extension for your browser! You can also download the Android app to Crab Fit on the go. - {/* eslint-disable-next-line */} - Created by Ben Grant, Crab Fit is the modern-day solution to your group event planning debates. + + {['chrome', 'firefox'].includes(browser) && ( + , + firefox: , + }[browser]} + target="_blank" + secondary + >{{ + chrome: t('home:about.chrome_extension'), + firefox: t('home:about.safari_extension'), + }[browser]} + )} + } + target="_blank" + secondary + >{t('home:about.android_app')} + + Created by Ben Grant, Crab Fit is the modern-day solution to your group event planning debates. The code for Crab Fit is open source, if you find any issues or want to contribute, you can visit the repository. By using Crab Fit you agree to the privacy policy. Consider donating below if it helped you out so it can stay free for everyone. 🦀 diff --git a/crabfit-frontend/src/pages/Home/homeStyle.ts b/crabfit-frontend/src/pages/Home/homeStyle.ts index 2a205e7..70ca3dc 100644 --- a/crabfit-frontend/src/pages/Home/homeStyle.ts +++ b/crabfit-frontend/src/pages/Home/homeStyle.ts @@ -89,6 +89,10 @@ export const AboutSection = styled.section` margin: 30px 0 0; background-color: ${props => props.theme.primaryBackground}; padding: 20px 0; + + & a { + color: ${props => props.theme.mode === 'light' ? props.theme.primaryDark : props.theme.primaryLight}; + } `; export const P = styled.p` @@ -125,3 +129,12 @@ export const OfflineMessage = styled.div` text-align: center; margin: 50px 0 20px; `; + +export const ButtonArea = styled.div` + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 12px; + margin: 30px 0; +`; diff --git a/crabfit-frontend/src/utils/index.ts b/crabfit-frontend/src/utils/index.ts new file mode 100644 index 0000000..701605a --- /dev/null +++ b/crabfit-frontend/src/utils/index.ts @@ -0,0 +1,31 @@ +export const detect_browser = () => { + // Opera 8.0+ + const isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; + + // Firefox 1.0+ + const isFirefox = typeof InstallTrigger !== 'undefined'; + + // Safari 3.0+ "[object HTMLElementConstructor]" + const isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && window['safari'].pushNotification)); + + // Internet Explorer 6-11 + const isIE = /*@cc_on!@*/false || !!document.documentMode; + + // Edge 20+ + const isEdge = !isIE && !!window.StyleMedia; + + // Chrome 1 - 79 + const isChrome = !!window.chrome; + + // Edge (based on chromium) detection + // eslint-disable-next-line + const isEdgeChromium = isChrome && (navigator.userAgent.indexOf("Edg") != -1); + + if (isEdgeChromium) return 'edge_chromium'; + if (isChrome) return 'chrome'; + if (isEdge) return 'edge'; + if (isIE) return 'ie'; + if (isSafari) return 'safari'; + if (isFirefox) return 'firefox'; + if (isOpera) return 'opera'; +}; diff --git a/crabfit-frontend/yarn.lock b/crabfit-frontend/yarn.lock index e7c363a..0a065f8 100644 --- a/crabfit-frontend/yarn.lock +++ b/crabfit-frontend/yarn.lock @@ -9232,10 +9232,10 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== -react-hook-form@^6.15.4: - version "6.15.4" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-6.15.4.tgz#328003e1ccc096cd158899ffe7e3b33735a9b024" - integrity sha512-K+Sw33DtTMengs8OdqFJI3glzNl1wBzSefD/ksQw/hJf9CnOHQAU6qy82eOrh0IRNt2G53sjr7qnnw1JDjvx1w== +react-hook-form@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.8.1.tgz#291609c50778bf8bc94e5d4c586e9ee5c6dd512f" + integrity sha512-pwWxd4UfwsKVh/W2YE2Hnu7KBY4KTCZq3QO1PMISzP31AZ5Kmv6ji085QVIHVWVSLT4oX6IhmZ2bWJ2oBIwobQ== react-i18next@^11.8.15: version "11.8.15"
Crab Fit helps you fit your event around everyone's schedules. Simply create an event above and send the link to everyone that is participating. Results update live and you will be able to see a heat-map of when everyone is free.Learn more about how to Crab Fit.
Create a lot of Crab Fits? Get the Chrome extension or Firefox extension for your browser! You can also download the Android app to Crab Fit on the go.
Created by Ben Grant, Crab Fit is the modern-day solution to your group event planning debates.
The code for Crab Fit is open source, if you find any issues or want to contribute, you can visit the repository. By using Crab Fit you agree to the privacy policy.
Consider donating below if it helped you out so it can stay free for everyone. 🦀