\": 40,\n};\n\nconst KeyboardChar = ({\n char,\n onClick,\n onKeydown,\n onKeyUp,\n style,\n foregroundColor,\n theme,\n color,\n}) => {\n const ContextConsumer = useContext(BrowserContext);\n\n // null check for testing, temporarily\n let allowKeyboardShortcuts, existsModal, isMobile, currentModal;\n if (ContextConsumer) {\n allowKeyboardShortcuts = ContextConsumer.allowKeyboardShortcuts;\n // TODO: once all modals migrated to use ModalConductor, existsModal won't need to exist\n existsModal = ContextConsumer.existsModal;\n currentModal = ContextConsumer.currentModal;\n isMobile = ContextConsumer.isMobile;\n }\n\n useEffect(() => {\n if (char && onClick && char.toUpperCase() in charToKeyCodeMap) {\n document.addEventListener(\"keydown\", handleKeydown);\n document.addEventListener(\"keyup\", handleKeyup);\n }\n return () => {\n document.removeEventListener(\"keydown\", handleKeydown);\n document.removeEventListener(\"keyup\", handleKeyup);\n };\n });\n\n const _matchesKey = (e) => {\n const keyCode = e.keyCode;\n const charFromProps = char.toUpperCase();\n return (\n keyCode === charToKeyCodeMap[charFromProps] &&\n allowKeyboardShortcuts &&\n currentModal == null &&\n !existsModal\n );\n };\n\n const handleKeydown = (e) => {\n if (onKeydown && _matchesKey(e)) {\n onKeydown(e);\n }\n };\n\n const handleKeyup = (e) => {\n if (_matchesKey(e)) {\n if (onKeyUp) {\n onKeyUp(e);\n }\n if (onClick) {\n onClick(e);\n }\n }\n };\n\n if (isMobile) {\n return ;\n }\n\n return (\n \n {char} \n \n );\n};\n\nconst KeyboardCharContent = styled.div`\n border: 2px solid ${({ theme }) => theme.primary};\n border-radius: 5px;\n color: ${({ theme }) => theme.primary};\n background: ${({ theme }) => theme.keyboardChar.background};\n font-family: ${font.header};\n ${(props) =>\n props.color &&\n `\n border-color: ${props.color};\n color: ${props.color};\n `};\n ${(props) =>\n props.theme.keyboardChar.text &&\n `\n color: ${props.theme.keyboardChar.text};\n `}\n ${(props) =>\n props.onClick &&\n `\n cursor: pointer;\n `};\n width: 25px;\n height: 25px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: bold;\n span {\n margin-top: 2px;\n margin-left: -1px;\n }\n`;\n\nexport default withTheme(KeyboardChar);\n","import React, { Fragment } from \"react\";\nimport styled from \"styled-components\";\nimport KeyboardChar from \"../Button/KeyboardChar\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport { colors } from \"../../../helpers/styles\";\n\nconst Slider = ({\n onChange,\n value,\n smallBlindCents,\n min,\n max,\n style,\n setValue,\n betFocus,\n potFocus,\n}) => {\n const smallStepCents = smallBlindCents ? smallBlindCents / 100.0 : 0.01;\n const bigStepCents = smallBlindCents\n ? parseFloat((smallBlindCents * 2).toFixed(2))\n : 5;\n\n const increment = () => {\n if (betFocus || potFocus) {\n return;\n }\n let newVal = parseFloat(value) + bigStepCents;\n if (newVal > max) {\n newVal = max;\n }\n setValue(newVal.toFixed(2));\n };\n\n const decrement = () => {\n if (betFocus || potFocus) {\n return;\n }\n let newVal = parseFloat(value) - bigStepCents;\n if (newVal < min) {\n newVal = min;\n }\n setValue(newVal.toFixed(2));\n };\n\n if (min >= max) {\n return ;\n }\n\n let validatedValue = value;\n if (validatedValue > max) {\n validatedValue = max;\n } else if (validatedValue < min || !validatedValue) {\n validatedValue = min;\n }\n\n return (\n \n \n \"\n color={colors.grey}\n style={{ display: \"none\" }}\n onClick={decrement}\n />\n \"\n color={colors.grey}\n style={{ display: \"none\" }}\n onClick={decrement}\n />\n \n \n \n \n \n \"\n color={colors.grey}\n style={{ display: \"none\" }}\n onClick={increment}\n />\n \"\n color={colors.grey}\n style={{ display: \"none\" }}\n onClick={increment}\n />\n \n );\n};\n\nconst InputWrapper = styled.div`\n position: relative;\n width: 500px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 20px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 40vw;\n margin-right: 20px;\n }\n`;\n\nconst RangeWrapper = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst InputRange = styled.input`\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n height: 10px;\n background: ${colors.white};\n outline: none;\n border-radius: 5px;\n\n &::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n cursor: pointer;\n background: ${({ theme }) => theme.primary};\n height: 20px;\n width: 15px;\n border-radius: 5px;\n }\n\n ${(props) =>\n props.disable &&\n `\n pointer-events: none;\n background: ${colors.grey};\n `};\n`;\n\nconst InputLineColor = styled.div`\n position: absolute;\n bottom: 7px;\n width: 100%;\n height: 10px;\n background: ${({ theme }) => theme.primary};\n pointer-events: none;\n border-radius: 5px 0 0 5px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n bottom: 8px;\n }\n`;\n\nexport default Slider;\n","import React from \"react\";\nimport styled, { withTheme } from \"styled-components\";\nimport { colors } from \"../../helpers/styles\";\n\nconst Switch = ({ isOn, handleToggle, dataTestId }) => {\n return (\n \n \n \n );\n};\n\nconst SwitchButton = styled.span`\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 25px;\n height: 25px;\n border-radius: 50%;\n transition: 0.2s;\n background: ${colors.white};\n box-shadow: 0 0 2px 0 rgba(10, 10, 10, 0.29);\n top: 5px;\n left: 5px;\n ${(props) =>\n props.isOn &&\n `\n transform: translateX(100%);\n left: 16px;\n `}\n`;\n\nconst CheckboxLabel = styled.label`\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n width: 70px;\n height: 35px;\n background: ${({ theme }) => theme.switch.background};\n border-radius: 100px;\n position: relative;\n transition: background-color 0.2s;\n flex-grow: 0;\n flex-shrink: 0;\n flex-basis: 70px;\n margin: 0;\n ${(props) =>\n props.isOn &&\n props.theme &&\n `\n background: ${props.theme.tertiary}\n `}\n`;\n\nexport default withTheme(Switch);\n","import styled from \"styled-components\";\nimport { font, colors } from \"../../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\n\nexport const BaseButton = styled.button`\n font-weight: bold;\n border-radius: 10px;\n &:active,\n &:focus {\n outline: 0;\n }\n transition: background 0.3s ease;\n ${(props) =>\n props.disable &&\n `\n background: ${({ theme }) => theme.foreground} !important;\n pointer-events: none;\n `}\n`;\n\nexport const Button = styled(BaseButton)`\n text-transform: uppercase;\n font-family: ${font.header};\n font-size: 12px;\n letter-spacing: 0.6px;\n color: white;\n border: 1px solid ${colors.white};\n width: 130px;\n height: 30px;\n background: ${({ theme }) => theme.tertiary};\n white-space: nowrap;\n ${(props) => props.fullWidth && `width: 100%;`}\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 12px;\n padding: 8px 12px;\n ${(props) => props.fullWidth && `width: 100%;`}\n }\n ${(props) =>\n props.disable &&\n `\n opacity: 0.3;\n pointer-events: none;\n `}\n`;\n\nexport const PrimaryButton = styled(Button)`\n background: ${({ theme }) => theme.primary};\n color: white;\n border: 1px solid ${({ theme }) => theme.foreground};\n color: ${colors.white};\n height: 60px;\n width: 120px;\n padding: 0 15px;\n top: 0px;\n font-weight: 400;\n font-size: 18px;\n ${(props) =>\n !props.disableShadow &&\n `\n &:active {\n transform: translate(5px, 5px);\n }\n `}\n ${(props) =>\n props.isClicked\n ? `background: ${({ theme }) => theme.tertiary}; color: ${\n colors.white\n }; outline: 0;`\n : ``} &:focus {\n outline: 0;\n }\n ${(props) =>\n !props.disableShadow &&\n props.pressed &&\n `\n transform: translate(5px, 5px);\n `}\n border-radius: 5px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 80px;\n height: 40px;\n }\n`;\n","import React from \"react\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport styled, { withTheme } from \"styled-components\";\nimport { colors } from \"../../../helpers/styles\";\n\nconst Icon = ({ isLeft = false, ...rest }) => {\n return ;\n};\n\nconst IconInner = styled(FontAwesomeIcon)`\n color: ${(props) => (props.color ? props.color : colors.darkGrey)};\n color: ${(props) =>\n props.widget && props.theme && `${props.theme.text.widget}`};\n ${(props) =>\n props.onClick &&\n `\n cursor: pointer;\n `}\n ${(props) => props.isLeft && \"margin-right: 10px\"};\n ${(props) => props.disabled && \"opacity: 0.5\"};\n ${(props) => props.disabled && \"cursor: not-allowed\"};\n ${(props) => props.disabled && \"pointer-events: none\"};\n`;\n\nexport default withTheme(Icon);\n","import React, { useState } from \"react\";\nimport styled, { withTheme } from \"styled-components\";\nimport * as Base from \"./Base\";\nimport KeyboardChar from \"./KeyboardChar\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\n\nconst PrimaryButton = ({\n style,\n isClicked,\n disableShadow,\n disable,\n fullWidth,\n keyboardChar,\n onClick,\n secondaryColor,\n children,\n theme,\n color,\n dataTestId,\n id,\n isPressed,\n}) => {\n const [pressed, setPressed] = useState(false);\n return (\n \n \n {children}\n \n {!disableShadow && }\n {keyboardChar && (\n \n setPressed(true)}\n onKeyUp={() => setPressed(false)}\n color={color || (secondaryColor && theme.secondary)}\n />\n \n )}\n \n );\n};\nconst ButtonWrapper = styled.div`\n position: relative;\n height: 65px;\n width: 125px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 80px;\n height: 40px;\n margin-right: 10px;\n }\n`;\nconst PrimaryButtonFixed = styled(Base.PrimaryButton)`\n position: absolute;\n ${(props) =>\n props.theme &&\n props.secondaryColor &&\n `background: ${props.theme.secondary}`};\n ${(props) =>\n props.color &&\n `\n background: ${props.color}\n `}\n z-index: 1;\n left: 0;\n`;\nconst PrimaryButtonShadow = styled(Base.PrimaryButton)`\n position: absolute;\n z-index: 0;\n background: ${(props) => props.color && `${props.color}`}\n left: 0;\n transform: translate(5px, 5px);\n`;\nconst KeyboardCharWrapper = styled.div`\n position: absolute;\n z-index: 1;\n left: 0;\n transform: translate(10px, -50%);\n`;\nexport default withTheme(PrimaryButton);\n","import styled from \"styled-components\";\nimport { font, colors } from \"../../../helpers/styles\";\nimport * as Base from \"./Base\";\nimport Icon from \"./Icon\";\nimport PrimaryButton from \"./PrimaryButton\";\nexport { PrimaryButton, Icon };\n\nexport const OutlineButton = styled(Base.Button)`\n font-weight: 600;\n border: 2px solid ${({ theme }) => theme.primary};\n border-radius: 5px;\n padding: 2px 8px;\n color: ${({ theme }) => theme.tertiary};\n letter-spacing: 0.1px;\n background: none;\n width: auto;\n &:hover {\n background: ${({ theme }) => theme.tertiary};\n color: ${colors.white};\n }\n`;\n\nexport const ModalButton = styled(Base.Button)`\n height: 40px;\n min-height: 40px;\n text-align: center;\n border-radius: 5px;\n font-size: 16px;\n border: 1px solid ${({ theme }) => theme.modal.background};\n margin-left: 10px;\n &:hover {\n opacity: 0.9;\n }\n`;\n\nexport const HeaderButton = styled(Base.Button)`\n margin-left: 5px;\n margin-right: 5px;\n`;\n\nexport const IconButton = styled(Base.BaseButton)`\n width: 30px;\n border: none;\n background: none;\n color: ${({ theme }) => theme.tertiary};\n`;\n\nexport const BackgroundButton = styled(Base.Button)`\n font-family: ${font.header};\n font-size: 18px;\n font-weight: bold;\n letter-spacing: 1px;\n padding: 15px;\n cursor: pointer;\n border-radius: 5px;\n border: none;\n height: auto;\n`;\n\nexport const SecondaryButton = styled(Base.Button)`\n width: auto;\n height: auto;\n background: ${({ theme }) => theme.foreground};\n border-radius: 5px;\n padding: 8px;\n border: none;\n color: ${colors.darkGrey2};\n font-size: 18px;\n width: 100%;\n margin: 0 5px;\n ${(props) => props.firstInList && `margin-left: 0;`}\n ${(props) => props.lastInList && `margin-right: 0;`}\n`;\n","import styled, { css } from \"styled-components\";\nimport { font, colors } from \"../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\n\nconst BaseInput = styled.input`\n font-family: ${font.body};\n border: none;\n font-size: 1em;\n background-color: none;\n background: none;\n ${(props) => props.fullWidth && `width: 100%;`}\n &:hover,\n &:focus {\n outline: 0;\n }\n`;\n\nexport const InputLine = styled(BaseInput)`\n color: ${colors.darkGrey};\n padding: 0.4em 0.4em 0.1em 0.4em;\n transition: all 0.2s ease-out;\n width: 100%;\n border-bottom: 1px solid black;\n`;\n\nexport const InputText = styled(BaseInput)`\n background: ${({ theme }) => theme.background};\n border-radius: 5px;\n padding: 5px 15px;\n font-size: 16px;\n color: ${({ theme }) => theme.text.mainBody};\n border: 1px solid ${({ theme }) => theme.foreground};\n ${(props) =>\n props.contrast &&\n `\n background: ${props.theme.input.contrast};\n `}\n ${(props) =>\n props.noBack &&\n `\n background: ${props.theme.input.noBack};\n `}\n ${(props) =>\n props.modal &&\n `\n background: ${props.theme.input.modal};\n `}\n`;\n\nexport const InputField = styled.span`\n font-family: ${font.body};\n display: block;\n position: relative;\n margin-top: ${(props) => props.top}px;\n text-align: center;\n width: 250px;\n ${(props) =>\n props.fullWidth &&\n css`\n width: 100%;\n ${InputText} {\n width: 100%;\n }\n `}\n`;\n\nexport const InputLabel = styled.label`\n font-family: ${font.body};\n color: #919293;\n box-sizing: border-box;\n display: block;\n font-size: 0.7em;\n line-height: 1;\n opacity: 0;\n overflow: hidden;\n padding: 0.5em 1em;\n pointer-events: none;\n position: absolute;\n text-overflow: ellipsis;\n top: 0;\n text-align: left;\n user-select: none;\n white-space: nowrap;\n width: 100%;\n z-index: 16;\n ${(props) => props.value && `opacity: 0;`};\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 0.7em;\n }\n`;\n\nexport const TextAreaInput = styled.textarea`\n resize: none;\n margin-top: 20px;\n padding: 8px;\n font-size: 16px;\n font-family: ${font.body};\n border: 1px solid ${({ theme }) => theme.foreground};\n background: ${({ theme }) => theme.input.contrast};\n border-radius: 5px;\n color: ${({ theme }) => theme.text.label};\n\n &:focus {\n outline: none;\n }\n &::placeholder {\n font-weight: 500;\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 0.7em;\n }\n`;\n","export const CHAT_STATE = {\n EXPANDED: 0,\n COLLAPSED: 1,\n NORMAL: 2,\n};\n","import styled, { css } from \"styled-components\";\nimport { font, colors } from \"../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\n\nconst BaseHeaderText = styled.div`\n width: 100%;\n ${({ align }) => (align ? `text-align: ${align}` : \"text-align: center;\")};\n ${(props) =>\n props.themeable && props.theme && `color: ${props.theme.text.mainBody};`}\n`;\n\nconst BaseBodyText = styled.div`\n ${({ align }) => (align ? `text-align: ${align}` : \"text-align: left;\")};\n margin-bottom: 0;\n`;\n\nexport const LargeHeaderText = styled(BaseHeaderText)`\n font-family: ${font.header};\n font-weight: bold;\n font-size: 2em;\n line-height: 25px;\n font-weight: bold;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 1.6em;\n }\n`;\n\nexport const MediumHeaderText = styled(BaseHeaderText)`\n font-family: ${font.header};\n font-size: 28px;\n letter-spacing: 1px;\n font-weight: bold;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 24px;\n letter-spacing: 0.8px;\n }\n`;\n\nexport const SmallHeaderText = styled(BaseHeaderText)`\n font-family: ${font.header};\n font-size: 18px;\n line-height: 18px;\n letter-spacing: 1px;\n font-weight: bold;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 12px;\n line-height: 12px;\n }\n`;\n\nexport const SmallBodyText = styled(BaseBodyText)`\n font-family: ${font.body};\n font-size: 14px;\n margin: 0;\n ${(props) => props.color && `color: ${props.color}`};\n ${(props) =>\n props.themeable && props.theme && `color: ${props.theme.text.mainBody};`}\n\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 10px;\n }\n`;\n\nexport const SmallHeaderTextEllipsis = styled(SmallHeaderText)`\n width: 100%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nexport const MediumBodyText = styled(BaseBodyText)`\n font-family: ${font.body};\n font-size: 18px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 12px;\n }\n`;\n\nexport const LargeBodyText = styled(BaseBodyText)`\n font-family: ${font.body};\n font-size: 22px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 20px;\n }\n`;\n\nexport const HighlightedText = styled.span`\n font-weight: bold;\n color: ${({ theme }) => theme.tertiary};\n`;\n\nexport const textWithLoadingDots = css`\n display: flex;\n flex-direction: row;\n font-weight: bold;\n letter-spacing: 0.3px;\n color: ${colors.darkGrey2};\n\n &:after {\n content: \" .\";\n animation: dots 1s steps(5, end) infinite;\n }\n @keyframes dots {\n 0%,\n 20% {\n color: ${colors.darkGrey2};\n text-shadow: 0.25em 0 0 ${colors.darkGrey2}, 0.5em 0 0 ${colors.darkGrey2};\n }\n 40% {\n color: ${colors.lightGrey2};\n text-shadow: 0.25em 0 0 ${colors.darkGrey2}, 0.5em 0 0 ${colors.darkGrey2};\n }\n 60% {\n text-shadow: 0.25em 0 0 ${colors.lightGrey2},\n 0.5em 0 0 ${colors.darkGrey2};\n }\n 80%,\n 100% {\n text-shadow: 0.25em 0 0 ${colors.lightGrey2},\n 0.5em 0 0 ${colors.lightGrey2};\n }\n }\n`;\n\nexport const LoadingWrapper = styled(SmallHeaderText)`\n font-size: 20px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 18px;\n }\n ${textWithLoadingDots}\n`;\n\nexport const BoldText = styled.div`\n margin-left: 5px;\n margin-right: 3px;\n color: ${({ theme }) => theme.primary};\n text-decoration: underline;\n`;\n","import React from \"react\";\nimport { SmallHeaderText, LargeBodyText } from \"../../shared/Text\";\nimport { colors, font } from \"../../helpers/styles\"; // ${font}\nimport { Icon } from \"../../shared/FormElements\";\nimport {\n faCheckSquare,\n faSquare,\n faTimes,\n} from \"@fortawesome/free-solid-svg-icons\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport { CHAT_STATE } from \"./utils\";\nimport styled from \"styled-components\";\n\nconst ChatTopBar = ({\n toggleShowGameLog,\n showGameLog,\n chatState,\n setChatState,\n numUnreadMessages,\n isMobile,\n isMTTLobby,\n}) => {\n return (\n {\n if (e.target.classList && e.target.classList.contains(\"chat-top-bar\")) {\n setChatState(\n chatState === CHAT_STATE.NORMAL\n ? CHAT_STATE.EXPANDED\n : CHAT_STATE.NORMAL\n );\n }\n }}\n >\n \n Chat{\" \"}\n {chatState === CHAT_STATE.COLLAPSED && numUnreadMessages !== 0 && (\n 99}>\n {numUnreadMessages <= 99 ? numUnreadMessages : \"99+\"} \n \n )}\n \n {!isMTTLobby && (\n \n \n theme.primary : colors.white}\n isLeft\n />\n \n \n Game Log\n \n \n )}\n \n setChatState(\n chatState === CHAT_STATE.COLLAPSED\n ? CHAT_STATE.NORMAL\n : CHAT_STATE.COLLAPSED\n )\n }\n >\n \n \n \n );\n};\n\nconst ChatTopBarWrapper = styled.div`\n background: ${({ theme }) => colors.darkGrey};\n width: 100%;\n height: 40px;\n flex-grow: 0;\n flex-shrink: 0;\n flex-basis: 40px;\n display: flex;\n justify-content: flex-end;\n align-items: center;\n padding-left: 10px;\n cursor: pointer;\n &:hover {\n background: ${({ theme }) => theme.secondaryLighter};\n }\n`;\n\nconst ChatHeaderText = styled(LargeBodyText)`\n margin-left: 10px;\n font-weight: bold;\n color: white;\n width: 100%;\n pointer-events: none;\n display: flex;\n align-items: center;\n`;\n\nconst UnreadNumber = styled.div`\n background: ${({ theme }) => theme.tertiary};\n border-radius: 50%;\n height: 20px;\n width: 20px;\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 10px;\n span {\n margin-left: -1px;\n }\n ${(props) =>\n props.big &&\n `\n height: 30px;\n width: 30px;\n `}\n`;\n\nconst GameMessageToggle = styled.div`\n background: white;\n border-radius: 5px;\n font-family: ${font.header};\n font-size: 16px;\n padding: 8px;\n background: none;\n letter-spacing: 1px;\n color: ${colors.white};\n cursor: pointer;\n display: flex;\n margin-right: 10px;\n width: 105px;\n flex-grow: 0;\n flex-shrink: 0;\n flex-basis: 105px;\n align-items: center;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 4px;\n }\n`;\n\nconst Checkbox = styled.div`\n background: white;\n width: 12px;\n height: 12px;\n display: flex;\n align-items: center;\n`;\n\nconst IconWrapper = styled.div`\n padding: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n`;\n\nexport default ChatTopBar;\n","import React, { useRef, useEffect, useState } from \"react\";\nimport { SmallBodyText } from \"../../shared/Text\";\nimport Linkify from \"react-linkify\";\nimport styled from \"styled-components\";\n\nconst linkDecorator = (\n href,\n text,\n key // for links in chat in new tab\n) => (\n \n {text}\n \n);\n\nconst ChatMain = ({ chats, updateScroll }) => {\n const chatMessageContainerRef = useRef(null);\n const [isScrollBottom, setIsScrollBottom] = useState(true);\n\n useEffect(() => {\n if (isScrollBottom) {\n scrollToBottom();\n }\n }, [updateScroll, chats]);\n\n const scrollToBottom = () => {\n if (chatMessageContainerRef.current) {\n chatMessageContainerRef.current.scrollTop =\n chatMessageContainerRef.current.scrollHeight;\n }\n };\n\n const onScroll = () => {\n setIsScrollBottom(\n chatMessageContainerRef.current.scrollTop +\n chatMessageContainerRef.current.clientHeight ===\n chatMessageContainerRef.current.scrollHeight\n );\n };\n\n return (\n \n {chats.map((chat, i) => (\n \n {chat.username ? `${chat.username}: ` : \"\"} \n {chat.message} \n \n ))}\n \n );\n};\n\nconst ChatMessageContainer = styled.div`\n height: 100%;\n overflow-y: scroll;\n overflow-x: hidden;\n overflow-wrap: break-word;\n flex: auto;\n color: ${({ theme }) => theme.text.mainBody};\n`;\n\nconst ChatMessage = styled(SmallBodyText)`\n padding: 0 20px;\n margin: 8px 0;\n display: block;\n ${(props) =>\n props.isGameMsg &&\n props.theme &&\n `\n color: ${props.theme.chat.gameText};\n font-weight: bold;\n `}\n a, a:hover {\n color: ${({ theme }) => theme.chat.linkText};\n text-decoration: underline;\n }\n`;\n\nexport default ChatMain;\n","import React, { Fragment, useEffect } from \"react\";\n\nconst MouseEvents = ({\n setAllowKeyboardShortcuts,\n existsModal,\n focusElement,\n setChatIsFocused,\n onEscPress,\n}) => {\n useEffect(() => {\n document.addEventListener(\"click\", handleDocumentClick);\n document.addEventListener(\"keydown\", handleKeydown);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n document.removeEventListener(\"keydown\", handleKeydown);\n };\n });\n\n const handleDocumentClick = (_) => {\n const activeId = document.activeElement.id;\n if (!existsModal) {\n const chatIsFocused = activeId === focusElement;\n setAllowKeyboardShortcuts(!chatIsFocused);\n setChatIsFocused(chatIsFocused);\n }\n };\n\n const handleKeydown = (e) => {\n const code = e.keyCode || e.which;\n if (code === 27) {\n onEscPress(e);\n }\n };\n\n return ;\n};\nexport default MouseEvents;\n","import React, { useState, useRef } from \"react\";\nimport MouseEvents from \"./MouseEvents\";\nimport styled from \"styled-components\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport { InputText } from \"../../shared/FormElements\";\nimport { SmallBodyText } from \"../../shared/Text\";\nimport { emit } from \"../../helpers/socket\";\nimport { CHAT_STATE } from \"./utils\";\nimport KeyboardChar from \"../../shared/FormElements/Button/KeyboardChar\";\n\nconst ChatInput = ({\n username,\n gameId,\n sessionKey,\n chatIsFocused,\n setAllowKeyboardShortcuts,\n setChatIsFocused,\n existsModal,\n chatState,\n}) => {\n const [inputMessage, setInputMessage] = useState(\"\");\n const chatInput = useRef(null);\n\n const handleSendMessage = (e) => {\n e.preventDefault();\n if (inputMessage !== \"\") {\n emit(\"send_message\", {\n username: username,\n game_id: gameId,\n key: sessionKey,\n msg: inputMessage,\n });\n setInputMessage(\"\");\n }\n };\n\n const handleInputChange = (e) => {\n setInputMessage(e.target.value);\n };\n\n const handleEscPress = (e) => {\n chatInput.current.blur();\n setChatIsFocused(false);\n setAllowKeyboardShortcuts(true);\n };\n\n return (\n <>\n \n \n \n \n {\n if (chatState !== CHAT_STATE.COLLAPSED) {\n chatInput.current.focus();\n setAllowKeyboardShortcuts(false);\n setChatIsFocused(true);\n }\n }}\n />\n \n Send your message here!\n \n \n \n >\n );\n};\n\nconst SendMessageForm = styled.form`\n width: 100%;\n margin-top: 5px;\n height: 30px;\n flex-grow: 0;\n flex-shrink: 0;\n flex-basis: 30px;\n display: flex;\n transition: 0.3s ease all;\n border-top: 1px solid ${({ theme }) => theme.chat.divider};\n ${(props) =>\n props.isFocused &&\n `\n height: 70px;\n flex-basis: 70px;\n `}\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 0 5px 5px 5px;\n height: auto;\n }\n`;\n\nconst SendMessageInput = styled(InputText)`\n width: 100%;\n margin: 0;\n`;\n\nconst MessageHelperText = styled.div`\n position: absolute;\n pointer-events: none;\n display: flex;\n align-items: center;\n color: ${({ theme }) => theme.text.placeholder};\n padding: 0 10px;\n ${(props) =>\n !props.show &&\n `\n opacity: 0;\n `}\n`;\n\nexport default ChatInput;\n","import React, { useState, useContext, Fragment } from \"react\";\nimport styled from \"styled-components\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport { colors } from \"../../helpers/styles\";\nimport { GameContext } from \"../../context/GameContext\";\nimport { BrowserContext } from \"../../context/BrowserContext\";\nimport { Icon } from \"../../shared/FormElements\";\nimport { faComment } from \"@fortawesome/free-solid-svg-icons\";\nimport { CHAT_STATE } from \"./utils\";\nimport ChatTopBar from \"./ChatTopBar\";\nimport ChatMain from \"./ChatMain\";\nimport ChatInput from \"./ChatInput\";\n\nconst Chat = ({ gameId, MTTId, isMTTLobby }) => {\n const { username, sessionKey, chats } = useContext(GameContext);\n const {\n setAllowKeyboardShortcuts,\n showChat,\n existsModal,\n isMobile,\n betButtonsOnRight,\n } = useContext(BrowserContext);\n\n const [chatIsFocused, setChatIsFocused] = useState(false);\n const [chatState, _setChatState] = useState(CHAT_STATE.NORMAL);\n const [showGameLog, setShowGameLog] = useState(true);\n const [updateScroll, setUpdateScroll] = useState(false);\n const [numReadMessages, setNumReadMessages] = useState(0);\n const [numReadPlayerMessages, setNumReadPlayerMessages] = useState(0);\n\n const getPlayerChats = () => {\n return chats.filter((chat) => !!chat.username);\n };\n\n const setChatState = (newState) => {\n if (newState === CHAT_STATE.COLLAPSED) {\n setNumReadMessages(chats.length);\n setNumReadPlayerMessages(getPlayerChats().length);\n }\n _setChatState(newState);\n setTimeout(() => {\n setUpdateScroll(!updateScroll);\n }, 400); // animation is 0.3s, so scroll after animation done\n };\n\n const toggleShowGameLog = () => {\n setShowGameLog(!showGameLog);\n setUpdateScroll(!updateScroll);\n };\n\n if (!showChat) {\n return ;\n }\n\n if (\n isMobile &&\n (chatState === CHAT_STATE.NORMAL || chatState === CHAT_STATE.COLLAPSED)\n ) {\n return (\n setChatState(CHAT_STATE.EXPANDED)}>\n \n \n );\n }\n\n return (\n \n {isMobile && (\n {\n setChatState(CHAT_STATE.COLLAPSED);\n }}\n />\n )}\n \n \n \n \n \n \n \n \n );\n};\n\nconst ChatWrapper = styled.div`\n margin-top: 20px;\n border: 2px solid ${({ theme }) => theme.foreground};\n border-radius: 20px;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n overflow: hidden;\n position: absolute;\n z-index: 3;\n bottom: 0;\n ${(props) => (props.betButtonsOnRight ? `left: 20px` : `right: 20px`)}\n ${(props) =>\n props.isMTTLobby ? `width: 20%; height: 80%;` : `width: 40%; height: 25%;`}\n transition: 0.3s ease all;\n resize: horizontal;\n\n ${(props) =>\n props.isFocused &&\n `\n height: 200px;\n `}\n ${(props) =>\n props.expanded &&\n `\n height: 80vh;\n `}\n ${(props) =>\n props.collapsed &&\n `\n height: 40px;\n `}\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 80%;\n width: 100%;\n bottom: 0px;\n left: 0;\n z-index: 4;\n }\n`;\n\nconst InnerChatWrapper = styled.div`\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: ${({ theme }) => theme.chat.background};\n`;\n\n/**Mobile styles */\nconst ChatButton = styled.div`\n position: absolute;\n z-index: 6;\n background: ${({ theme }) => theme.primary};\n color: white;\n height: 50px;\n width: 50px;\n border-radius: 5px;\n bottom: 20px;\n left: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n bottom: 15px;\n position: fixed;\n }\n`;\n\nconst WhiteBack = styled.div`\n position: absolute;\n background: rgba(255, 255, 255, 0.8);\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n`;\n\nexport default Chat;\n","import styled, { css } from \"styled-components\";\nimport { Tooltip } from \"react-bootstrap\";\nimport { SmallBodyText, LargeBodyText } from \"./Text\";\n\nexport const MainOverlay = styled.div`\n position: fixed;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n ${(props) => props.theme && `background: ${props.theme.modal.overlay};`}\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-around;\n opacity: ${(props) => (props.opacity == null ? 0.8 : props.opacity)};\n z-index: ${(props) => (props.zIndex == null ? 2 : props.zIndex)};\n transition: ${(props) =>\n props.transitionTimeSeconds == null\n ? \"0.4\"\n : props.transitionTimeSeconds}s\n ease all;\n ${(props) =>\n props.hide &&\n `\n opacity: 0;\n pointer-events: none;\n `}\n`;\n\nexport const removeScrollbars = css`\n overflow-y: hidden;\n overflow-x: hidden;\n &:hover {\n overflow-y: scroll;\n overflow-x: scroll;\n }\n &::-webkit-scrollbar {\n // Chrome scrollbar hiding\n width: 0 !important;\n display: none;\n }\n scrollbar-width: none;\n -ms-overflow-style: none; /* For Firefox and IE */\n overflow: -moz-scrollbars-none; /* this will hide the scrollbar in mozilla based browsers */\n -ms-overflow-style: none; /* this will hide the scrollbar in internet explorers */\n`;\n\nexport const OverlayWrapper = styled(Tooltip)`\n margin-left: 10px !important;\n .tooltip-inner {\n max-width: ${(props) => props.maxWidth || \"400px\"}\n padding: 15px;\n border-radius: 10px;\n }\n`;\n\nexport const ListWrapper = styled.div`\n display: flex;\n margin: 5px;\n justify-content: center;\n`;\n\nexport const ListBodyText = styled(SmallBodyText)`\n font-size: 16px !important;\n`;\n\nexport const InfoIcon = styled(LargeBodyText)`\n margin-left: 10px;\n margin-bottom: 4px;\n margin-top: 2px;\n padding: 0 10px;\n border-radius: 50px;\n font-size: 20px;\n font-weight: bold;\n background-color: ${({ theme }) => theme.text.label};\n color: ${({ theme }) => theme.modal.background};\n`;\n","/**\n * Add all new modals to be rendered into components/Global/ModalConductor/index.js\n */\nimport React, { Fragment, useState, useRef, useEffect } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { ModalButton } from \"../FormElements\";\nimport { MediumHeaderText, SmallBodyText } from \"../Text\";\nimport { styles } from \"../../helpers/styles\";\nimport { MainOverlay, removeScrollbars } from \"../Overlay\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\n\nconst transitionTime = 1200;\nconst Modal = ({\n title,\n subtitle,\n children,\n btnText,\n onBtnClick,\n btnTextAlt,\n onBtnAltClick,\n show,\n transitionOut,\n loading,\n margin,\n onOverlayClick,\n align,\n ctaContent,\n isInvalid,\n dataTestId,\n dataTestAltId,\n fullWidthInput,\n scrollMiddle,\n}) => {\n const [actionDone, setActionDone] = useState(false);\n const modalContainerRef = useRef(null);\n\n const onCTAClick = (func) => async (e) => {\n setActionDone(true);\n const promise = func(e);\n if (func && promise)\n promise.then((isDone) => {\n if (!isDone) {\n setActionDone(false);\n }\n });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, []);\n\n const scrollToBottom = () => {\n if (modalContainerRef.current) {\n modalContainerRef.current.scrollTop = scrollMiddle\n ? (modalContainerRef.current.scrollHeight -\n modalContainerRef.current.clientHeight) /\n 2\n : 0;\n }\n };\n\n if (!show) {\n return ;\n }\n\n return (\n \n \n \n \n \n \n {(title || subtitle) && (\n \n {title && {title} }\n {subtitle && {subtitle} }\n \n )}\n {children}\n \n {btnText && (\n \n {ctaContent}\n {btnTextAlt && (\n \n {btnTextAlt}\n \n )}\n \n {btnText}\n \n \n )}\n \n \n \n \n );\n};\n\nconst ModalOuter = styled.div`\n position: fixed;\n overflow: scroll; // overriden by removeScrollbars\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n z-index: 8;\n ${(props) =>\n props.transitionOut &&\n css`\n pointer-events: none;\n ${ModalWrapper} {\n margin-top: 100px;\n opacity: 0;\n pointer-events: none;\n }\n `}\n ${(props) =>\n props.isLoading &&\n css`\n pointer-events: none;\n ${ModalContent} > * {\n opacity: 0.5;\n }\n ${CTABottom} {\n opacity: 0.5;\n }\n `}\n ${removeScrollbars}\n`;\n\nexport const CenterModal = styled.div`\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n ${(props) => props.margin && `margin: 100px 0`};\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n ${(props) => props.margin && `margin: 100px 0 200px 0`};\n }\n`;\n\nconst ModalWidthWrapper = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-around;\n`;\n\nconst ModalWrapper = styled.div`\n width: 50vw;\n max-width: 90%;\n min-width: 300px;\n z-index: 13;\n border-radius: 5px;\n overflow: hidden;\n transition: 0.8s ease all;\n box-shadow: ${styles.largeBoxShadowDark};\n background: ${(props) => props.theme && props.theme.modal.background};\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 70vw;\n }\n`;\n\nconst bottomCTAHeight = 70;\nexport const ModalContent = styled.div`\n padding: 30px 0px 30px 0px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-around;\n justify-self: center;\n &:hover,\n &:focus {\n outline: 0;\n }\n`;\n\nconst HeaderWrapper = styled.div`\n border-bottom: 1px solid ${({ theme }) => theme.modal.divider};\n padding-bottom: 20px;\n margin-bottom: 20px;\n width: 100%;\n`;\n\nconst ModalTitle = styled(MediumHeaderText)`\n color: ${({ theme }) => theme.tertiary};\n text-transform: uppercase;\n`;\n\nconst ModalSubTitle = styled(SmallBodyText)`\n color: ${({ theme }) => theme.modal.subtitle};\n text-transform: uppercase;\n text-align: center;\n`;\n\nconst CTABottom = styled.div`\n bottom: 0;\n width: 100%;\n background: ${({ theme }) => theme.modal.cta};\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 15px 30px;\n height: ${bottomCTAHeight}px;\n transition: 0.2s ease all;\n z-index: -1;\n`;\n\nexport default Modal;\nexport { transitionTime };\n","export const arraysEqual = (_arr1, _arr2) => {\n if (\n !Array.isArray(_arr1) ||\n !Array.isArray(_arr2) ||\n _arr1.length !== _arr2.length\n )\n return false;\n var arr1 = _arr1.concat().sort();\n var arr2 = _arr2.concat().sort();\n\n for (var i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n return true;\n};\n\nexport const removeNumberCommas = (str) => {\n if (str === null) return null;\n return str.replace(/,/g, \"\");\n};\n\nexport const insertNumberCommas = (str) => {\n if (str === \"\") return \"\";\n else if (str.charAt(0) === \"-\") return \"-\" + insertNumberCommas(str.slice(1));\n const reversedStr = str.split(\"\").reverse().join(\"\");\n const splitStr = reversedStr.match(/.{1,3}/g);\n const joinedStr = splitStr.join(\",\");\n return joinedStr.split(\"\").reverse().join(\"\");\n};\n\nexport const insertFloatCommas = (str) => {\n const commaIndex = str.search(\"[.]\");\n return (\n insertNumberCommas(str.slice(0, commaIndex)) +\n str.slice(commaIndex, str.length)\n );\n};\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport { colors, font } from \"../../../helpers/styles\";\nimport { isInvalidOrEmpty } from \"../../../helpers/utils\";\nimport {\n removeNumberCommas,\n insertNumberCommas,\n} from \"../../../helpers/calculations\";\nimport { InputText, InputField } from \"../../../shared/FormElements\";\nimport { SmallHeaderText } from \"../../../shared/Text\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\n\nconst GameSettingsChoice = ({\n setSmallBlindCents,\n setBigBlindCents,\n smallBlindCents,\n bigBlindCents,\n isInCents,\n setIsInCents,\n}) => {\n const onSmallBlindChange = (e) => {\n const val = removeNumberCommas(e.target.value);\n if (!isInvalidOrEmpty(val)) {\n setSmallBlindCents(val);\n setBigBlindCents(val * 2);\n }\n };\n\n return (\n \n \n \n \n \n \n /{bigBlindCents ? insertNumberCommas(bigBlindCents.toString()) : \"10\"}{\" \"}\n \n setIsInCents(true)}>\n cents \n \n setIsInCents(false)}>\n chips \n \n \n \n );\n};\n\nconst ChoiceWrapper = styled.div`\n display: flex;\n border-radius: 5px;\n overflow: hidden;\n`;\n\nconst BlindsWrapper = styled(InputField)`\n margin-right: -5px;\n text-align: auto;\n width: auto;\n`;\n\nconst BigBlindsWrapper = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 60px;\n padding: 0 8px;\n text-align: left;\n color: ${colors.grey};\n`;\n\nconst SmallBlind = styled(InputText)`\n width: 130px;\n border-radius: 0;\n text-align: right;\n font-family: ${font.header};\n letter-spacing: 1px;\n font-size: 18px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 50px;\n margin: 0;\n }\n`;\n\nconst CentsSwitch = styled.div`\n margin-left: 4px;\n border-radius: 2px;\n border: 1px solid ${({ theme }) => theme.foreground};\n padding: 3px 4px;\n opacity: 30%;\n cursor: pointer;\n ${(props) =>\n props.isInCents &&\n `\n background: ${props.theme.foreground};\n opacity: 100%;\n `}\n`;\n\nconst ChipsSwitch = styled.div`\n border-radius: 2px;\n border: 1px solid ${({ theme }) => theme.foreground};\n padding: 3px 4px;\n opacity: 30%;\n cursor: pointer;\n ${(props) =>\n !props.isInCents &&\n `\n background: ${props.theme.foreground};\n opacity: 100%;\n `}\n`;\n\nexport default GameSettingsChoice;\n","import React from \"react\";\nimport { MediumHeaderText } from \"../../../shared/Text\";\nimport {\n OverlayWrapper,\n ListWrapper,\n ListBodyText,\n} from \"../../../shared/Overlay\";\n\nexport const TournamentInfoOverlay = (props) => {\n return (\n \n Tournament Mode Features \n \n \n \n Increasing Blinds \n \n \n Mandatory Timer \n \n \n Automatic Buy Ins \n \n \n Disabled Buy In Requests \n \n \n \n \n );\n};\n\nexport const GodModeOverlay = (props) => {\n return (\n \n God Mode Features \n \n \n Players have the option to show or hide their cards from the creator\n of the game. This mode is ideal for teaching or streaming.\n \n \n \n );\n};\n","import React, { useState, useContext, useEffect } from \"react\";\nimport styled from \"styled-components\";\nimport { GameContext } from \"../context/GameContext\";\nimport { SmallHeaderText, MediumHeaderText, LargeHeaderText } from \"./Text\";\nimport { timer } from \"../helpers/constants\";\nimport { formatCurrency } from \"../helpers/utils\";\n\n// hardcoded for now\nconst blindCoefficients = [1, 2, 3, 5, 10, 15, 25, 50, 75, 100, 150, 200];\n\nconst BlindStructureScroll = ({\n initialBlindCents,\n blindIncreaseMinutes,\n showTitle,\n showCurrentBlinds,\n isInCents,\n}) => {\n const { smallBlindCents, gameStartTime } = useContext(GameContext);\n\n const [gameTimeSeconds, setGameTimeSeconds] = useState(0);\n\n useEffect(() => {\n if (gameStartTime) {\n setTimeout(() => {\n setGameTimeSeconds(\n Math.round(\n (Date.now() - gameStartTime - timer.INITIAL_TIME_MILLISECONDS) /\n 1000\n )\n );\n }, 1000);\n }\n });\n\n const parsePixels = (translate) => {\n if (translate === \"\") return 0;\n return parseFloat(translate.substring(11, translate.length - 3));\n };\n\n const scrollLeft = () => {\n const container = document.getElementById(\"blinds-container\");\n container.style.transform = `translateX(${Math.min(\n 0,\n parsePixels(container.style.transform) + 400\n )}px)`;\n };\n\n const scrollRight = () => {\n const container = document.getElementById(\"blinds-container\");\n container.style.transform = `translateX(${Math.max(\n -(container.scrollWidth - container.offsetWidth),\n parsePixels(container.style.transform) - 400\n )}px)`;\n };\n\n const formatSeconds = (seconds) => {\n if (seconds < 10) {\n return `0:0${seconds}`;\n } else if (seconds < 60) {\n return `0:${seconds}`;\n } else {\n return `${Math.floor(seconds / 60)}:${\n seconds % 60 < 10 ? `0${seconds % 60}` : seconds % 60\n }`;\n }\n };\n\n let items = blindCoefficients.map((blind, idx) => {\n if (\n showCurrentBlinds &&\n gameStartTime &&\n blind * initialBlindCents === smallBlindCents\n ) {\n return (\n \n {idx * blindIncreaseMinutes} min \n \n {formatCurrency(blind * initialBlindCents, isInCents)}/\n {formatCurrency(2 * blind * initialBlindCents, isInCents)}\n \n \n );\n } else {\n return (\n \n {idx * blindIncreaseMinutes} min \n \n {formatCurrency(blind * initialBlindCents, isInCents)}/\n {formatCurrency(2 * blind * initialBlindCents, isInCents)}\n \n \n );\n }\n });\n items.push( );\n\n return (\n \n {showTitle && Blind Structure }\n {showCurrentBlinds && (\n \n {gameStartTime\n ? `Current Game Time: ${formatSeconds(gameTimeSeconds)}`\n : \"Game has not started\"}\n \n )}\n \n {\"<\"} \n \n {items} \n \n {\">\"} \n \n \n );\n};\n\nconst StructureWrapper = styled.div`\n width: 100%;\n color: ${({ theme }) => theme.text.label};\n .disabled {\n display: none;\n }\n .active {\n display: block;\n }\n`;\n\nconst ScrollWrapper = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst BlindsWrapper = styled.div`\n width: 80%;\n overflow: hidden;\n`;\n\nconst BlindsContainer = styled.div`\n display: flex;\n transform: translateX(0px);\n transition: 0.5s all ease-in-out;\n`;\n\nconst BlindWrapper = styled.div`\n text-align: center;\n padding: 5px 15px;\n border-left: 1px solid ${({ theme }) => theme.text.label};\n`;\n\nconst CurrentBlindWrapper = styled.div`\n text-align: center;\n padding: 5px 15px;\n border-left: 1px solid ${({ theme }) => theme.text.label};\n color: ${({ theme }) => theme.tertiary};\n`;\n\nconst Arrow = styled(LargeHeaderText)`\n text-align: center;\n width: 50px;\n height: 20px;\n cursor: pointer;\n`;\n\nexport default BlindStructureScroll;\n","import React, { useState, useContext, useEffect } from \"react\";\nimport { OverlayTrigger } from \"react-bootstrap\";\nimport styled, { css } from \"styled-components\";\nimport Modal from \"../../../shared/Modal\";\nimport GameSettingsChoice from \"./GameSettingsChoice\";\nimport { TournamentInfoOverlay, GodModeOverlay } from \"./InfoOverlay\";\nimport BlindStructureScroll from \"../../../shared/BlindStructureScroll\";\nimport { MediumHeaderText, SmallBodyText } from \"../../../shared/Text\";\nimport {\n InputText,\n InputField,\n InputLabel,\n} from \"../../../shared/FormElements\";\nimport { InfoIcon } from \"../../../shared/Overlay\";\nimport {\n Accordion,\n AccordionItem,\n AccordionItemPanel,\n} from \"react-accessible-accordion\";\nimport { isInvalidOrEmpty, formatCurrency } from \"../../../helpers/utils\";\nimport {\n removeNumberCommas,\n insertNumberCommas,\n} from \"../../../helpers/calculations\";\nimport { colors } from \"../../../helpers/styles\";\nimport { blinds, timer, buyin } from \"../../../helpers/constants\";\nimport Switch from \"../../../shared/FormElements/Switch\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\n\nconst GameSettingsModal = (props) => {\n const [smallBlindCents, setSmallBlindCents] = useState(\n blinds.DEFAULT_SMALL_BLIND_CENTS\n );\n const [bigBlindCents, setBigBlindCents] = useState(\n blinds.DEFAULT_BIG_BLIND_CENTS\n );\n const [blindIncreaseMinutes, setBlindIncreaseMinutes] = useState(\n blinds.DEFAULT_BLIND_INCREASE_MINUTES\n );\n // timers don't exist yet\n // const [timer, setTimer] = useState(-1);\n const [isTimerEnabled, setIsTimerEnabled] = useState(false);\n const [timerLengthSeconds, setTimerLengthSeconds] = useState(\n timer.DEFAULT_TIMER_SECONDS\n );\n const [isGodModeEnabled, setIsGodModeEnabled] = useState(false);\n const [isTournament, setIsTournament] = useState(false);\n const [autoBuyInCents, setAutoBuyInCents] = useState(\n buyin.DEFAULT_AUTO_BUYIN_CENTS\n );\n const [gamePassword, setGamePassword] = useState(\"\");\n const { updateDisplay } = useContext(GameContext);\n const { isMobile } = useContext(BrowserContext);\n const [handTimerLengthSeconds, setHandTimerLengthSeconds] = useState(\n timer.MEDIUM_HAND_TIMER_SECONDS\n );\n const [isInCents, setIsInCents] = useState(true);\n const [autoBuyInFocus, setAutoBuyInFocus] = useState(false);\n\n useEffect(() => {\n if (!autoBuyInFocus) {\n // round betValueCents\n if (autoBuyInCents) {\n const autoBuyInCentsInt = parseInt(autoBuyInCents);\n setAutoBuyInCents(autoBuyInCentsInt);\n }\n }\n }, [autoBuyInFocus]);\n\n const onSaveButtonClick = () => {\n if (props.title === \"Create New Game\") {\n props._createGame(\n smallBlindCents,\n gamePassword,\n null,\n \"game\",\n isTimerEnabled,\n timerLengthSeconds,\n handTimerLengthSeconds,\n isGodModeEnabled,\n isTournament,\n autoBuyInCents,\n blindIncreaseMinutes * 60,\n isInCents\n );\n } else {\n // TODO, this is where you would make settings save during the game\n // this block represents the \"Game Settings\" modal as opposed to the \"Create New Game\" modal\n // for now just close it\n updateDisplay(\"play\");\n }\n return new Promise(() => true);\n };\n\n const onNumberInputChange = (e, cb) => {\n const val = removeNumberCommas(e.target.value);\n if (!isInvalidOrEmpty(val)) {\n cb(val);\n }\n };\n\n const getAutoBuyInValue = () => {\n const autoBuyInDollarsFloat = parseFloat(autoBuyInCents) / 100;\n if (isNaN(autoBuyInDollarsFloat) || autoBuyInDollarsFloat === null) {\n return \"\";\n }\n if (!isInCents) {\n return Math.round(autoBuyInCents, 0);\n }\n return autoBuyInFocus\n ? parseFloat(autoBuyInCents) / 100\n : autoBuyInDollarsFloat.toFixed(2);\n };\n\n const enterPressedForBet = (event) => {\n if (!isInCents && !/[0-9]/.test(event.key)) {\n event.preventDefault();\n }\n };\n\n const password = (\n \n \n Game password (optional)\n \n setGamePassword(e.target.value)}\n type=\"password\"\n placeholder=\"Game password (optional)\"\n contrast\n />\n \n );\n\n return (\n \n \n \n \n Blinds\n \n \n {isTournament\n ? \"Set your initial small/big blinds\"\n : \"Set your small/big blinds\"}\n \n {isTournament ? \"Blind structure shown below\" : \"\"}\n \n
\n setSmallBlindCents(val)}\n setBigBlindCents={(val) => setBigBlindCents(val)}\n isInCents={isInCents}\n setIsInCents={setIsInCents}\n />\n \n \n \n Timer \n \n {\n if (!isTournament) {\n setIsTimerEnabled(!isTimerEnabled);\n }\n }}\n />\n \n \n \n Timer (seconds) \n \n Set amount of time per turn\n \n \n onNumberInputChange(e, setTimerLengthSeconds)}\n placeholder={timer.DEFAULT_TIMER_SECONDS}\n modal\n />\n \n \n \n \n \n Tournament Mode \n \n ? \n \n \n {\n setIsTournament(!isTournament);\n setIsTimerEnabled(true);\n }}\n dataTestId={\"tournament-mode-switch\"}\n />\n \n \n \n \n {isInCents && (\n \n Automatic Buy-in (dollars)\n \n )}\n {!isInCents && (\n \n Automatic Buy-in (chips)\n \n )}\n \n Set automatic buy-in amount\n \n \n e.target.blur()}\n value={getAutoBuyInValue()}\n onChange={(e) =>\n setAutoBuyInCents(\n isInCents\n ? parseFloat(e.target.value).toFixed(2) * 100\n : e.target.value\n )\n }\n onKeyPress={enterPressedForBet}\n placeholder={formatCurrency(\n buyin.DEFAULT_AUTO_BUYIN_CENTS,\n isInCents\n )}\n onFocus={() => setAutoBuyInFocus(true)}\n onBlur={() => setAutoBuyInFocus(false)}\n modal\n />\n \n \n \n \n Increasing Blinds (minutes)\n \n \n Set increasing blinds time interval\n \n \n \n onNumberInputChange(e, setBlindIncreaseMinutes)\n }\n placeholder={buyin.DEFAULT_BLIND_INCREASE_MINUTES}\n modal\n />\n \n \n \n \n \n \n \n \n \n God Mode \n \n ? \n \n \n setIsGodModeEnabled(!isGodModeEnabled)}\n />\n \n {props.disableModalStyles && password}\n \n );\n};\n\nconst SettingsWrapper = styled.div`\n margin: 5px 0 10px 0;\n width: 100%;\n padding: 0 40px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n ${(props) =>\n props.disabled &&\n `\n opacity: 0.2;\n pointer-events: none;\n `}\n`;\n\nconst SettingsText = styled(MediumHeaderText)`\n width: auto;\n display: inline-block;\n`;\n\nconst SettingsLabel = styled.div`\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n margin-right: 20px;\n width: 100%;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst SettingsSubText = styled(SmallBodyText)`\n width: 100%;\n`;\n\nconst BigBlindInputText = styled(InputText)`\n height: 50px;\n text-align: right;\n font-size: 22px;\n width: 30%;\n`;\n\nconst AccordionWrapper = styled(Accordion)`\n width: 100%;\n`;\n\nexport const PasswordElement = styled(InputField)`\n input {\n transition: border 0.3s ease;\n }\n ${(props) =>\n !props.disableCustomStyles &&\n css`\n display: flex;\n input {\n width: 90%;\n }\n `}\n\n ${(props) =>\n props.invalid &&\n css`\n input {\n border: 1px solid ${colors.red};\n }\n `}\n ${(props) =>\n !props.disablePadding &&\n `\n padding-right: 10px;\n `}\n`;\n\nconst AutoBuyInInputText = styled(BigBlindInputText)`\n &::-webkit-outer-spin-button,\n &::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n -moz-appearance: textfield;\n`;\n\nexport default GameSettingsModal;\n","import React, { useState, useEffect } from \"react\";\nimport styled from \"styled-components\";\nimport Modal from \"../../../shared/Modal\";\nimport { MediumBodyText } from \"../../../shared/Text\";\n\nconst LandscapeSwitchModal = () => {\n const [isLandscape, setIsLandscape] = useState(true);\n const [overrideHide, setIsOverrideHide] = useState(false);\n\n useEffect(() => {\n checkDimensions();\n window.addEventListener(\"resize\", setScreenOrientation);\n // TODO @dmitri cleanup event listener memory leak, fix hook deps, this is being called too much\n return () => {\n window.removeEventListener(\"resize\", setScreenOrientation);\n };\n });\n\n const checkDimensions = (e) => {\n let width;\n let height;\n if (e) {\n width = e.target.innerWidth;\n height = e.target.innerHeight;\n } else {\n width = window.innerWidth;\n height = window.innerHeight;\n }\n setIsLandscape(width > height);\n };\n\n const setScreenOrientation = (e) => {\n checkDimensions(e);\n };\n return (\n {\n setIsOverrideHide(true);\n }}\n >\n \n \n {\n \"You're on portrait! Rotate your phone into landscape for a better experience.\"\n }\n \n \n \n );\n};\n\nconst Wrapper = styled.div`\n padding: 0 20px;\n`;\n\nconst Label = styled(MediumBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport default LandscapeSwitchModal;\n","import React, { useState } from \"react\";\nimport styled from \"styled-components\";\nimport {\n InputText,\n InputField,\n InputLabel,\n} from \"../../../shared/FormElements\";\nimport Modal from \"../../../shared/Modal\";\nimport { PasswordElement } from \"../GameSettingsModal\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faTimes, faPencilAlt } from \"@fortawesome/free-solid-svg-icons\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport { faTwitter, faPatreon } from \"@fortawesome/free-brands-svg-icons\";\n\nconst NewPlayerModal = (props) => {\n const [username, setUsername] = useState(\n props.prefilledUsername ? props.prefilledGamePassword : \"\"\n );\n const [userPassword, setUserPassword] = useState(\"\");\n const [gamePassword, setGamePassword] = useState(\"\");\n const [invalidGamePassword, setIsInvalidGamePassword] = useState(false);\n const [invalidUserPassword, setIsInvalidUserPassword] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n\n const handleUsernameChange = (e) => {\n setUsername(e.target.value);\n };\n\n const handleUserPasswordChange = (e) => {\n setUserPassword(e.target.value);\n setIsInvalidUserPassword(false);\n };\n\n const handleGamePasswordChange = (e) => {\n setGamePassword(e.target.value);\n setIsInvalidGamePassword(false);\n };\n const onJoinClick = (e) => {\n if (username && username !== \"\") {\n return props\n ._logIn(\n props.prefilledUsername\n ? props.prefilledUsername.toLowerCase()\n : username.toLowerCase(),\n userPassword,\n props.prefilledGamePassword\n ? props.prefilledGamePassword\n : gamePassword,\n props.isGod\n )\n .then((response) => {\n if (response.error) {\n if (response.field && response.field.includes(\"Game\")) {\n setIsInvalidGamePassword(true);\n }\n if (response.field && response.field.includes(\"User\")) {\n setIsInvalidUserPassword(true);\n }\n return false;\n }\n return true;\n });\n }\n return new Promise(() => false);\n };\n\n const onSpectateClick = (e) => {\n if (username && username !== \"\") {\n return props\n ._spectateGame(\n props.prefilledUsername\n ? props.prefilledUsername.toLowerCase()\n : username.toLowerCase(),\n userPassword,\n props.prefilledGamePassword\n ? props.prefilledGamePassword\n : gamePassword,\n props.isGod\n )\n .then((response) => {\n if (response.error) {\n if (response.field && response.field.includes(\"Game\")) {\n setIsInvalidGamePassword(true);\n }\n if (response.field && response.field.includes(\"User\")) {\n setIsInvalidUserPassword(true);\n }\n return false;\n }\n return true;\n });\n }\n return new Promise(() => false);\n };\n\n const checkOnEnter = (event) => {\n var code = event.keyCode || event.which;\n if (code === 13) {\n setIsLoading(true);\n onJoinClick(event);\n }\n };\n\n let gamePassField = ``;\n if (props.hasGamePassword) {\n gamePassField = (\n \n \n Game Password\n \n \n \n \n );\n }\n\n return (\n \n \n \n Username\n \n \n \n \n \n User Password (optional)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n};\n\nconst FullWidthInput = styled(InputText)`\n width: 100%;\n`;\n\nconst InvalidIcon = styled(FontAwesomeIcon)`\n position: absolute;\n right: 30px;\n ${(props) => props.is_user_password_field && `right: 20px;`}\n top: 50%;\n transform: translateY(-50%);\n color: ${({ theme }) => theme.tertiary};\n height: 10px;\n transition: opacity 0.3s ease;\n ${(props) =>\n props.hide &&\n `\n opacity: 0;\n cursor-events: none;\n `}\n`;\n\nconst MediaContainer = styled.div`\n margin-top: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst MediaIcon = styled(Icon)`\n margin: 0 10px;\n`;\n\nexport default NewPlayerModal;\n","import React from \"react\";\nimport { SmallBodyText, HighlightedText } from \"../../../shared/Text\";\nimport Modal from \"../../../shared/Modal\";\n\nconst SiteIntroModal = (props) => {\n return (\n \n \n \n lipoker is the simplest, fastest way to play poker in browser. \n Just share the link to invite your friends to play! \n {props.numGames} games played!\n \n \n No downloads. No signup. No hassle. \n \n
\n \n );\n};\n\nexport default SiteIntroModal;\n","import React from \"react\";\nimport Modal from \"../../../shared/Modal\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\n\nconst DisconnectedModal = ({ show, onBtnClick, hasJoinedGame }) => {\n let innerText = hasJoinedGame ? (\n \n Our server is updating! Don't worry, your game will resume\n \n exactly where you left off.\n \n \n You should automatically reconnect in a minute. If not, try refreshing the\n \n page in a minute to resume playing!\n \n ) : (\n \n Our server is updating! It should only take a minute.\n \n \n Refresh the page soon to try again.\n \n );\n return (\n \n {innerText}
\n \n );\n};\n\nconst Label = styled(SmallBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport default DisconnectedModal;\n","import React from \"react\";\nimport Modal from \"../../../shared/Modal\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\n\nconst NoInternetModal = ({ show, onBtnClick }) => {\n let wifiDisconnectText = (\n \n Your internet disconnected! Don't worry, your game will resume\n \n exactly where you left off.\n \n Just refresh once your internet is more stable -- you'll\n \n automatically check/fold till then.\n \n );\n return (\n \n {wifiDisconnectText}
\n \n );\n};\n\nconst Label = styled(SmallBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport default NoInternetModal;\n","import React from \"react\";\nimport Modal from \"../../../shared/Modal\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\n\nconst DifferentLogInModal = ({ show, onBtnClick, isSpectating }) => {\n return (\n \n {!isSpectating ? (\n \n \n Your account in this game has been opened from somewhere else.\n \n \n Refresh the page to reclaim control of your account! Consider\n a strong password to make sure no one else can log in as you\n and\n \n see your cards.\n \n
\n ) : (\n \n \n Your spectator account in this game has been opened from somewhere\n else.\n \n \n Refresh the page to reclaim control of your account! Consider\n a strong password to make sure no one else can log in as you.\n \n \n
\n )}\n \n );\n};\n\nconst Label = styled(SmallBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport default DifferentLogInModal;\n","import {\n faSmileBeam,\n faSmile,\n faMeh,\n faFrown,\n faFrownOpen,\n} from \"@fortawesome/free-solid-svg-icons\";\n\nexport const optionsOrderedByRating = [\n {\n icon: faFrownOpen,\n textResponse: \"Oh no :( How can we make your experience better?\",\n },\n {\n icon: faFrown,\n textResponse: \"Oh no :( How can we make your experience better?\",\n },\n {\n icon: faMeh,\n textResponse: \"How can we make your experience better?\",\n },\n {\n icon: faSmile,\n textResponse: \"Great! What do you like or want to see more of?\",\n },\n {\n icon: faSmileBeam,\n textResponse: \"Great! What do you like or want to see more of?\",\n },\n];\n","import React, { useState, useEffect } from \"react\";\nimport { sendFeedback } from \"../../../helpers/api\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport { MediumBodyText, SmallBodyText } from \"../../../shared/Text\";\nimport { InputText, Icon, TextAreaInput } from \"../../../shared/FormElements\";\nimport { optionsOrderedByRating } from \"./options\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport styled from \"styled-components\";\nimport { logError } from \"../../../helpers/logger\";\nimport { faHeart } from \"@fortawesome/free-solid-svg-icons\";\n\n/**\n * Required to submit feedback:\n * rating\n * OR\n * rating + text feedback + email (if you add text feedback, email required)\n * OR\n * rating + beta interested + email (if you heart react, email required)\n */\nconst FeedbackModal = (props) => {\n const [rating, setRating] = useState(-1);\n const [email, setEmail] = useState(\"\");\n const [textareaText, setTextareaText] = useState(\"\");\n const [betaInterest, setBetaInterest] = useState(-1);\n const [isLoadingAfterSend, setIsLoadingAfterSend] = useState(false);\n const [startTransition, setStartTransition] = useState(true);\n\n const _reset = () => {\n setRating(-1);\n setTextareaText(\"\");\n setBetaInterest(-1);\n };\n\n const submitFeedback = () => {\n if (rating !== -1) {\n setIsLoadingAfterSend(true);\n return sendFeedback(\n props.gameId,\n props.username,\n email,\n rating + 1,\n textareaText,\n betaInterest,\n props.provider\n )\n .then((_) => {\n setStartTransition(true);\n setIsLoadingAfterSend(false);\n setTimeout(() => {\n props.hideModal(true);\n _reset();\n }, transitionTime);\n return false;\n })\n .catch((err) => {\n logError(err);\n return false;\n });\n }\n return Promise.resolve(false);\n };\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n props.hideModal(false);\n }, transitionTime);\n };\n\n useEffect(() => {\n // if showing again, reset\n if (props.show) {\n setStartTransition(false);\n }\n }, [props.show]);\n\n return (\n \n \n \n How happy are you with LiPoker? \n \n {optionsOrderedByRating.map(({ icon }, i) => (\n setRating(i)}\n />\n ))}\n \n \n {rating !== -1 && (\n <>\n setTextareaText(e.target.value)}\n />\n \n setEmail(e.target.value)}\n type=\"email\"\n placeholder={textareaText.length === 0 ? \"Email\" : \"Email *\"}\n />\n {textareaText.length !== 0 && (\n {`* required: We'd love to let you know once we implement your suggestion!`} \n )}\n {textareaText.length === 0 && betaInterest === 1 && (\n {`* required: We'd love to let you know once we address your feedback!`} \n )}\n \n setBetaInterest(-betaInterest)}\n />\n {`Heart react if you'd be open to working with us to beta test and provide feedback!`} \n \n {rating >= 3 && (\n \n \n {\n \"Like every site, Lipoker needs resources. Please consider making a donation \"\n }\n \n here\n \n {\". Thank you for your tremendous support.\"}\n \n \n )}\n >\n )}\n \n \n );\n};\n\nconst Label = styled(MediumBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst SmallLabel = styled(SmallBodyText)`\n color: ${({ theme }) => theme.text.placeholder};\n margin-left: 2px;\n margin-top: 3px;\n`;\n\nconst FeedbackModalInner = styled.div`\n display: flex;\n flex-direction: column;\n width: 100%;\n padding: 0 80px;\n ${(props) =>\n props.disable &&\n `\n opacity: 0.5;\n pointer-events: none;\n `}\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 0 50px;\n }\n`;\n\nconst FeedbackHeader = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst OptionPicker = styled.div`\n display: flex;\n margin-top: 10px;\n width: 100%;\n justify-content: space-between;\n`;\n\nconst Option = styled(Icon)`\n color: ${(props) =>\n props.selected\n ? ({ theme }) => theme.tertiary\n : ({ theme }) => theme.foreground};\n ${(props) => props.index === 0 && `margin-left: 0px;`}\n transition: 0.2s ease all;\n &:hover {\n transform: scale(1.1);\n }\n`;\n\nconst HeartWrapper = styled.div`\n display: flex;\n flex-direction: row;\n margin-top: 20px;\n align-items: center;\n`;\n\nconst HeartLabel = styled(SmallLabel)`\n margin-left: 6px;\n`;\n\nconst InputLine = styled(InputText)`\n width: 100%;\n margin-right: 10px;\n`;\n\nexport default FeedbackModal;\n","import React from \"react\";\nimport { SmallBodyText, MediumBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\n\nexport const Mersenne = () => {\n return (\n \n We use the Mersenne Twister, one of the most reliable and widely used\n random number generators (also certified and recommended by iTechLabs in\n 2017 for online gaming). Moreover, it is the default PRNG for many\n software systems, including Python, Microsoft Excel, MATLAB, R, Ruby, and\n more. For a more in-depth explanation about the Mersenne Twister, check\n out this{\" \"}\n \n link\n \n .\n \n );\n};\n\nexport const ShowHand = () => {\n return (\n \n The winners, players who have went all-in, and the last aggressor (or\n first person left of dealer if everyone checks) along with players who\n have increasingly better hands going clockwise-- show their cards. For the\n rest of the players who have not folded, they have the option to show\n their hands or not.\n \n );\n};\n\nexport const Philosophy = () => {\n return (\n \n Unlike other sites that present a complicated interface for you to play\n on, Lipoker is the only site built for making your online poker experience\n with friends as simple and enjoyable as possible. From video chat to\n customizable themes, our commitment is to create the best platform for you\n to play poker.\n \n );\n};\n\nexport const ContactUs = () => {\n return (\n \n Should you run into problems or have any questions, please contact us at{\" \"}\n lipoker.team@gmail.com !\n \n );\n};\n\nexport const Patreon = () => {\n return (\n \n We would love your support to further our commitment to providing you the\n best platform play online poker. This includes covering costs for our\n servers, development time for new features, and many other initiatives. If\n you are interested in making a donation, you can do so here:{\" \"}\n \n Lipoker@Patreon\n \n \n );\n};\n\n// rough draft\nexport const GamePlay = () => {\n return (\n \n \n We currently host No Limit Hold ‘em, the most popular variant of poker.\n
\n \n Definitions: \n Table Positions: \n Dealer - \n Big blind - \n Small blind - \n $$$:\n \n - Pot: total sum wagered by players in a single hand\n \n 5 Different Play Actions : \n Fold - player gives up their cards and takes no further part in the hand\n \n Bet - person who first wages a positive amount of money\n \n Check - players have the option to defer their bet (only if NO bet has\n occurred)\n \n Call - player matches the most recent bet\n \n Raise - player raises amount, forcing other players to match. Their\n raise amount must at least double the most recent bet.\n \n If you don’t have enough money to raise or call, you must go All-In\n (player places all his money in the pot) to remain in the hand.\n \n
\n $$$: - Pot: total sum wagered by players in a single hand 5 Different Play\n Actions : Fold - player gives up their cards and takes no further part in\n the hand Bet - person who first wages a positive amount of money Check -\n players have the option to defer their bet (only if NO bet has occurred)\n Call - player matches the most recent bet Raise - player raises amount,\n forcing other players to match. Their raise amount must at least double\n the most recent bet. If you don’t have enough money to raise or call, you\n must go All-In (player places all his money in the pot) to remain in the\n hand. Gameplay: Clockwise If at any point in time, all players in the hand\n decide to All-in, the gameplay jumps straight into showdown (described in\n post-flop section). Pre-flop: - This is the stage before any cards show up\n on the table. - After everyone has finished checking or calling, the game\n proceeds to the post-flop stage. Post-flop: - Community cards (3 cards): -\n Turn, river: 4th and 5th card dealt - If two or more players are still in\n the hand after the river, the gameplay proceeds to the showdown stage. -\n Showdown - All the remaining table cards are dealt - In general, the\n person with the best hand is declared the winner and collects the entire\n pot. - However, alternative endings may happen: - Tie - All-in’s -\n Side-pots\n \n );\n};\n\nconst AnswerWrapper = styled(MediumBodyText)`\n align-content: center;\n color: ${({ theme }) => theme.text.label};\n font-size: 17px;\n`;\n","import React, { useState, useEffect } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport {\n Accordion,\n AccordionItem,\n AccordionItemButton,\n AccordionItemPanel,\n} from \"react-accessible-accordion\";\nimport \"react-accessible-accordion/dist/fancy-example.css\";\nimport { SmallHeaderText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\nimport * as explanations from \"./QAs\";\n\nconst faqList = [\n {\n id: 0,\n question: \"Why was Lipoker created?\",\n answer: ,\n },\n {\n id: 1,\n question: \"How is the deck being shuffled?\",\n answer: ,\n },\n {\n id: 2,\n question: \"Who shows their cards during showdown?\",\n answer: ,\n },\n {\n id: 3,\n question: \"How can I donate?\",\n answer: ,\n },\n {\n id: 4,\n question: \"I have a few more questions, whom should I contact?\",\n answer: ,\n },\n];\n\nconst FAQModal = ({ hideModal, show, isFourColorDeck, isMobile }) => {\n const [startTransition, setStartTransition] = useState(true);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(false);\n }, transitionTime);\n };\n\n useEffect(() => {\n // if showing again, reset\n if (show) {\n setStartTransition(false);\n }\n }, [show]);\n\n let accordionList = faqList.map((x) => {\n return (\n \n \n {x.question} \n \n {x.answer} \n \n );\n });\n\n return (\n \n \n {accordionList}\n \n \n );\n};\n\nconst AccordionWrapper = styled(Accordion)`\n display: flex;\n flex-direction: column;\n align-content: center;\n padding: 6px 10%;\n width: 100%;\n`;\n\nconst QuestionHeader = styled(SmallHeaderText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst ButtonWrapper = styled(AccordionItemButton)`\n cursor: pointer;\n background: ${({ theme }) => theme.foreground};\n padding-top: 15px;\n padding-bottom: 12px;\n padding-left: 10px;\n padding-right: 10px;\n border-radius: 2px;\n &:hover {\n background: ${({ theme }) => theme.background};\n }\n outline: none;\n margin-bottom: 3px;\n`;\n\nexport default FAQModal;\n","import React from \"react\";\nimport { font, colors } from \"../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst CardBack = ({ style }) => {\n return (\n \n \n LP \n \n \n );\n};\n\nconst CardBackContent = styled.div`\n background: ${({ theme }) => theme.tertiary};\n border: 5px solid ${colors.white};\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n border-width: 3px;\n }\n`;\n\nconst LogoWrapper = styled.div`\n width: 25px;\n height: 25px;\n border-radius: 50%;\n border: 1px solid ${colors.white};\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: ${font.header};\n font-size: 12px;\n color: ${colors.white};\n span {\n margin: 2px 0 0 1px;\n letter-spacing: 0.6px;\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n border: none;\n }\n`;\n\nexport default CardBack;\n","import React from \"react\";\nimport { colors } from \"../../helpers/styles\";\nimport styled from \"styled-components\";\n\nconst CardEmpty = (props) => {\n return ;\n};\n\nconst CardEmptyContent = styled.div`\n height: 100%;\n width: 100%;\n border: 2px dashed ${colors.white};\n`;\n\nexport default CardEmpty;\n","import React from \"react\";\n\nconst Club = ({ color = \"#000000\", style }) => {\n return (\n \n \n \n \n \n \n );\n};\n\nexport default Club;\n","import React from \"react\";\n\nconst Diamond = ({ color = \"#000000\", style }) => {\n return (\n \n \n \n );\n};\n\nexport default Diamond;\n","import React from \"react\";\n\nconst Spade = ({ color = \"#000000\", style }) => {\n return (\n \n \n \n \n \n );\n};\n\nexport default Spade;\n","import React from \"react\";\n\nconst Heart = ({ color = \"#000000\", style }) => {\n return (\n \n \n \n \n \n );\n};\n\nexport default Heart;\n","const sizes = {\n lg: {\n height: \"95px\",\n width: \"72px\",\n iconHeight: \"30px\",\n iconBottom: \"5px\",\n fontSize: \"52px\",\n },\n md: {\n height: \"78px\",\n width: \"58px\",\n iconHeight: \"26px\",\n iconBottom: \"5px\",\n fontSize: \"44px\",\n },\n sm: {\n height: \"70px\",\n width: \"50px\",\n iconHeight: \"22px\",\n iconBottom: \"5px\",\n fontSize: \"42px\",\n },\n mobile: {\n height: \"50px\",\n width: \"35px\",\n iconHeight: \"14px\",\n iconBottom: \"5px\",\n fontSize: \"26px\",\n },\n};\nexport default sizes;\n","import React, { useEffect, useState } from \"react\";\nimport CardBack from \"./CardBack\";\nimport CardEmpty from \"./CardEmpty\";\nimport { Heart, Spade, Diamond, Club } from \"./suits\";\nimport sizes from \"./sizes\";\nimport styled, { withTheme } from \"styled-components\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport { colors, font } from \"../../helpers/styles\";\n\nconst Card = ({\n isFourColorDeck,\n theme,\n size,\n enforceCornerSuit,\n isEmpty,\n isBack,\n number,\n suit,\n handCardNumber,\n border,\n isFlat,\n isHighlighted,\n isFolded,\n style,\n}) => {\n const [color, setColor] = useState(\"\");\n const allSuits = [\"spade\", \"club\", \"diamond\", \"heart\"];\n\n useEffect(() => {\n const dark = [\"spade\", \"club\"];\n if (isFourColorDeck) {\n setColor(theme.card[suit]);\n } else if (dark.includes(suit)) {\n setColor(theme.card.dark);\n } else {\n setColor(theme.card.light);\n }\n }, [isFourColorDeck, theme]);\n\n const getSuit = () => {\n if (color.length === 0) {\n return <>>;\n }\n const sizeObj = sizes[size || \"md\"];\n let style = {\n height: sizeObj.iconHeight,\n };\n if (size !== \"mobile\" || enforceCornerSuit) {\n Object.assign(style, {\n bottom: sizeObj.iconBottom,\n right: \"5px\",\n position: \"absolute\",\n });\n }\n\n if (suit === \"spade\") {\n return ;\n } else if (suit === \"diamond\") {\n return ;\n } else if (suit === \"club\") {\n return ;\n } else {\n return ;\n }\n };\n\n let cardInner;\n if (isEmpty) {\n cardInner = ;\n } else if (isBack) {\n cardInner = ;\n } else {\n cardInner = (\n \n {number} \n {allSuits.includes(suit) && getSuit()}\n \n );\n }\n\n return (\n \n {cardInner}\n \n );\n};\n\nconst CardBase = styled.div`\n background: ${colors.white};\n ${(props) =>\n props.isFolded &&\n props.theme &&\n `\n background: ${props.theme.foreground};\n `}\n width: 100%;\n height: 100%;\n h1 {\n position: absolute;\n top: 3px;\n left: 8px;\n }\n img {\n position: absolute;\n bottom: 5px;\n right: 5px;\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n h1 {\n position: inherit;\n line-height: 22px;\n margin: 0;\n }\n img {\n position: inherit;\n }\n }\n`;\n\nconst CardWrapper = styled.div`\n position: relative;\n font-family: ${font.header};\n border-radius: 5px;\n overflow: hidden;\n ${(props) =>\n props.size &&\n `\n height: ${sizes[props.size][\"height\"]};\n width: ${sizes[props.size][\"width\"]};\n ${CardBase} img {\n height: ${sizes[props.size][\"iconHeight\"]};\n }\n ${CardBase} h1 {\n font-size: ${sizes[props.size][\"fontSize\"]};\n }\n `}\n ${(props) =>\n props.color &&\n `\n color: ${props.color}\n `}\n ${(props) =>\n props.handCardNumber === \"0\" &&\n `\n position: absolute;\n transform: rotate(-6deg);\n left: 17px;\n z-index: 2;\n `}\n ${(props) =>\n props.handCardNumber === \"1\" &&\n `\n position: absolute;\n transform: rotate(6deg);\n right: 17px;\n z-index: 1;\n `}\n ${(props) =>\n !props.isFlat && props.theme\n ? `\n border: 1px solid ${props.theme.foreground}\n `\n : \"margin: 5px;\"}\n ${(props) =>\n props.border &&\n props.theme &&\n `\n border: 1px solid ${props.theme.foreground}\n `}\n ${(props) =>\n props.isHighlighted &&\n props.theme &&\n `border: 3px solid ${props.theme.tertiary};`}\n ${(props) =>\n props.isFolded &&\n `\n opacity: 0.6;\n `}\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n height: ${sizes[\"mobile\"][\"height\"]};\n width: ${sizes[\"mobile\"][\"width\"]};\n ${CardBase} img {\n height: ${sizes[\"mobile\"][\"iconHeight\"]};\n }\n ${CardBase} h1 {\n font-size: ${sizes[\"mobile\"][\"fontSize\"]};\n }\n ${(props) =>\n props.handCardNumber === \"0\" &&\n `\n position: absolute;\n transform: rotate(-6deg);\n left: 12px;\n z-index: 2;\n `}\n ${(props) =>\n props.handCardNumber === \"1\" &&\n `\n position: absolute;\n transform: rotate(6deg);\n right: 12px;\n z-index: 1;\n `}\n ${(props) =>\n props.isFlat &&\n `\n margin: 2px;\n `}\n }\n`;\n\nconst cardHeightRaw = 55;\nconst mobileCardHeightRaw = 40;\nconst cardHeight = `${cardHeightRaw}px`;\nconst mobileCardHeight = `${mobileCardHeightRaw}px`;\n\nexport { cardHeight, mobileCardHeight, cardHeightRaw, mobileCardHeightRaw };\nexport default withTheme(Card);\n","export const nameMap = {\n \"High Card\": \"10. High Card\",\n Pair: \"9. Pair\",\n \"Two Pair\": \"8. Two Pair\",\n Trips: \"7. Three of a Kind\",\n Straight: \"6. Straight\",\n Flush: \"5. Flush\",\n \"Full House\": \"4. Full House\",\n Quads: \"3. Four of a Kind\",\n \"Straight Flush\": \"2. Straight Flush\",\n};\n\nexport const handsList = [\n {\n name: \"1. Royal Flush\",\n cards: [\n {\n suit: \"heart\",\n number: \"A\",\n },\n {\n suit: \"heart\",\n number: \"K\",\n },\n {\n suit: \"heart\",\n number: \"Q\",\n },\n {\n suit: \"heart\",\n number: \"J\",\n },\n {\n suit: \"heart\",\n number: \"10\",\n },\n ],\n },\n {\n name: \"2. Straight Flush\",\n cards: [\n {\n suit: \"heart\",\n number: \"5\",\n },\n {\n suit: \"heart\",\n number: \"4\",\n },\n {\n suit: \"heart\",\n number: \"3\",\n },\n {\n suit: \"heart\",\n number: \"2\",\n },\n {\n suit: \"heart\",\n number: \"A\",\n },\n ],\n },\n {\n name: \"3. Four of a Kind\",\n cards: [\n {\n suit: \"heart\",\n number: \"4\",\n },\n {\n suit: \"diamond\",\n number: \"4\",\n },\n {\n suit: \"club\",\n number: \"4\",\n },\n {\n suit: \"spade\",\n number: \"4\",\n },\n {\n isEmpty: true,\n },\n ],\n },\n {\n name: \"4. Full House\",\n cards: [\n {\n suit: \"club\",\n number: \"8\",\n },\n {\n suit: \"diamond\",\n number: \"8\",\n },\n {\n suit: \"club\",\n number: \"5\",\n },\n {\n suit: \"spade\",\n number: \"5\",\n },\n {\n suit: \"heart\",\n number: \"5\",\n },\n ],\n },\n {\n name: \"5. Flush\",\n cards: [\n {\n suit: \"club\",\n number: \"9\",\n },\n {\n suit: \"club\",\n number: \"2\",\n },\n {\n suit: \"club\",\n number: \"4\",\n },\n {\n suit: \"club\",\n number: \"6\",\n },\n {\n suit: \"club\",\n number: \"A\",\n },\n ],\n },\n {\n name: \"6. Straight\",\n cards: [\n {\n suit: \"spade\",\n number: \"9\",\n },\n {\n suit: \"diamond\",\n number: \"8\",\n },\n {\n suit: \"diamond\",\n number: \"7\",\n },\n {\n suit: \"club\",\n number: \"6\",\n },\n {\n suit: \"heart\",\n number: \"5\",\n },\n ],\n },\n {\n name: \"7. Three of a Kind\",\n cards: [\n {\n suit: \"spade\",\n number: \"2\",\n },\n {\n suit: \"heart\",\n number: \"2\",\n },\n {\n suit: \"diamond\",\n number: \"2\",\n },\n {\n isEmpty: true,\n },\n {\n isEmpty: true,\n },\n ],\n },\n {\n name: \"8. Two Pair\",\n cards: [\n {\n suit: \"club\",\n number: \"9\",\n },\n {\n suit: \"diamond\",\n number: \"9\",\n },\n {\n suit: \"spade\",\n number: \"K\",\n },\n {\n suit: \"diamond\",\n number: \"K\",\n },\n {\n isEmpty: true,\n },\n ],\n },\n {\n name: \"9. Pair\",\n cards: [\n {\n suit: \"heart\",\n number: \"6\",\n },\n {\n suit: \"club\",\n number: \"6\",\n },\n {\n isEmpty: true,\n },\n {\n isEmpty: true,\n },\n {\n isEmpty: true,\n },\n ],\n },\n {\n name: \"10. High Card\",\n cards: [\n {\n suit: \"spade\",\n number: \"A\",\n },\n {\n isEmpty: true,\n },\n {\n isEmpty: true,\n },\n {\n isEmpty: true,\n },\n {\n isEmpty: true,\n },\n ],\n },\n];\n","import React, { useState, useEffect } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport Card from \"../../../shared/Card\";\nimport { handsList, nameMap } from \"./handsList\";\nimport { MediumBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\n\nconst HandRankingsModal = ({\n hideModal,\n show,\n isFourColorDeck,\n isMobile,\n playerHand,\n}) => {\n const [startTransition, setStartTransition] = useState(true);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(false);\n }, transitionTime);\n };\n\n useEffect(() => {\n // if showing again, reset\n if (show) {\n setStartTransition(false);\n }\n }, [show]);\n\n return (\n \n {handsList.map((row, i) => {\n const cardsList = row.cards;\n const highlight =\n playerHand in nameMap ? nameMap[playerHand] === row.name : false;\n return (\n \n {row.name} \n \n {cardsList.map((card, j) => (\n \n ))}\n \n \n );\n })}\n \n );\n};\nconst HandRow = styled.div`\n display: flex;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n padding: 0 60px;\n`;\nconst CardsRow = styled.div`\n display: flex;\n ${(props) =>\n props.highlight &&\n props.theme &&\n `\n border: 2px solid ${props.theme.foreground};\n border-radius: 5px;\n background: ${props.theme.foreground};\n `}\n`;\n\nconst HandText = styled(MediumBodyText)`\n width: 100%;\n font-weight: bold;\n color: ${({ theme }) => theme.text.widget};\n`;\n\nexport default HandRankingsModal;\n","import React, { useState, useEffect } from \"react\";\nimport styled from \"styled-components\";\nimport { MediumHeaderText } from \"./Text\";\n\nconst TabView = ({ tabNamesToViews, defaultView, onTabChange }) => {\n const [currentView, setCurrentView] = useState(defaultView);\n const [activeIndex, setActiveIndex] = useState(0);\n\n useEffect(() => {\n setCurrentView(defaultView);\n }, [defaultView]);\n\n const tabsList = Object.keys(tabNamesToViews);\n return (\n <>\n \n {tabsList.map((tabName, i) => {\n return (\n {\n setCurrentView(tabName);\n onTabChange(tabName);\n setActiveIndex(i);\n }}\n isActive={currentView === tabName}\n >\n {tabName} \n \n );\n })}\n \n \n \n \n {tabNamesToViews[currentView]}\n >\n );\n};\n\nconst TabBar = styled.div`\n position: relative;\n width: 100%;\n height: 40px;\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n border-bottom: 1px solid ${({ theme }) => theme.modal.divider};\n`;\n\nconst TabItem = styled.div`\n width: 100%;\n height: 100%;\n color: ${({ theme }) => theme.tertiary};\n display: flex;\n align-items: flex-end;\n justify-content: center;\n cursor: pointer;\n ${(props) =>\n !props.isActive &&\n `\n opacity: 0.3;\n `}\n`;\n\nconst ActiveBar = styled.div`\n position: absolute;\n height: 5px;\n bottom: 0;\n transform: translate(0, 100%);\n transition: 0.3s ease left;\n padding: 0 6%;\n`;\n\nconst InnerBar = styled.div`\n background: ${({ theme }) => theme.tertiary};\n width: 100%;\n height: 100%;\n`;\n\nexport default TabView;\n","import styled from \"styled-components\";\nimport { colors, font } from \"../../helpers/styles\";\n\nimport { RadioGroup, Radio } from \"react-radio-group\";\n\nexport const StyledDiv = styled.div`\n float: right;\n margin-top: 10px;\n width: 30%;\n`;\n\nexport const StyledRadioLabel = styled.div`\n margin-right: 20px;\n width: 100%;\n color: ${({ theme }) => theme.text.label};\n font-family: ${font.body};\n`;\n\nexport const StyledRadioGroup = styled(RadioGroup)`\n margin-right: 0px;\n`;\n\nexport const StyledRadio = styled(Radio)`\n vertical-align: middle;\n margin-right: 10px;\n width: 18px;\n height: 18px;\n color: colors.lightRed;\n background: ${colors.white};\n`;\n","import { LargeBodyText } from \"../../../shared/Text\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport styled from \"styled-components\";\n\nexport const SettingsOption = styled.div`\n display: flex;\n justify-content: space-between;\n width: 100%;\n padding: 5px;\n align-items: center;\n margin: 12px 0;\n ${(props) =>\n props.disabled &&\n `\n opacity: 0.4;\n pointer-events: none;\n `}\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n border-right-width: 0;\n border-left-width: 0;\n border-radius: 0;\n margin: 0;\n }\n`;\n\nexport const SettingsText = styled(LargeBodyText)`\n margin-top: 2px;\n font-weight: bold;\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport const Wrapper = styled.div`\n width: 100%;\n height: 100%;\n padding: 20px 50px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 10px;\n }\n`;\n","import React from \"react\";\nimport { SmallBodyText } from \"./Text\";\nimport { Icon } from \"./FormElements\";\nimport { faTimes } from \"@fortawesome/free-solid-svg-icons\";\nimport { useStateWithLocalStorageBoolean } from \"../hooks/useStateWithLocalStorage\";\nimport styled, { withTheme } from \"styled-components\";\n\nconst CollapsibleNotice = ({ uniqueId, text, theme }) => {\n const [show, setShow] = useStateWithLocalStorageBoolean(uniqueId, true);\n\n if (!show) {\n return <>>;\n }\n\n return (\n \n {text}\n setShow(false)}\n />\n \n );\n};\n\nconst Wrapper = styled(SmallBodyText)`\n position: relative;\n text-align: center;\n color: ${({ theme }) => theme.text.label};\n border: 1px dashed ${({ theme }) => theme.text.label};\n padding: 10px 0;\n`;\n\nconst CancelIcon = styled(Icon)`\n position: absolute;\n right: 10px;\n top: 50%;\n transform: translate(0, -50%);\n cursor: pointer;\n`;\n\nexport default withTheme(CollapsibleNotice);\n","import React from \"react\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport Switch from \"../../../shared/FormElements/Switch\";\nimport {\n StyledDiv,\n StyledRadio,\n StyledRadioGroup,\n StyledRadioLabel,\n} from \"../../../shared/FormElements/RadioButton\";\nimport { InputText } from \"../../../shared/FormElements\";\nimport { Wrapper, SettingsOption, SettingsText } from \"./shared\";\nimport CollapsibleNotice from \"../../../shared/CollapsibleNotice\";\nimport styled from \"styled-components\";\nimport { blinds, timer } from \"../../../helpers/constants\";\n\nconst GameSettingsView = ({\n smallBlindSettings,\n setSmallBlindSettings,\n isTimerEnabledSettings,\n setIsTimerEnabledSettings,\n timerLengthSecondsSettings,\n setTimerLengthSecondsSettings,\n isTournament,\n isAdmin,\n handTimerLengthSecondsSettings,\n setHandTimerLengthSecondsSettings,\n useCentsSettings,\n setUseCentsSettings,\n adminPlayers,\n}) => {\n const stringifyPlayerNames = () => {\n const playerNames = adminPlayers.map((player) => player.playerName);\n return playerNames.join(\", \");\n };\n\n return (\n \n 1 ? \"s\" : \"\"\n } ${stringifyPlayerNames()} can change settings`\n }\n uniqueId=\"admin-settings-notice\"\n />\n \n \n Small Blind \n \n Change will go into effect on the next hand.\n \n \n setSmallBlindSettings(e.target.value)}\n placeholder={blinds.DEFAULT_SMALL_BLIND_CENTS}\n modal\n />\n {useCentsSettings && cents }\n {!useCentsSettings && chips }\n \n \n \n Use Money Denomination \n \n setUseCentsSettings(!useCentsSettings)}\n />\n \n \n \n Timer \n \n Change will go into effect on the next hand.\n \n \n \n setIsTimerEnabledSettings(!isTimerEnabledSettings)\n }\n />\n \n \n \n Timer (seconds) \n \n Change will go into effect on the next hand.\n \n \n setTimerLengthSecondsSettings(e.target.value)}\n placeholder={timer.DEFAULT_TIMER_SECONDS}\n modal\n />\n \n \n \n Delay Between Hands \n \n \n \n \n \n Slow (14)\n \n \n \n Medium (8)\n \n \n \n Fast (2)\n \n \n \n \n \n );\n};\n\nconst SettingsLabel = styled.div`\n margin-right: 20px;\n width: 100%;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst BigBlindInputText = styled(InputText)`\n height: 45px;\n text-align: right;\n font-size: 22px;\n width: 30%;\n margin-right: 5px;\n`;\n\nexport default GameSettingsView;\n","import React from \"react\";\nimport Switch from \"../../../shared/FormElements/Switch\";\nimport { Wrapper, SettingsOption, SettingsText } from \"./shared\";\n\nconst UserPreferencesView = ({\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n setJoinCall,\n isMobile,\n isFourColorDeck,\n setIsFourColorDeck,\n}) => {\n return (\n \n \n Gameplay Sound \n setSound(!sound)} />\n \n \n Four Color Deck \n setIsFourColorDeck(!isFourColorDeck)}\n />\n \n \n Enable Video Call \n {\n if (showVideoChat) {\n // we want to disable\n setShowVideoChat(false);\n setJoinCall(false);\n } else {\n // we want to show view, but not join yet\n setShowVideoChat(true);\n }\n }}\n />\n \n \n Video On Right \n setVideoIsOnRight(!videoIsOnRight)}\n />\n \n \n Bet Buttons On Right \n setBetButtonsOnRight(!betButtonsOnRight)}\n />\n \n \n );\n};\n\nexport default UserPreferencesView;\n","import React, { useState, useEffect, useCallback } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport TabView from \"../../../shared/TabView\";\nimport GameSettingsView from \"./GameSettingsView\";\nimport UserPreferencesView from \"./UserPreferencesView\";\nimport { emit } from \"../../../helpers/socket\";\n\nconst BrowserSettingsModal = ({\n gameId,\n username,\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n setJoinCall,\n hideModal,\n show,\n isMobile,\n smallBlindCents,\n isTimerEnabled,\n timerLengthSeconds, // timer.DEFAULT_TIMER_SECONDS\n handTimerLengthSeconds, // timer.MEDIUM_HAND_TIMER_SECONDS\n isFourColorDeck,\n setIsFourColorDeck,\n isSpectating,\n isTournament,\n isAdmin,\n isInCents,\n adminPlayers,\n startTransition,\n setStartTransition,\n}) => {\n const [btnText, setBtnText] = useState(\"Save\");\n const [disableSave, setDisableSave] = useState(false);\n const [smallBlindSettings, _setSmallBlindSettings] = useState(\n smallBlindCents\n );\n const [isTimerEnabledSettings, _setIsTimerEnabledSettings] = useState(\n isTimerEnabled\n );\n const [timerLengthSecondsSettings, _setTimerLengthSecondsSettings] = useState(\n timerLengthSeconds\n );\n const [\n handTimerLengthSecondsSettings,\n _setHandTimerLengthSecondsSettings,\n ] = useState(handTimerLengthSeconds);\n\n const [useCentsSettings, _setUseCentsSettings] = useState(isInCents);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(true);\n }, transitionTime);\n };\n\n const setSmallBlindSettings = useCallback(\n (newSmallBlindCentsSettings) => {\n if (newSmallBlindCentsSettings) {\n _setSmallBlindSettings(parseInt(newSmallBlindCentsSettings));\n } else {\n _setSmallBlindSettings(newSmallBlindCentsSettings);\n }\n setDisableSave(false);\n },\n [smallBlindCents]\n );\n\n const setIsTimerEnabledSettings = useCallback(\n (newIsTimerEnabledSettings) => {\n setDisableSave(false); // is there a reason the order of these two is switched above? or are state updates gauranteed to wait till fn call ends\n _setIsTimerEnabledSettings(newIsTimerEnabledSettings);\n },\n [isTimerEnabled]\n );\n\n const setTimerLengthSecondsSettings = useCallback(\n (newTimerLengthSecondsSettings) => {\n if (newTimerLengthSecondsSettings) {\n _setTimerLengthSecondsSettings(parseInt(newTimerLengthSecondsSettings));\n } else {\n _setTimerLengthSecondsSettings(newTimerLengthSecondsSettings);\n }\n setDisableSave(false);\n // setDisableSave logic is broken af -- it starts enabled, and then if 2 features are changed\n // then 1 is unchanged it stays disabled. maybe just set it to always be enabled\n },\n [timerLengthSeconds]\n );\n\n const setHandTimerLengthSecondsSettings = useCallback(\n (newHandTimerLengthSecondsSettings) => {\n _setHandTimerLengthSecondsSettings(newHandTimerLengthSecondsSettings);\n setDisableSave(false);\n },\n [handTimerLengthSeconds]\n );\n\n const setUseCentsSettings = useCallback(\n (newUseCentsSettings) => {\n _setUseCentsSettings(newUseCentsSettings);\n setDisableSave(false);\n },\n [isInCents]\n );\n\n useEffect(() => {\n setSmallBlindSettings(smallBlindCents);\n setIsTimerEnabledSettings(isTimerEnabled);\n setTimerLengthSecondsSettings(timerLengthSeconds);\n setHandTimerLengthSecondsSettings(handTimerLengthSeconds);\n setUseCentsSettings(isInCents);\n }, [\n setSmallBlindSettings,\n smallBlindCents,\n setIsTimerEnabledSettings,\n isTimerEnabled,\n setTimerLengthSecondsSettings,\n timerLengthSeconds,\n setHandTimerLengthSecondsSettings,\n handTimerLengthSeconds,\n setUseCentsSettings,\n isInCents,\n ]);\n const onSaveButtonClick = (e) => {\n emit(\"set_pending_settings_change\", {\n username: username,\n game_id: gameId,\n small_blind_amount: smallBlindSettings,\n is_timer_enabled: isTimerEnabledSettings,\n timer_length_seconds: timerLengthSecondsSettings,\n hand_timer_length_seconds: handTimerLengthSecondsSettings,\n is_in_cents: useCentsSettings,\n });\n close();\n return Promise.resolve(false);\n };\n\n let tabNamesToViews;\n\n if (!isSpectating) {\n tabNamesToViews = {\n Settings: (\n \n ),\n Preferences: (\n \n ),\n };\n } else {\n tabNamesToViews = {\n Preferences: (\n \n ),\n };\n }\n\n const handleTabChange = (newTab) => {\n if (newTab === \"Settings\") {\n setBtnText(\"Save\");\n } else {\n setBtnText(null);\n }\n };\n\n let defaultView = !isSpectating ? \"Settings\" : \"Preferences\";\n\n return (\n \n \n \n );\n};\n\nexport default BrowserSettingsModal;\n","import React, { useState, useEffect, useRef } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport { MediumBodyText } from \"../../../shared/Text\";\nimport { InputText } from \"../../../shared/FormElements\";\nimport { removeNumberCommas } from \"../../../helpers/calculations\";\nimport { formatCurrency } from \"../../../helpers/utils\";\nimport styled from \"styled-components\";\n\nconst BuyInModal = ({\n show,\n hideModal,\n emitSocketMessage,\n ownStackCents,\n isInCents,\n}) => {\n const [startTransition, setStartTransition] = useState(true);\n const [buyInDollars, setBuyInAmount] = useState(\"\");\n const [subtractDollars, setSubtractAmount] = useState(\"\");\n const buyInInputElement = useRef(null);\n const subtractInputElement = useRef(null);\n\n useEffect(() => {\n // if showing again, reset\n if (show) {\n setStartTransition(false);\n buyInInputElement.current.focus();\n }\n }, [show]);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(false);\n }, transitionTime);\n };\n\n const _isInvalidOrEmpty = (val) => {\n if (val === \"\") {\n setBuyInAmount(\"\");\n return true;\n }\n return isNaN(val);\n };\n\n const _isInvalidOrEmptySubtract = (val) => {\n if (val === \"\") {\n setSubtractAmount(\"\");\n return true;\n }\n return isNaN(val);\n };\n\n const onBuyInChange = (e) => {\n const val = removeNumberCommas(e.target.value);\n if (!_isInvalidOrEmpty(val)) {\n setBuyInAmount(val);\n }\n };\n\n const onSubtractChange = (e) => {\n const val = removeNumberCommas(e.target.value);\n if (!_isInvalidOrEmptySubtract(val)) {\n setSubtractAmount(val);\n }\n };\n\n const getTotalStackAmount = () => {\n let amountToAdd = 0;\n if (buyInDollars.length !== 0) {\n amountToAdd = parseFloat(buyInDollars);\n }\n if (subtractDollars.length !== 0) {\n amountToAdd -= parseFloat(subtractDollars);\n }\n if (isInCents) {\n amountToAdd *= 100;\n }\n return Math.round(ownStackCents + amountToAdd);\n };\n\n const sendSocketBuyIn = () => {\n let buyInNumber, subtractNumber;\n if (buyInDollars.length === 0) buyInNumber = 0;\n else buyInNumber = parseFloat(buyInDollars);\n if (subtractDollars.length === 0) subtractNumber = 0;\n else subtractNumber = parseFloat(subtractDollars);\n\n if (isInCents) {\n buyInNumber *= 100;\n subtractNumber *= 100;\n }\n\n emitSocketMessage(\"request_buy_in\", {\n amount: Math.round(buyInNumber - subtractNumber),\n });\n setBuyInAmount(\"\");\n setSubtractAmount(\"\");\n close();\n return Promise.resolve(false);\n };\n\n const onBuyInKeypress = (isInCents) => (event) => {\n if (!isInCents && !/[0-9]/.test(event.key)) {\n event.preventDefault();\n }\n var code = event.keyCode || event.which;\n if (\n code === 13 &&\n (buyInDollars.length !== 0 || subtractDollars.length !== 0)\n ) {\n sendSocketBuyIn();\n }\n };\n\n return (\n \n \n \n Your current stack \n {formatCurrency(ownStackCents, isInCents)} \n
\n \n Buy in for more \n \n \n + \n \n \n \n
\n \n Subtract from stack \n \n \n - \n \n \n \n
\n \n Your new stack would be \n \n {formatCurrency(getTotalStackAmount(), isInCents)}\n \n
\n \n \n );\n};\n\nconst BuyInWrapper = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n padding: 0 60px;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst Row = styled.div`\n width: 100%;\n display: flex;\n padding: 10px 0;\n ${(props) =>\n props.strong &&\n `\n font-weight: bold;\n `}\n`;\n\nconst LabelText = styled(MediumBodyText)`\n width: 100%;\n`;\n\nconst NumbersText = styled(MediumBodyText)`\n margin-right: 15px;\n`;\n\nconst InputWrapper = styled.div`\n display: flex;\n span {\n margin-right: 10px;\n font-weight: bold;\n }\n`;\n\nconst BuyInInput = styled(InputText)`\n width: 120px;\n text-align: right;\n`;\n\nexport default BuyInModal;\n","import React, { useState, useEffect } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport styled from \"styled-components\";\n\nconst RaisePercentInfoModal = ({ hideModal, show }) => {\n const [startTransition, setStartTransition] = useState(true);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(false);\n }, transitionTime);\n };\n\n useEffect(() => {\n // if showing again, reset\n if (show) {\n setStartTransition(false);\n }\n }, [show]);\n\n return (\n \n \n \n \n \n );\n};\n\nconst Wrapper = styled.div`\n padding: 0 20px;\n`;\n\nconst FullWidthImage = styled.img`\n width: 100%;\n`;\n\nexport default RaisePercentInfoModal;\n","import React, { useState, useEffect } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\nimport BlindStructureScroll from \"../../../shared/BlindStructureScroll\";\n\nconst BlindStructureModal = ({\n show,\n hideModal,\n initialBlindCents,\n blindIncreaseMinutes,\n}) => {\n const [startTransition, setStartTransition] = useState(true);\n\n useEffect(() => {\n // if showing again, reset\n if (show) {\n setStartTransition(false);\n }\n }, [show]);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(false);\n }, transitionTime);\n };\n\n return (\n \n \n \n );\n};\n\nexport default BlindStructureModal;\n","import React, { useState, useEffect } from \"react\";\nimport { mobileBreakpoint, mobileHeightBreakpoint } from \"../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst Notification = (props) => {\n const [show, setShow] = useState(false);\n\n const showNotification = () => {\n const notifShowDuration = props.notifDuration || 1700;\n setShow(true);\n setTimeout(() => {\n setShow(false);\n props.onNotifEnd();\n }, notifShowDuration);\n };\n\n useEffect(() => {\n if (props.show && !show) {\n showNotification();\n }\n }, [props.show]);\n\n return (\n \n {props.children}\n \n );\n};\n\nconst NotificationWrapper = styled.div`\n position: absolute;\n z-index: 4;\n background: white;\n padding: 10px;\n border: 2px solid ${({ theme }) => theme.foreground};\n border-radius: 5px;\n bottom: 100px;\n left: 50%;\n transform: translateX(-50%);\n transition: 0.3s ease all;\n pointer-events: none;\n\n ${(props) => {\n let s = \"\";\n if (!props.show) {\n s += `\n bottom: 0;\n opacity: 0;\n `;\n }\n\n if (props.clickable) {\n s += `\n white-space: nowrap;\n pointer-events: auto;\n `\n }\n\n return s\n }}}\n\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n bottom: 20px;\n }\n`;\n\nexport default Notification;\n","import React, { useState, useEffect } from \"react\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport Notification from \"../../../shared/Notification\";\n\nconst PatreonModal = (props) => {\n\n const [startTransition, setStartTransition] = useState(true);\n\n useEffect(() => {\n // if showing again, reset\n if (props.show) {\n setStartTransition(false);\n }\n }, [props.show]);\n\n\n return (\n \n \n Support us on{\" \"}\n \n Patreon\n \n !\n \n \n );\n};\n\nexport default PatreonModal;\n","import React, { useHistory, useContext } from \"react\";\nimport Modal from \"../../../shared/Modal\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\nimport { GameContext } from \"../../../context/GameContext\";\n\nconst MTTGameEndModal = () => {\n // const history = useHistory();\n const { ownStackCents, MTTId, updateDisplay } = useContext(GameContext);\n const roundEndText = (\n \n This table has ended!\n \n {ownStackCents == 0\n ? \"You have been eliminated, click to go back to the lobby.\"\n : \"You have qualified for the next round! click to go back to the lobby and wait for your next game.\"}\n \n );\n\n const goToLobby = () => {\n updateDisplay(\"mtt\");\n };\n\n return (\n \n {roundEndText}
\n \n );\n};\n\nconst Label = styled(SmallBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport default MTTGameEndModal;\n","import React, { useState, useEffect, useContext } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport Modal from \"../../../shared/Modal\";\nimport GameSettingsChoice from \"../GameSettingsModal/GameSettingsChoice\";\nimport BlindStructureScroll from \"../../../shared/BlindStructureScroll\";\nimport { MediumHeaderText, SmallBodyText } from \"../../../shared/Text\";\nimport {\n InputText,\n InputField,\n InputLabel,\n} from \"../../../shared/FormElements\";\nimport { isInvalidOrEmpty, formatCurrency } from \"../../../helpers/utils\";\nimport {\n removeNumberCommas,\n insertNumberCommas,\n} from \"../../../helpers/calculations\";\nimport { colors } from \"../../../helpers/styles\";\nimport { blinds, timer, buyin } from \"../../../helpers/constants\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\n\nconst MTTSettingsModal = (props) => {\n const { updateDisplay } = useContext(GameContext);\n const { isMobile } = useContext(BrowserContext);\n\n const [smallBlindCents, setSmallBlindCents] = useState(\n blinds.DEFAULT_SMALL_BLIND_CENTS\n );\n const [bigBlindCents, setBigBlindCents] = useState(\n blinds.DEFAULT_BIG_BLIND_CENTS\n );\n const [startingStack, setStartingStack] = useState(\n buyin.DEFAULT_AUTO_BUYIN_CENTS\n );\n const [blindIncreaseMinutes, setBlindIncreaseMinutes] = useState(\n blinds.DEFAULT_BLIND_INCREASE_MINUTES\n );\n const [timerLengthSeconds, setTimerLengthSeconds] = useState(\n timer.DEFAULT_TIMER_SECONDS\n );\n const [MTTPassword, setMTTPassword] = useState(\"\");\n const [isInCents, setIsInCents] = useState(true);\n const [startingStackFocus, setStartingStackFocus] = useState(false);\n\n useEffect(() => {\n if (!startingStackFocus) {\n // round betValueCents\n if (startingStack) {\n const startingStackInt = parseInt(startingStack);\n setStartingStack(startingStackInt);\n }\n }\n }, [startingStackFocus]);\n\n const getStartingStackValue = () => {\n const startingStackDollarsFloat = parseFloat(startingStack) / 100;\n if (\n isNaN(startingStackDollarsFloat) ||\n startingStackDollarsFloat === null\n ) {\n return \"\";\n }\n if (!isInCents) {\n return Math.round(startingStack, 0);\n }\n return startingStackFocus\n ? parseFloat(startingStack) / 100\n : startingStackDollarsFloat.toFixed(2);\n };\n\n const enterPressedForBet = (event) => {\n if (!isInCents && !/[0-9]/.test(event.key)) {\n event.preventDefault();\n }\n };\n\n const onSaveButtonClick = () => {\n if (props.title === \"Create MTT\") {\n props._createMTT(\n smallBlindCents,\n MTTPassword,\n timerLengthSeconds,\n startingStack,\n blindIncreaseMinutes * 60,\n isInCents\n );\n } else {\n // TODO, this is where you would make settings save during the game\n // this block represents the \"Game Settings\" modal as opposed to the \"Create New Game\" modal\n // for now just close it\n updateDisplay(\"mtt\");\n }\n return new Promise(() => true);\n };\n\n const onNumberInputChange = (e, cb) => {\n const val = removeNumberCommas(e.target.value);\n if (!isInvalidOrEmpty(val)) {\n cb(val);\n }\n };\n\n const password = (\n \n \n MTT Password (optional)\n \n setMTTPassword(e.target.value)}\n type=\"password\"\n placeholder=\"MTT password (optional)\"\n contrast\n />\n \n );\n\n return (\n \n \n \n \n Blinds\n \n \n Set your initial small/big blinds\n \n Blind structure shown below\n \n
\n setSmallBlindCents(val)}\n setBigBlindCents={(val) => setBigBlindCents(val)}\n isInCents={isInCents}\n setIsInCents={setIsInCents}\n />\n \n \n \n Timer (seconds) \n \n Set amount of time per turn\n \n \n onNumberInputChange(e, setTimerLengthSeconds)}\n placeholder={timer.DEFAULT_TIMER_SECONDS}\n modal\n />\n \n \n \n {isInCents && (\n Starting Stack (Dollars) \n )}\n {!isInCents && (\n Starting Stack (chips) \n )}\n \n Set stack players start with\n \n \n e.target.blur()}\n value={getStartingStackValue()}\n onChange={(e) =>\n setStartingStack(\n isInCents ? parseFloat(e.target.value) * 100 : e.target.value\n )\n }\n onKeyPress={enterPressedForBet}\n placeholder={formatCurrency(\n buyin.DEFAULT_AUTO_BUYIN_CENTS,\n isInCents\n )}\n onFocus={() => setStartingStackFocus(true)}\n onBlur={() => setStartingStackFocus(false)}\n modal\n />\n \n \n \n Increasing Blinds (minutes) \n \n Set increasing blinds time interval\n \n \n onNumberInputChange(e, setBlindIncreaseMinutes)}\n placeholder={buyin.DEFAULT_BLIND_INCREASE_MINUTES}\n modal\n />\n \n \n \n \n {props.disableModalStyles && password}\n \n );\n};\n\nconst SettingsWrapper = styled.div`\n margin: 5px 0 10px 0;\n width: 100%;\n padding: 0 40px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n ${(props) =>\n props.disabled &&\n `\n opacity: 0.2;\n pointer-events: none;\n `}\n`;\n\nconst SettingsText = styled(MediumHeaderText)`\n width: auto;\n display: inline-block;\n`;\n\nconst SettingsLabel = styled.div`\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n margin-right: 20px;\n width: 100%;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst SettingsSubText = styled(SmallBodyText)`\n width: 100%;\n`;\n\nconst BigBlindInputText = styled(InputText)`\n height: 50px;\n text-align: right;\n font-size: 22px;\n width: 30%;\n`;\n\nexport const PasswordElement = styled(InputField)`\n input {\n transition: border 0.3s ease;\n }\n ${(props) =>\n !props.disableCustomStyles &&\n css`\n display: flex;\n input {\n width: 90%;\n }\n `}\n\n ${(props) =>\n props.invalid &&\n css`\n input {\n border: 1px solid ${colors.red};\n }\n `}\n ${(props) =>\n !props.disablePadding &&\n `\n padding-right: 10px;\n `}\n`;\n\nconst AutoBuyInInputText = styled(BigBlindInputText)`\n &::-webkit-outer-spin-button,\n &::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n -moz-appearance: textfield;\n`;\n\nexport default MTTSettingsModal;\n","import React, { useHistory, useContext } from \"react\";\nimport Modal from \"../../../shared/Modal\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport styled from \"styled-components\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { emit } from \"../../../helpers/socket\";\n\nconst MTTDistributeModal = (props) => {\n // const history = useHistory();\n const {\n username,\n gameId,\n stack,\n MTTId,\n sessionKey,\n updateDisplay,\n totalBuyinCents,\n autoBuyinCents,\n } = useContext(GameContext);\n\n const roundEndText = (\n \n Redirecting you to a table in 5 seconds...\n \n {}\n \n );\n\n const joinGame = () => {\n emit(\"join\", {\n username: username,\n game_id: gameId,\n key: sessionKey,\n });\n\n updateDisplay(\"play\");\n };\n\n return (\n joinGame()}\n dataTestId=\"mtt-distribute-button\"\n >\n {roundEndText}
\n \n );\n};\n\nconst Label = styled(SmallBodyText)`\n color: ${({ theme }) => theme.text.label};\n`;\n\nexport default MTTDistributeModal;\n","import React, { useState, useEffect, useCallback } from \"react\";\nimport Modal, { transitionTime } from \"../../../shared/Modal\";\n// import TabView from \"../../../shared/TabView\";\n// import GameSettingsView from \"./GameSettingsView\";\n// import UserPreferencesView from \"./UserPreferencesView\";\nimport { emit } from \"../../../helpers/socket\";\n\nconst MTTBrowserSettingsModal = ({\n gameId,\n username,\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n setJoinCall,\n hideModal,\n show,\n isMobile,\n smallBlindCents,\n isTimerEnabled,\n timerLengthSeconds, // timer.DEFAULT_TIMER_SECONDS\n handTimerLengthSeconds, // timer.MEDIUM_HAND_TIMER_SECONDS\n isFourColorDeck,\n setIsFourColorDeck,\n isSpectating,\n isTournament,\n isAdmin,\n isInCents,\n adminPlayers,\n startTransition,\n setStartTransition,\n}) => {\n const [btnText, setBtnText] = useState(\"Save\");\n const [disableSave, setDisableSave] = useState(false);\n const [smallBlindSettings, _setSmallBlindSettings] = useState(\n smallBlindCents\n );\n const [isTimerEnabledSettings, _setIsTimerEnabledSettings] = useState(\n isTimerEnabled\n );\n const [timerLengthSecondsSettings, _setTimerLengthSecondsSettings] = useState(\n timerLengthSeconds\n );\n const [\n handTimerLengthSecondsSettings,\n _setHandTimerLengthSecondsSettings,\n ] = useState(handTimerLengthSeconds);\n\n const [useCentsSettings, _setUseCentsSettings] = useState(isInCents);\n\n const close = () => {\n setStartTransition(true);\n setTimeout(() => {\n hideModal(true);\n }, transitionTime);\n };\n\n const setSmallBlindSettings = useCallback(\n (newSmallBlindCentsSettings) => {\n if (newSmallBlindCentsSettings) {\n _setSmallBlindSettings(parseInt(newSmallBlindCentsSettings));\n } else {\n _setSmallBlindSettings(newSmallBlindCentsSettings);\n }\n setDisableSave(false);\n },\n [smallBlindCents]\n );\n\n const setIsTimerEnabledSettings = useCallback(\n (newIsTimerEnabledSettings) => {\n setDisableSave(false); // is there a reason the order of these two is switched above? or are state updates gauranteed to wait till fn call ends\n _setIsTimerEnabledSettings(newIsTimerEnabledSettings);\n },\n [isTimerEnabled]\n );\n\n const setTimerLengthSecondsSettings = useCallback(\n (newTimerLengthSecondsSettings) => {\n if (newTimerLengthSecondsSettings) {\n _setTimerLengthSecondsSettings(parseInt(newTimerLengthSecondsSettings));\n } else {\n _setTimerLengthSecondsSettings(newTimerLengthSecondsSettings);\n }\n setDisableSave(false);\n // setDisableSave logic is broken af -- it starts enabled, and then if 2 features are changed\n // then 1 is unchanged it stays disabled. maybe just set it to always be enabled\n },\n [timerLengthSeconds]\n );\n\n const setHandTimerLengthSecondsSettings = useCallback(\n (newHandTimerLengthSecondsSettings) => {\n _setHandTimerLengthSecondsSettings(newHandTimerLengthSecondsSettings);\n setDisableSave(false);\n },\n [handTimerLengthSeconds]\n );\n\n const setUseCentsSettings = useCallback(\n (newUseCentsSettings) => {\n _setUseCentsSettings(newUseCentsSettings);\n setDisableSave(false);\n },\n [isInCents]\n );\n\n useEffect(() => {\n setSmallBlindSettings(smallBlindCents);\n setIsTimerEnabledSettings(isTimerEnabled);\n setTimerLengthSecondsSettings(timerLengthSeconds);\n setHandTimerLengthSecondsSettings(handTimerLengthSeconds);\n setUseCentsSettings(isInCents);\n }, [\n setSmallBlindSettings,\n smallBlindCents,\n setIsTimerEnabledSettings,\n isTimerEnabled,\n setTimerLengthSecondsSettings,\n timerLengthSeconds,\n setHandTimerLengthSecondsSettings,\n handTimerLengthSeconds,\n setUseCentsSettings,\n isInCents,\n ]);\n const onSaveButtonClick = (e) => {\n emit(\"set_pending_settings_change\", {\n username: username,\n game_id: gameId,\n small_blind_amount: smallBlindSettings,\n is_timer_enabled: isTimerEnabledSettings,\n timer_length_seconds: timerLengthSecondsSettings,\n hand_timer_length_seconds: handTimerLengthSecondsSettings,\n is_in_cents: useCentsSettings,\n });\n close();\n return Promise.resolve(false);\n };\n\n let tabNamesToViews;\n\n // if (!isSpectating) {\n // tabNamesToViews = {\n // Settings: (\n // \n // ),\n // Preferences: (\n // \n // ),\n // };\n // } else {\n // tabNamesToViews = {\n // Preferences: (\n // \n // ),\n // };\n // }\n\n const handleTabChange = (newTab) => {\n if (newTab === \"Settings\") {\n setBtnText(\"Save\");\n } else {\n setBtnText(null);\n }\n };\n\n let defaultView = !isSpectating ? \"Settings\" : \"Preferences\";\n\n return (\n \n {/* */}\n \n );\n};\n\nexport default MTTBrowserSettingsModal;\n","export const optionsConfig = {\n interfaceConfigOverwrite: {\n SHOW_JITSI_WATERMARK: false,\n SHOW_WATERMARK_FOR_GUESTS: false,\n SHOW_CHROME_EXTENSION_BANNER: false,\n VERTICAL_FILMSTRIP: true,\n SETTINGS_SECTIONS: [\"devices\"],\n filmStripOnly: false,\n FILM_STRIP_MAX_HEIGHT: 80,\n DEFAULT_BACKGROUND: \"transparent\",\n JITSI_WATERMARK_LINK: \"https://lipoker.io\",\n MOBILE_APP_PROMO: false,\n DISABLE_FOCUS_INDICATOR: true,\n DISABLE_DOMINANT_SPEAKER_INDICATOR: true,\n DISPLAY_WELCOME_PAGE_CONTENT: false,\n TILE_VIEW_MAX_COLUMNS: 1,\n DEFAULT_REMOTE_DISPLAY_NAME: \"Poker Player\",\n DISABLE_VIDEO_BACKGROUND: true, // reduces CPU by 25%: https://community.jitsi.org/t/high-cpu-utilization-on-client-end/25764/25\n VIDEO_QUALITY_LABEL_DISABLED: true,\n CONNECTION_INDICATOR_DISABLED: true,\n HIDE_KICK_BUTTON_FOR_GUESTS: true,\n HIDE_INVITE_MORE_HEADER: true, // their \"Invite\" button doesn't fit screen/has bad UI\n },\n configOverwrite: {\n enableNoisyMicDetection: false,\n enableTalkWhileMuted: false,\n enableNoAudioDetection: false,\n enableWelcomePage: false,\n prejoinPageEnabled: false,\n resolution: 480,\n startWithVideoMuted: true,\n startWithAudioMuted: true,\n disableInviteFunctions: true,\n toolbarButtons: [\"microphone\", \"camera\", \"tileview\"],\n },\n};\n","import React, { useState, useEffect } from \"react\";\nimport styled from \"styled-components\";\nimport { colors } from \"../../helpers/styles\";\nimport { MediumHeaderText, SmallBodyText } from \"../../shared/Text\";\nimport { Icon, PrimaryButton } from \"../../shared/FormElements\";\nimport { faVideo } from \"@fortawesome/free-solid-svg-icons\";\nimport { optionsConfig } from \"./options\";\nimport { logError } from \"../../helpers/logger\";\nimport { textWithLoadingDots, MediumBodyText } from \"../../shared/Text\";\nimport { useStateWithLocalStorageBoolean } from \"../../hooks/useStateWithLocalStorage\";\n\nconst VideoChat = ({\n roomName,\n password,\n displayName,\n showVideoChat,\n setShowVideoChat,\n isMobile,\n isOnRight,\n joinCall,\n setJoinCall,\n iframeClientName,\n}) => {\n const [loading, setLoading] = useState(true);\n const [tileViewEnabled, setTileViewEnabled] = useStateWithLocalStorageBoolean(\n \"tileViewEnabled\",\n false\n );\n\n // Most of the magic happens here\n // for reference: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js\n // https://github.com/jitsi/jitsi-meet/blob/master/config.js\n // for overwrites see https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe\n const options = Object.assign(\n {\n roomName,\n parentNode: \"jitsi-container\",\n },\n optionsConfig\n );\n\n const handleJoinCallClick = (e) => {\n setJoinCall(true);\n setLoading(true);\n };\n\n useEffect(() => {\n let jitsi = null;\n\n if (isMobile) {\n if (jitsi) {\n jitsi.dispose();\n }\n setJoinCall(false);\n setShowVideoChat(false);\n } else if (iframeClientName !== \"game\") {\n if (jitsi) {\n jitsi.dispose();\n }\n setJoinCall(false);\n setShowVideoChat(false);\n } else if (joinCall) {\n try {\n if (window.JitsiMeetExternalAPI) {\n options.parentNode = document.getElementById(options.parentNode);\n // eslint-disable-next-line no-undef\n jitsi = new JitsiMeetExternalAPI(\"jitsi.member.fsf.org\", options);\n } else {\n jitsi = { error: true };\n }\n\n if (!jitsi.error) {\n jitsi.executeCommand(\"subject\", \" \");\n jitsi.addListener(\"videoConferenceJoined\", () => {\n if (password) jitsi.executeCommand(\"password\", password);\n setLoading(false);\n // Hack the tile view. Runs only once per user. If they clear their cookies, then it will have the opposite effect and disable their tile view.\n if (!tileViewEnabled) {\n console.error(\"Going to toggle the tile view\");\n jitsi.executeCommand(\"toggleTileView\");\n setTileViewEnabled(true);\n }\n jitsi.executeCommand(\"displayName\", displayName);\n });\n\n jitsi.addListener(\"passwordRequired\", () => {\n if (password) {\n jitsi.executeCommand(\"password\", password);\n }\n setLoading(false);\n });\n\n // this event closes container to avoid seeing jitsi advertisement\n jitsi.addListener(\"videoConferenceLeft\", () => {\n setShowVideoChat(false);\n setJoinCall(false);\n });\n }\n } catch (error) {\n logError(error);\n console.error(error);\n }\n } else if (jitsi) {\n jitsi.dispose();\n }\n\n return () => jitsi && jitsi.dispose();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n displayName,\n joinCall,\n options,\n password,\n isMobile,\n setJoinCall,\n setShowVideoChat,\n ]);\n\n const jitsiContainerWithLoading = (\n <>\n {loading && Loading }\n \n >\n );\n\n const joinCallDisplay = (\n \n \n \n \n \n join call\n \n setShowVideoChat(false)}\n >\n Dismiss\n \n \n Not stable on Safari or mobile yet.\n \n \n );\n\n const videoChatContainer = joinCall\n ? jitsiContainerWithLoading\n : joinCallDisplay;\n\n if (iframeClientName !== \"game\" || isMobile) {\n return <>>;\n }\n\n return (\n <>\n {!showVideoChat && (\n setShowVideoChat(true)}>\n \n {\"<<\"}\n \n \n \n )}\n {showVideoChat && (\n \n {videoChatContainer}\n \n )}\n >\n );\n};\n\nconst VideoChatWrapper = styled.div`\n width: 15%;\n border-bottom-left-radius: 5px;\n height: 100%;\n z-index: 3;\n position: absolute;\n\n right: 0;\n color: ${colors.darkGrey2};\n background: ${({ theme }) => theme.foreground};\n display: ${(props) => (props.visible ? \"block\" : \"none\")};\n ${(props) => !props.isOnRight && `left: 0;`}\n`;\n\nconst JoinCallWrapper = styled.div`\n padding: 10px;\n margin-top: 50px;\n`;\n\nconst JitsiContainer = styled.div`\n width: 100%;\n height: 100%;\n display: ${(props) => (props.loading ? \"none\" : \"block\")};\n`;\n\nconst VideochatLoadingWrapper = styled(MediumHeaderText)`\n margin-top: 50px;\n display: flex;\n justify-content: center;\n align-items: center;\n text-align: center;\n ${textWithLoadingDots}\n`;\n\nconst WidgetText = styled(MediumBodyText)`\n color: ${({ theme }) => theme.text.widget};\n font-size: 14px;\n font-weight: bold;\n align-items: center;\n text-align: center;\n display: flex;\n flex-direction: column;\n`;\n\nconst WidgetWrapper = styled.div`\n position: absolute;\n z-index: 3;\n cursor: pointer;\n transition: 0.3s ease padding;\n background: ${({ theme }) => theme.foreground};\n top: 40%;\n right: 0;\n border-bottom-left-radius: 7px;\n border-top-left-radius: 7px;\n padding: 5px 10px;\n overflow: hidden;\n &:hover {\n padding-right: 20px;\n opacity: 0.7;\n }\n`;\n\nexport default React.memo(VideoChat);\n","import React from \"react\";\nimport styled, { withTheme } from \"styled-components\";\nimport { colors } from \"../../../helpers/styles\";\nimport { formatCurrency } from \"../../../helpers/utils\";\nimport { MediumBodyText } from \"../../../shared/Text\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\n\nconst LeaderboardRow = (props) => {\n let color = props.balance >= 0 ? props.theme.profit : props.theme.loss;\n\n let totalBuyin = formatCurrency(props.buyin, props.isInCents);\n\n let winAmount = `${Math.sign(props.balance) >= 0 ? \"+\" : \"-\"}${formatCurrency(\n Math.abs(props.balance),\n props.isInCents\n )}`;\n return (\n \n \n \n {props.rank && (\n
\n {props.rank} \n \n )}\n
{props.name} \n
\n {totalBuyin} \n {winAmount} \n \n \n );\n};\n\nexport default withTheme(LeaderboardRow);\nexport const padding = \"10px\";\n\nconst RowElem = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n padding: 0 ${padding};\n background: ${({ theme }) => theme.modal.background};\n\n ${(props) =>\n props.height &&\n `\n ${Container} {\n height: ${props.height}px;\n border: none;\n }\n `};\n ${(props) =>\n props.highlight &&\n props.theme &&\n `\n ${Container} {\n background: ${props.theme.modal.divider};\n }\n `}\n ${(props) =>\n props.strong &&\n `\n ${PlayerName} {\n font-weight: bold;\n font-size: 1.2em;\n }\n `}\n ${(props) =>\n props.first &&\n `\n ${Container} {\n border-top-left-radius: 5px;\n border-top-right-radius: 5px;\n }\n `}\n ${(props) =>\n props.last &&\n props.theme &&\n `\n ${Container} {\n border-bottom-left-radius: 5px;\n border-bottom-right-radius: 5px;\n border-bottom: 2px solid ${props.theme.modal.divider};\n }\n `}\n`;\n\nconst Rank = styled.div`\n background: ${colors.grey};\n width: 25px;\n height: 25px;\n border-radius: 50%;\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n margin-left: 10px;\n font-weight: bold;\n`;\n\nconst PlayerName = styled(MediumBodyText)`\n display: flex;\n align-items: center;\n font-size: 1em;\n margin: 0;\n justify-self: flex-start;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 100px;\n margin-left: 10px;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst BuyIn = styled(MediumBodyText)`\n //text-align: center;\n color: grey;\n font-weight: bold;\n font-size: 1em;\n //margin: 0 10px 0 0px;\n`;\n\nconst Winnings = styled(MediumBodyText)`\n font-weight: bold;\n font-size: 1em;\n margin: 0 10px 0 0px;\n`;\n\nconst Container = styled(MediumBodyText)`\n width: 100%;\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-direction: row;\n border: 2px solid ${({ theme }) => theme.modal.divider};\n border-bottom: none;\n padding: 20px 0;\n\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: auto;\n padding: 5px;\n }\n`;\n","import React from \"react\";\nimport { SmallHeaderText } from \"../Text\";\nimport { colors, font } from \"../../helpers/styles\"; // ${font}\nimport { Icon } from \".\";\nimport { faCheckSquare, faSquare } from \"@fortawesome/free-solid-svg-icons\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst CheckboxField = ({ onClick, isOn, text, style, dataTestId }) => {\n return (\n \n \n theme.primary : colors.white}\n isLeft\n />\n \n {text} \n \n );\n};\n\nconst Checkbox = styled.div`\n background: white;\n width: 12px;\n height: 12px;\n display: flex;\n align-items: center;\n margin-right: 8px;\n`;\n\nconst GameMessageToggle = styled.div`\n background: white;\n border-radius: 5px;\n font-family: ${font.header};\n font-size: 16px;\n padding: 8px;\n background: none;\n letter-spacing: 1px;\n color: ${colors.white};\n cursor: pointer;\n display: flex;\n margin-right: 10px;\n flex-grow: 0;\n flex-shrink: 0;\n align-items: center;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 4px;\n }\n`;\n\nexport default CheckboxField;\n","import React, { Fragment, useState, useContext, useEffect } from \"react\";\nimport { OverlayTrigger } from \"react-bootstrap\";\nimport LeaderboardRow, { padding } from \"./LeaderboardRow\";\nimport {\n MediumHeaderText,\n SmallBodyText,\n SmallHeaderText,\n} from \"../../../shared/Text\";\nimport { MainOverlay, removeScrollbars } from \"../../../shared/Overlay\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport { faCrown } from \"@fortawesome/free-solid-svg-icons\";\nimport { colors, styles } from \"../../../helpers/styles\";\nimport styled, { css } from \"styled-components\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\nimport { MediumBodyText } from \"../../../shared/Text\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport { emit } from \"../../../helpers/socket\";\nimport { PrimaryButton } from \"../../../shared/FormElements\";\nimport { OverlayWrapper } from \"../../../shared/Overlay\";\nimport CheckboxField from \"../../../shared/FormElements/CheckboxField\";\n\nconst Leaderboard = ({ isAdmin }) => {\n const {\n gameId,\n sessionReset,\n sessionLeaderboard: sessionUsers,\n leaderboard: users,\n username,\n isInCents,\n } = useContext(GameContext);\n const { isMobile } = useContext(BrowserContext);\n const [showLeaderboard, setShowLeaderboard] = useState(false);\n const [showAllTimePlayers, setShowAllTimePlayers] = useState(false);\n const [leaderboardUsers, setLeaderboardUsers] = useState(sessionUsers);\n const [sessionResetClicked, setSessionResetClicked] = useState(sessionReset);\n const clientUser = users.filter((user) => user.name === username);\n\n useEffect(() => {\n if (showAllTimePlayers) {\n setLeaderboardUsers(users);\n } else {\n setLeaderboardUsers(sessionUsers);\n }\n setSessionResetClicked(sessionReset);\n }, [sessionReset, showAllTimePlayers, sessionUsers, users]);\n\n const onSessionResetClicked = () => {\n emit(\"update_session_reset\", {\n game_id: gameId,\n username: username,\n name: username,\n session_reset: !sessionResetClicked,\n });\n setSessionResetClicked(!sessionResetClicked);\n };\n\n return (\n \n setShowLeaderboard(true)}\n horizontalPadding={isMobile ? \"15px\" : \"40px\"}\n data-testid={\"leaderboard-widget\"}\n >\n \n \n LEADERBOARD\n \n \n \n \n setShowLeaderboard(false)}>\n \n \n \n {\" \"}\n Leaderboard{\" \"}\n \n {\n e.stopPropagation();\n setShowAllTimePlayers(!showAllTimePlayers);\n }}\n isOn={showAllTimePlayers}\n text=\"Show all time players\"\n style={{ padding: 0 }}\n dataTestId={\"show-all-time-players\"}\n />\n \n\n \n {\"Player\"} \n {\"Buy In\"} \n {\"Profit\"} \n \n\n {/** Temp disable this user header until we decide if we want */}\n {false && clientUser.length === 1 && (\n \n \n \n \n \n )}\n \n {!!leaderboardUsers &&\n leaderboardUsers.map((user, i) => (\n \n ))}\n \n {isAdmin && (\n \n \n \n
{\n e.stopPropagation();\n onSessionResetClicked();\n }}\n dataTestId={\"session-reset-button\"}\n >\n {sessionResetClicked ? \"Undo Reset\" : \"Reset Session\"}\n \n
\n \n \n )}\n \n \n \n \n \n \n );\n};\nconst Wrapper = styled.div`\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: scroll; // overriden by removeScrollbars\n top: 0;\n left: 0;\n z-index: 8;\n ${(props) => props.hide && `pointer-events: none;`}\n ${removeScrollbars}\n`;\nconst LeaderboardBody = styled.div`\n width: 100%;\n height: auto;\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 5;\n transition: 0.7s cubic-bezier(0.55, -0.09, 0.43, 1.36) all;\n`;\nconst LeaderboardBodyInner = styled.div`\n display: table;\n width: 30vw;\n min-width: 250px;\n box-shadow: ${styles.largeBoxShadow};\n border-radius: 5px;\n overflow: hidden;\n background: ${({ theme }) => theme.modal.background};\n margin: 40px 0;\n`;\n\nconst LeaderboardTab = styled.div`\n position: absolute;\n pointer-events: all;\n cursor: pointer;\n top: 0;\n left: 50%;\n transform: translateX(-50%);\n background: ${({ theme }) => theme.foreground};\n border-radius: 5px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n padding: 10px\n ${(props) => (props.horizontalPadding ? props.horizontalPadding : \"40px\")};\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 10px\n ${(props) => (props.horizontalPadding ? props.horizontalPadding : \"15px\")};\n }\n z-index: 3;\n transition: padding 0.4s ease, top 1s ease-out, transform 1s ease-out;\n color: ${({ theme }) => theme.text.widget};\n &:hover {\n padding-top: 20px;\n opacity: 0.7;\n }\n ${(props) =>\n !props.hide &&\n `\n top: -100%;\n `}\n`;\n\nconst LeaderboardWrapper = styled.div`\n ${(props) =>\n props.hide &&\n css`\n ${LeaderboardBody} {\n top: -50px;\n transform: translate(0, -100%);\n pointer-events: none;\n }\n `}\n`;\n\nexport const LeaderboardHeaderText = styled(MediumHeaderText)`\n text-align: center;\n display: block;\n color: ${colors.white};\n font-weight: bold;\n`;\n\nexport const LeaderboardHeader = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n flex-wrap: wrap;\n\n width: 100%;\n min-width: 100px;\n background: ${({ theme }) => theme.tertiary};\n padding: 15px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 8px;\n }\n`;\n\nconst PlayerTitle = styled(MediumBodyText)`\n font-weight: bold;\n font-size: 1em;\n justify-self: flex-start;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 100px;\n margin-left: 50px;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst BuyInTitle = styled(MediumBodyText)`\n font-weight: bold;\n font-size: 1em;\n margin-left: 5px;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst WinningsTitle = styled(MediumBodyText)`\n font-weight: bold;\n font-size: 1em;\n margin-right: 25px;\n color: ${({ theme }) => theme.text.label};\n`;\n\nconst TitlesWrapper = styled(MediumBodyText)`\n width: 100%;\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-direction: row;\n margin-top: 10px;\n background: ${({ theme }) => theme.modal.background};\n\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 15px;\n margin-top: 8px;\n }\n`;\n\nconst MainRows = styled.div`\n padding: ${padding} 0;\n`;\n\nconst UserRow = styled.div`\n background: ${({ theme }) => theme.background};\n padding: 10px;\n width: 100%;\n`;\n\nconst UserRowInner = styled.div`\n background: ${({ theme }) => theme.background};\n border-radius: 5px;\n overflow: hidden;\n`;\n\nconst SessionResetWrapper = styled.div`\n display: flex;\n width: 100%;\n justify-content: center;\n margin-bottom: 15px;\n`;\n\nconst SessionResetOverlay = (props) => {\n return (\n \n \n Resets all player profits next hand. Full stats will still be available\n in the all time leaderboard.\n \n \n );\n};\n\nexport default Leaderboard;\n","import React, { useState } from \"react\";\nimport { Icon } from \"./FormElements\";\nimport { MainOverlay } from \"./Overlay\";\nimport { faPlus } from \"@fortawesome/free-solid-svg-icons\";\nimport { colors, font } from \"../helpers/styles\";\nimport styled from \"styled-components\";\n\nconst DropDownOptionsPicker = ({\n show,\n initialExpanded,\n optionsList,\n style,\n includeOverlay,\n dataTestId,\n onCloseOverride,\n toggleInner,\n}) => {\n const [expanded, _setExpanded] = useState(initialExpanded || false);\n\n const setExpanded = (expanded) => {\n if (onCloseOverride && !expanded) {\n onCloseOverride();\n } else {\n _setExpanded(expanded);\n }\n };\n\n return (\n \n \n setExpanded(!expanded)}\n toggled={expanded}\n >\n {toggleInner ? (\n toggleInner\n ) : (\n \n \n \n )}\n \n \n {expanded && (\n \n {optionsList.map((action, i) => (\n \n \n {action.inner}\n \n {action.showLabel && {action.name} }\n \n ))}\n \n )}\n {includeOverlay && (\n setExpanded(false)}\n />\n )}\n \n );\n};\n\nconst Wrapper = styled.div`\n ${(props) => props.hide && `pointer-events: none;`}\n`;\n\nconst DropDownList = styled.div`\n margin-top: 10px;\n`;\n\nconst OptionCircle = styled.div`\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: ${({ theme }) => theme.secondary};\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: ${font.body};\n font-weight: bold;\n color: ${colors.white};\n cursor: pointer;\n transition: transform 400ms cubic-bezier(0.26, 0.76, 0.4, 1.12)\n ${(props) => props.delay || 0}ms,\n opacity 200ms ease 0ms;\n span {\n transition: 0.2s ease transform;\n }\n &:hover {\n background: ${({ theme }) => theme.primary};\n }\n ${(props) =>\n props.toggled &&\n `\n background: ${colors.darkGrey2};\n &:hover {\n background: ${colors.darkGrey2};\n }\n span.rotate {\n transform: rotate(45deg);\n }\n `}\n ${(props) =>\n props.selected &&\n props.theme &&\n `\n background: ${props.theme.primary};\n `}\n ${(props) =>\n props.hide &&\n `\n transform: translateX(-100px);\n `}\n ${(props) =>\n props.disabled &&\n `\n pointer-events: none;\n cursor: not-allowed;\n `}\n`;\n\nconst OptionLabel = styled.div`\n display: none;\n margin-left: 10px;\n pointer-events: none;\n background: ${({ theme }) => theme.foreground};\n color: ${({ theme }) => theme.text.label};\n font-weight: bold;\n font-family: ${font.body};\n font-size: 12px;\n border-radius: 5px;\n height: fit-content;\n min-width: max-content;\n padding: 2px 5px;\n`;\n\nconst OptionsCircleWrapper = styled.div`\n display: flex;\n position: relative;\n align-items: center;\n margin-bottom: 10px;\n &:hover {\n cursor: pointer;\n ${OptionLabel} {\n display: block;\n }\n }\n ${(props) =>\n props.isLoading &&\n `\n pointer-events: none;\n opacity: 0.5;\n `}\n`;\n\nexport default DropDownOptionsPicker;\n","import styled from \"styled-components\";\n\nexport const ThemeCircle = styled.div`\n width: 25px;\n height: 25px;\n border-radius: 50%;\n ${(props) =>\n props.theme &&\n `\n background-image: linear-gradient(to bottom right, ${props.theme.primary}, ${props.theme.background}, ${props.theme.tertiary});\n `}\n ${(props) =>\n props.color1 &&\n props.color2 &&\n props.color3 &&\n `\n background-image: linear-gradient(to bottom right, ${props.color1}, ${props.color2}, ${props.color3});\n `}\n`;\n\nexport const dropdownPositioning = {\n position: \"absolute\",\n zIndex: 3,\n top: \"70px\",\n left: \"20px\",\n};\nexport const mobileDropdownPositioning = {\n position: \"absolute\",\n zIndex: 3,\n top: \"60px\",\n left: \"10px\",\n};\n","import React, { useState, useEffect, useContext } from \"react\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport DropdownOptionsPicker from \"../../../shared/DropdownOptionsPicker\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport {\n faSignInAlt,\n faSignOutAlt,\n faUndo,\n faCommentAlt,\n faSpinner,\n faEdit,\n faGift,\n faPause,\n faPlay,\n faCoins,\n faDollarSign,\n} from \"@fortawesome/free-solid-svg-icons\";\nimport { colors } from \"../../../helpers/styles\";\nimport { modals, tableActions, roles } from \"../../../helpers/constants\";\nimport {\n ThemeCircle,\n dropdownPositioning,\n mobileDropdownPositioning,\n} from \"./shared\";\n\nconst PlayerActions = ({\n show,\n queuedAction,\n emitSocketMessage,\n isStanding,\n setCurrentModal,\n isMobile,\n setShowThemePicker,\n setShowSocialMedia,\n isInCents,\n}) => {\n const { activePlayers, username, paused, ongoing } = useContext(GameContext);\n\n const clientPlayer = activePlayers.filter(\n (player) => player.playerName === username\n )[0];\n const numWinners = activePlayers.filter((player) => player.isWinner).length;\n const hasTurn =\n activePlayers.filter((player) => player.playerIsTurn).length > 0;\n\n const [isLoadingStand, setIsLoadingStand] = useState(false);\n\n useEffect(() => {\n setIsLoadingStand(false);\n }, [queuedAction]);\n\n const toggleAction = () => {\n const actionToDo = isStanding\n ? tableActions.ACTION_SIT\n : tableActions.ACTION_STAND;\n emitSocketMessage(\"queue_action\", {\n action:\n queuedAction !== tableActions.ACTION_NONE\n ? tableActions.ACTION_NONE\n : actionToDo,\n });\n setIsLoadingStand(true);\n };\n\n const togglePause = () => {\n emitSocketMessage(\"pause_game\", {});\n };\n\n let buyIn;\n if (\n emitSocketMessage && // player, not spectator\n clientPlayer.requestedBuyIn > 0 &&\n clientPlayer.playerRole !== roles.ROLE_ADMIN\n ) {\n buyIn = {\n name: \"Update buy in request to admin\",\n inner: ,\n onClick: () => setCurrentModal(modals.BUY_IN),\n selected: true,\n showLabel: true,\n };\n } else if (emitSocketMessage && clientPlayer.pendingBuyIn > 0) {\n buyIn = {\n name: \"Change pending buy in\",\n inner: ,\n onClick: () => setCurrentModal(modals.BUY_IN),\n showLabel: true,\n };\n } else {\n buyIn = {\n name: \"Buy in for more\",\n inner: (\n <>\n <>+ >\n {isInCents && }\n {!isInCents && }\n >\n ),\n onClick: () => setCurrentModal(modals.BUY_IN),\n showLabel: true,\n };\n }\n\n let pauseNextHand;\n if (emitSocketMessage && clientPlayer.playerRole === roles.ROLE_ADMIN) {\n if ((ongoing && numWinners > 0) || !ongoing || !hasTurn) {\n pauseNextHand = {\n name: \"Cannot pause\",\n inner: paused ? (\n \n ) : (\n \n ),\n showLabel: true,\n disabled: true,\n };\n } else {\n pauseNextHand = {\n name: paused ? \"Unpause game\" : \"Pause game after this hand\",\n inner: paused ? (\n \n ) : (\n \n ),\n onClick: togglePause,\n showLabel: true,\n };\n }\n }\n\n let sitOut;\n if (queuedAction !== tableActions.ACTION_NONE) {\n sitOut = {\n name: `Undo ${isStanding ? \"join game\" : \"sit out\"}`,\n inner: ,\n onClick: toggleAction,\n selected: true,\n isLoading: isLoadingStand,\n dataTestId: \"cancel-sit-out-action\",\n showLabel: true,\n };\n } else {\n sitOut = {\n name: `${isStanding ? \"Join game\" : \"Sit out\"} next hand`,\n inner: (\n \n ),\n onClick: toggleAction,\n selected: false,\n isLoading: isLoadingStand,\n dataTestId: `${isStanding ? \"join-game\" : \"sit-out\"}`,\n showLabel: true,\n };\n }\n\n const feedback = {\n name: \"Share your feedback\",\n inner: (\n <>\n \n >\n ),\n onClick: () => setCurrentModal(modals.FEEDBACK),\n showLabel: true,\n };\n const changeTheme = {\n name: \"Change theme\",\n inner: ,\n onClick: () => setShowThemePicker(true),\n showLabel: true,\n };\n\n const socialMedia = {\n name: \"Check out what we're up to! 👀\",\n inner: (\n <>\n \n >\n ),\n onClick: () => setShowSocialMedia(true),\n showLabel: true,\n };\n\n const actionsList = emitSocketMessage\n ? pauseNextHand\n ? [buyIn, sitOut, pauseNextHand, feedback, changeTheme, socialMedia]\n : [buyIn, sitOut, feedback, changeTheme, socialMedia]\n : [feedback, changeTheme, socialMedia];\n\n return (\n \n );\n};\n\nexport default PlayerActions;\n","import { colors } from \"../styles\";\n\nexport function generateDarkTheme(primary, secondary, tertiary, background) {\n return {\n // general\n primary: primary,\n secondary: colors.mediumGrey,\n secondaryLighter: colors.darkBlueLighter,\n tertiary: tertiary,\n background: background,\n foreground: \"#525252\",\n disabled: \"#8a8a8a\",\n profit: colors.green,\n loss: tertiary,\n // components\n card: {\n heart: tertiary,\n diamond: primary,\n spade: colors.black,\n club: colors.green,\n dark: colors.black,\n light: tertiary,\n },\n modal: {\n background: background,\n subtitle: colors.grey,\n overlay: colors.black,\n divider: \"#525252\",\n cta: secondary,\n },\n chat: {\n background: \"#525252\",\n divider: background,\n gameText: colors.darkGrey2,\n linkText: colors.lightGrey,\n },\n table: {\n outer: \"#525252\",\n inner: secondary,\n },\n openSeat: {\n background: secondary,\n borderColor: \"#525252\",\n },\n player: {\n disabledBackground: background,\n borderColor: \"#525252\",\n borderHighlightedColor: colors.lightGrey,\n pot: background,\n },\n keyboardChar: {\n background: background,\n text: colors.white,\n },\n input: {\n modal: secondary,\n contrast: background,\n noBack: \"#525252\",\n },\n switch: {\n background: secondary,\n },\n flatShadow: \"#585858\",\n // text\n text: {\n mainBody: colors.lightGrey,\n widget: colors.white,\n label: colors.white,\n placeholder: \"#525252\",\n },\n };\n}\n\nconst darkTheme = generateDarkTheme(\n colors.blue,\n colors.darkBlue,\n colors.red,\n colors.darkGrey\n);\n\nexport default darkTheme;\n","import { colors } from \"../styles\";\nimport { themes } from \"../constants\";\nimport { generateLightTheme } from \"./light\";\nimport { generateDarkTheme } from \"./dark\";\n\nconst themeColors = {\n [themes.THEME_DARK_DEFAULT]: {\n isDark: true,\n colors: {\n blue: colors.blue,\n darkBlue: colors.darkBlue,\n red: colors.red,\n darkGrey: colors.darkGrey,\n },\n },\n [themes.THEME_SALOON]: {\n isDark: true,\n colors: {\n lightGreen: \"#654321\",\n green: \"#006400\",\n gold: \"#C3976A\",\n brown: \"#2a1b0e\",\n },\n },\n [themes.THEME_LIGHT_DEFAULT]: {\n isDark: false,\n colors: {\n blue: colors.blue,\n darkBlue: colors.darkBlue,\n red: colors.red,\n lightGrey: colors.lightGrey,\n },\n },\n [themes.THEME_DARK_PURPLE]: {\n isDark: true,\n colors: {\n red: \"#ff6363\",\n purple: \"#543864\",\n yellow: \"#ffa531\",\n blueBlack: \"#202040\",\n },\n },\n [themes.THEME_DARK_GREEN]: {\n isDark: true,\n colors: {\n green: \"#3c6562\",\n darkGreen: \"#00454a\",\n lightRed: \"#ed6363\",\n darkBlue: \"#003545\",\n },\n },\n [themes.THEME_DARK_BLUE_PINK]: {\n isDark: true,\n colors: {\n blue: \"#4d5ade\",\n purple: \"#4d2b80\",\n pink: \"#e14594\",\n darkBlue: \"#182952\",\n },\n },\n};\n\nconst generateSwatch = (themeName) => {\n if (!(themeName in themeColors)) {\n return [];\n }\n return Object.keys(themeColors[themeName][\"colors\"]).map(\n (key) => themeColors[themeName][\"colors\"][key]\n );\n};\n\nconst themesDict = {};\nObject.keys(themeColors).forEach((themeName) => {\n const swatch = generateSwatch(themeName);\n const generatedTheme = themeColors[themeName].isDark\n ? generateDarkTheme(...swatch)\n : generateLightTheme(...swatch);\n themesDict[themeName] = {\n theme: generatedTheme,\n swatch: swatch,\n };\n});\n\nconst defaultTheme = themesDict[themes.THEME_DARK_DEFAULT].theme;\nexport { defaultTheme };\nexport default themesDict;\n","import { colors } from \"../styles\";\n\nexport function generateLightTheme(primary, secondary, tertiary, background) {\n return {\n // general\n primary: primary,\n secondary: secondary,\n secondaryLighter: colors.darkBlueLighter,\n tertiary: tertiary,\n background: background,\n foreground: colors.lightGrey2,\n disabled: colors.darkGrey2,\n profit: colors.green,\n loss: tertiary,\n // components\n card: {\n heart: tertiary,\n diamond: primary,\n spade: colors.black,\n club: colors.green,\n dark: colors.black,\n light: tertiary,\n },\n modal: {\n background: colors.white,\n subtitle: colors.grey,\n overlay: colors.white,\n divider: background,\n cta: background,\n },\n chat: {\n background: colors.white,\n divider: colors.lightGrey2,\n gameText: primary,\n linkText: secondary,\n },\n table: {\n outer: colors.grey,\n inner: colors.darkGrey,\n },\n openSeat: {\n background: colors.lightGrey2,\n borderColor: colors.white,\n },\n player: {\n disabledBackground: colors.grey,\n borderColor: background,\n borderHighlightedColor: background,\n pot: secondary,\n },\n keyboardChar: {\n background: colors.white,\n text: null,\n },\n input: {\n modal: background,\n contrast: colors.white,\n noBack: colors.white,\n },\n switch: {\n background: colors.lightGrey2,\n },\n flatShadow: colors.grey,\n // text\n text: {\n mainBody: colors.darkGrey,\n widget: colors.darkGrey2,\n label: colors.black,\n placeholder: colors.grey,\n },\n };\n}\n","import React from \"react\";\nimport DropdownOptionsPicker from \"../../../shared/DropdownOptionsPicker\";\nimport themesDict from \"../../../helpers/theme\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport { faArrowLeft } from \"@fortawesome/free-solid-svg-icons\";\nimport {\n ThemeCircle,\n dropdownPositioning,\n mobileDropdownPositioning,\n} from \"./shared\";\n\nconst ThemePicker = ({ show, isMobile, theme, setTheme, close }) => {\n const generateThemesList = () => {\n return Object.keys(themesDict).map((themeName) => {\n return {\n name: themeName,\n inner: (\n \n ),\n onClick: () => setTheme(themeName),\n showLabel: false,\n selected: theme === themeName,\n };\n });\n };\n\n const themesList = generateThemesList();\n\n return (\n }\n />\n );\n};\n\nexport default ThemePicker;\n","import React from \"react\";\nimport DropdownOptionsPicker from \"../../../shared/DropdownOptionsPicker\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport { faArrowLeft, faPencilAlt } from \"@fortawesome/free-solid-svg-icons\";\nimport { faTwitter, faPatreon } from \"@fortawesome/free-brands-svg-icons\";\nimport { dropdownPositioning, mobileDropdownPositioning } from \"./shared\";\n\nconst socialMedia = [\n {\n name: \"twitter\",\n link: \"https://twitter.com/lipoker_io\",\n icon: faTwitter,\n color: \"#1E9DEA\",\n },\n {\n name: \"patreon\",\n link: \"https://www.patreon.com/lipoker\",\n icon: faPatreon,\n color: \"#EF6551\",\n },\n {\n name: \"blog\",\n link: \"https://www.notion.so/Lipoker-Blog-5ba5c59a84234bd1a4343f5880780ea0\",\n icon: faPencilAlt,\n color: \"#FFFFFF\",\n },\n];\n\nconst SocialMedia = ({ show, isMobile, close }) => {\n const generateSocialMediaList = () => {\n return socialMedia.map((obj) => {\n return {\n name: obj.name,\n inner: (\n \n \n \n ),\n showLabel: false,\n };\n });\n };\n\n const socialMediaList = generateSocialMediaList();\n\n return (\n }\n />\n );\n};\n\nexport default SocialMedia;\n","import React, { useState } from \"react\";\nimport PlayerActions from \"./PlayerActions\";\nimport ThemePicker from \"./themePicker\";\nimport SocialMedia from \"./SocialMedia\";\n\nconst ActionsDropdown = ({\n queuedAction,\n emitSocketMessage,\n isStanding,\n setCurrentModal,\n isMobile,\n theme,\n setTheme,\n isInCents,\n}) => {\n const [showThemePicker, setShowThemePicker] = useState(false);\n\n const [showSocialMedia, setShowSocialMedia] = useState(false);\n\n return (\n <>\n \n setShowThemePicker(false)}\n />\n setShowSocialMedia(false)}\n />\n >\n );\n};\n\nexport default ActionsDropdown;\n","import React, { Fragment } from \"react\";\nimport Notification from \"../shared/Notification\";\nimport { MediumBodyText } from \"../shared/Text\";\nimport { mobileBreakpoint, mobileHeightBreakpoint } from \"../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst Widget = ({\n onClick,\n left,\n style,\n text,\n showNotif,\n setShowNotif,\n notification,\n dataTestId,\n}) => {\n return (\n \n \n {text} \n \n setShowNotif(false)}>\n {notification}\n \n \n );\n};\n\nconst WidgetText = styled(MediumBodyText)`\n color: ${({ theme }) => theme.text.widget};\n font-size: 14px;\n font-weight: bold;\n text-align: center;\n`;\n\nconst WidgetWrapper = styled.div`\n display: flex;\n z-index: 3;\n margin-left: ${(props) => (props.left ? props.left : \"0px\")};\n cursor: pointer;\n transition: 0.3s ease padding;\n border-bottom-left-radius: 7px;\n border-bottom-right-radius: 7px;\n background: ${({ theme }) => theme.foreground};\n padding: 12px 20px;\n height: 100%;\n &:hover {\n padding-top: 20px;\n opacity: 0.7;\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n padding: 5px 10px;\n }\n`;\n\nexport default Widget;\n","import React, { Fragment, useState } from \"react\";\nimport Widget from \"../../shared/Widget\";\nimport { BlindStructureModal } from \"../Modals\";\n\nconst BlindStructureWidget = ({\n isMobile,\n setAllowKeyboardShortcuts,\n setExistsModal,\n initialBlindCents,\n blindIncreaseMinutes,\n}) => {\n const [showBlindStructureModal, _setShowBlindStructureModal] = useState(\n false\n );\n\n const setShowBlindStructureModal = (show) => {\n _setShowBlindStructureModal(show);\n setAllowKeyboardShortcuts(!show);\n setExistsModal(show);\n };\n\n return (\n \n {\n setShowBlindStructureModal(true);\n }}\n text={\"Blinds\"}\n showNotif={false}\n left={isMobile ? \"2px\" : \"6px\"}\n //right={isMobile ? \"85px\" : \"135px\"}\n />\n {\n setShowBlindStructureModal(false);\n }}\n initialBlindCents={initialBlindCents}\n blindIncreaseMinutes={blindIncreaseMinutes}\n />\n \n );\n};\n\nexport default BlindStructureWidget;\n","import React, { Fragment, useState } from \"react\";\nimport { FAQModal } from \"../../components/Modals\";\nimport { Icon } from \"../FormElements\";\nimport { faQuestionCircle } from \"@fortawesome/free-solid-svg-icons\";\nimport Widget from \"../Widget\";\n\nconst FAQWidget = ({\n isMobile,\n setAllowKeyboardShortcuts,\n setExistsModal,\n isFourColorDeck,\n}) => {\n const [showFAQModal, _setShowFAQModal] = useState(false);\n\n const setShowFAQModal = (show) => {\n _setShowFAQModal(show);\n setAllowKeyboardShortcuts(!show);\n setExistsModal(show);\n };\n\n return (\n \n setShowFAQModal(true)}\n text={ }\n showNotif={false}\n left={isMobile ? \"2px\" : \"6px\"}\n />\n {\n setShowFAQModal(false);\n }}\n isFourColorDeck={isFourColorDeck}\n />\n \n );\n};\n\nexport default FAQWidget;\n","import React, { Fragment, useState } from \"react\";\nimport { HandRankingsModal } from \"../../components/Modals\";\nimport { logClickHandRankingEvent } from \"../../helpers/logger\";\nimport Widget from \"../Widget\";\n\nconst HandRankingsWidget = ({\n isMobile,\n setAllowKeyboardShortcuts,\n setExistsModal,\n isFourColorDeck,\n playerHand,\n}) => {\n const [showModal, _setShowModal] = useState(false);\n\n const setShowModal = (show) => {\n _setShowModal(show);\n setAllowKeyboardShortcuts(!show);\n setExistsModal(show);\n };\n\n return (\n \n {\n setShowModal(true);\n logClickHandRankingEvent();\n }}\n text={isMobile ? \"Hands\" : \"Hand Rankings\"}\n showNotif={false}\n left={isMobile ? \"2px\" : \"6px\"}\n //right={isMobile ? \"85px\" : \"135px\"}\n />\n {\n setShowModal(false);\n }}\n isFourColorDeck={isFourColorDeck}\n playerHand={playerHand}\n />\n \n );\n};\n\nexport default HandRankingsWidget;\n","import React, { Fragment, useState } from \"react\";\nimport { BrowserSettingsModal } from \"../Modals\";\nimport { Icon } from \"../../shared/FormElements\";\nimport { faCog } from \"@fortawesome/free-solid-svg-icons\";\nimport Widget from \"../../shared/Widget\";\n\nconst SettingsWidget = ({\n gameId,\n username,\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n joinCall,\n setJoinCall,\n setAllowKeyboardShortcuts,\n setExistsModal,\n isMobile,\n smallBlindCents,\n isTimerEnabled,\n timerLengthSeconds, // timer.DEFAULT_TIMER_SECONDS\n handTimerLengthSeconds, // timer.MEDIUM_HAND_TIMER_SECONDS\n isFourColorDeck,\n setIsFourColorDeck,\n isSpectating,\n isTournament,\n isAdmin,\n isInCents,\n adminPlayers,\n}) => {\n const [showSettingsModal, _setShowSettingsModal] = useState(false);\n const [startTransition, setStartTransition] = useState(true);\n\n const setShowSettingsModal = (show) => {\n console.log(\"settingsWidget\", show);\n setStartTransition(!show);\n _setShowSettingsModal(show);\n setAllowKeyboardShortcuts(!show);\n setExistsModal(show);\n };\n\n return (\n \n setShowSettingsModal(true)}\n text={ }\n showNotif={false}\n left={isMobile ? \"2px\" : \"6px\"}\n dataTestId={\"settings-widget\"}\n />\n {\n setShowSettingsModal(false);\n }}\n sound={sound}\n setSound={setSound}\n showVideoChat={showVideoChat}\n setShowVideoChat={setShowVideoChat}\n videoIsOnRight={videoIsOnRight}\n setVideoIsOnRight={setVideoIsOnRight}\n betButtonsOnRight={betButtonsOnRight}\n setBetButtonsOnRight={setBetButtonsOnRight}\n joinCall={joinCall}\n setJoinCall={setJoinCall}\n smallBlindCents={smallBlindCents}\n isTimerEnabled={isTimerEnabled}\n timerLengthSeconds={timerLengthSeconds} // timer.DEFAULT_TIMER_SECONDS\n handTimerLengthSeconds={handTimerLengthSeconds} // timer.MEDIUM_HAND_TIMER_SECONDS\n isFourColorDeck={isFourColorDeck}\n setIsFourColorDeck={setIsFourColorDeck}\n isSpectating={isSpectating}\n isTournament={isTournament}\n isAdmin={isAdmin}\n isInCents={isInCents}\n adminPlayers={adminPlayers}\n startTransition={startTransition}\n setStartTransition={setStartTransition}\n />\n \n );\n};\n\nexport default SettingsWidget;\n","import React, { Fragment } from \"react\";\nimport { Icon } from \"../../shared/FormElements\";\nimport { faEye, faEyeSlash } from \"@fortawesome/free-solid-svg-icons\";\nimport Widget from \"../../shared/Widget\";\n\nconst ShowCardsToGodButton = ({\n emitSocketMessage,\n isGodModeEnabled,\n isGod,\n showCardsToGod,\n god,\n isSpectating,\n}) => {\n const showCardsToGodRequest = () => {\n emitSocketMessage(\"show_cards_to_god\", { show_cards: !showCardsToGod });\n };\n\n if (!isGodModeEnabled || isGod || isSpectating) {\n return <>>;\n }\n\n return (\n \n showCardsToGodRequest()}\n text={\n showCardsToGod ? (\n <>\n {\"Hide Cards from \" + god} \n >\n ) : (\n <>\n {\"Show Cards to \" + god} \n >\n )\n }\n style={ShowCardsGodButtonPosition}\n />\n \n );\n};\n\nconst ShowCardsGodButtonPosition = {\n position: \"absolute\",\n zIndex: 3,\n top: \"70px\",\n right: \"20px\",\n height: \"40px\",\n borderTopLeftRadius: \"7px\",\n borderTopRightRadius: \"7px\",\n paddingTop: \"10px\",\n};\n\nexport default ShowCardsToGodButton;\n","import React, { Fragment, useContext } from \"react\";\nimport { GameContext } from \"../../context/GameContext\";\nimport { Icon } from \"../../shared/FormElements\";\nimport { faRandom } from \"@fortawesome/free-solid-svg-icons\";\nimport Widget from \"../../shared/Widget\";\nimport { colors } from \"../../helpers/styles\";\nimport { roles } from \"../../helpers/constants\";\n\nconst RandomSeatsButton = ({ emitSocketMessage, isSpectating }) => {\n const { activePlayers, username, ongoing } = useContext(GameContext);\n const adminPlayer = activePlayers.filter(\n (player) => player.playerRole === roles.ROLE_ADMIN\n )[0];\n\n const randomizeSeats = () => {\n emitSocketMessage(\"randomize_seats\", {});\n };\n if (\n ongoing ||\n activePlayers.length < 2 ||\n (adminPlayer && adminPlayer.playerName !== username) ||\n isSpectating\n ) {\n return <>>;\n }\n\n return (\n \n randomizeSeats()}\n text={\n <>\n {\"Shuffle Seats\"} \n >\n }\n style={RandomSeatButtonPosition}\n />\n \n );\n};\n\nconst RandomSeatButtonPosition = {\n position: \"absolute\",\n zIndex: 3,\n top: \"120px\",\n right: \"20px\",\n height: \"40px\",\n borderTopLeftRadius: \"7px\",\n borderTopRightRadius: \"7px\",\n backgroundColor: colors.grey,\n paddingTop: \"10px\",\n};\n\nexport default RandomSeatsButton;\n","import React, { Fragment, useContext } from \"react\";\nimport { BrowserContext } from \"../../context/BrowserContext\";\nimport Leaderboard from \"./Leaderboard\";\nimport PlayerActionsDropdown from \"./PlayerActionsDropdown\";\nimport BlindStructureWidget from \"./BlindStructureWidget\";\nimport FAQWidget from \"../../shared/Widgets/FAQWidget\";\nimport HandRankingsWidget from \"../../shared/Widgets/HandRankingsWidget\";\nimport SettingsWidget from \"./SettingsWidget\";\nimport ShowCardsToGodButton from \"./ShowCardsToGodButton\";\n// import SocialIcons from \"./SocialIcons\";\nimport RandomSeatsButton from \"./RandomSeatsButton\";\nimport styled from \"styled-components\";\nimport { roles } from \"../../helpers/constants\";\nimport { formatCurrency } from \"../../helpers/utils\";\nimport { MediumHeaderText, SmallHeaderText } from \"../../shared/Text\";\nimport { GameContext } from \"../../context/GameContext\";\n\nconst FixedWidgets = ({\n emitSocketMessage,\n gameId,\n username,\n smallBlindCents,\n isTimerEnabled,\n timerLengthSeconds,\n handTimerLengthSeconds,\n activePlayers,\n isSpectating,\n isTournament,\n initialBlindCents,\n blindIncreaseMinutes,\n}) => {\n const {\n isMobile,\n setAllowKeyboardShortcuts,\n setExistsModal,\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n joinCall,\n setJoinCall,\n setCurrentModal,\n theme,\n setTheme,\n isFourColorDeck,\n setIsFourColorDeck,\n } = useContext(BrowserContext);\n const {\n isGodModeEnabled,\n isGod,\n showCardsToGod,\n god,\n isInCents,\n } = useContext(GameContext);\n const clientPlayer = activePlayers.filter(\n (player) => player.playerName === username\n )[0];\n\n const adminPlayers = activePlayers.filter(\n (player) => player.playerRole === roles.ROLE_ADMIN\n );\n\n return (\n \n {!isSpectating && clientPlayer && (\n \n )}\n {isSpectating && (\n \n )}\n \n \n Lipoker {isGodModeEnabled ? \"God Mode\" : \"\"}\n \n {`Blinds: ${formatCurrency(\n smallBlindCents,\n isInCents\n )}/${formatCurrency(2 * smallBlindCents, isInCents)}`} \n \n \n \n {isTournament && (\n \n )}\n \n \n \n \n \n \n \n );\n};\n\n// a temporary fix, I think the positioning of all of these elements should be refactored to not be absolute positioned\nconst WidgetWrapper = styled.div`\n display: flex;\n position: absolute;\n top: 0;\n right: 0;\n`;\n\nconst HeaderWrapper = styled.div`\n position: absolute;\n ${(props) =>\n props.isMobile ? \"top: 5px; left: 13px;\" : \"top: 5px; left: 20px;\"};\n color: ${({ theme }) => theme.secondary};\n`;\n\nexport default FixedWidgets;\n","import React, { Fragment, useState } from \"react\";\nimport { MTTBrowserSettingsModal } from \"../Modals\";\nimport { Icon } from \"../../shared/FormElements\";\nimport { faCog } from \"@fortawesome/free-solid-svg-icons\";\nimport Widget from \"../../shared/Widget\";\n\nconst MTTSettingsWidget = ({\n gameId,\n username,\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n joinCall,\n setJoinCall,\n setAllowKeyboardShortcuts,\n setExistsModal,\n isMobile,\n smallBlindCents,\n isTimerEnabled,\n timerLengthSeconds, // timer.DEFAULT_TIMER_SECONDS\n handTimerLengthSeconds, // timer.MEDIUM_HAND_TIMER_SECONDS\n isFourColorDeck,\n setIsFourColorDeck,\n isSpectating,\n isTournament,\n isAdmin,\n isInCents,\n adminPlayers,\n}) => {\n const [showSettingsModal, _setShowSettingsModal] = useState(false);\n const [startTransition, setStartTransition] = useState(true);\n\n const setShowSettingsModal = (show) => {\n console.log(\"MTTSettingsWidget\", show);\n setStartTransition(!show);\n _setShowSettingsModal(show);\n setAllowKeyboardShortcuts(!show);\n setExistsModal(show);\n };\n\n return (\n \n setShowSettingsModal(true)}\n text={ }\n showNotif={false}\n left={isMobile ? \"2px\" : \"6px\"}\n dataTestId={\"settings-widget\"}\n />\n {\n setShowSettingsModal(false);\n }}\n sound={sound}\n setSound={setSound}\n showVideoChat={showVideoChat}\n setShowVideoChat={setShowVideoChat}\n videoIsOnRight={videoIsOnRight}\n setVideoIsOnRight={setVideoIsOnRight}\n betButtonsOnRight={betButtonsOnRight}\n setBetButtonsOnRight={setBetButtonsOnRight}\n joinCall={joinCall}\n setJoinCall={setJoinCall}\n smallBlindCents={smallBlindCents}\n isTimerEnabled={isTimerEnabled}\n timerLengthSeconds={timerLengthSeconds} // timer.DEFAULT_TIMER_SECONDS\n handTimerLengthSeconds={handTimerLengthSeconds} // timer.MEDIUM_HAND_TIMER_SECONDS\n isFourColorDeck={isFourColorDeck}\n setIsFourColorDeck={setIsFourColorDeck}\n isSpectating={isSpectating}\n isTournament={isTournament}\n isAdmin={isAdmin}\n isInCents={isInCents}\n adminPlayers={adminPlayers}\n startTransition={startTransition}\n setStartTransition={setStartTransition}\n />\n \n );\n};\n\nexport default MTTSettingsWidget;\n","import React, { Fragment, useContext } from \"react\";\nimport { BrowserContext } from \"../../context/BrowserContext\";\n// import PlayerActionsDropdown from \"./PlayerActionsDropdown\";\nimport FAQWidget from \"../../shared/Widgets/FAQWidget\";\nimport HandRankingsWidget from \"../../shared/Widgets/HandRankingsWidget\";\nimport MTTSettingsWidget from \"./MTTSettingsWidget\";\n// import SocialIcons from \"./SocialIcons\";\nimport styled from \"styled-components\";\nimport { roles } from \"../../helpers/constants\";\nimport { formatCurrency } from \"../../helpers/utils\";\nimport { MediumHeaderText, SmallHeaderText } from \"../../shared/Text\";\nimport { GameContext } from \"../../context/GameContext\";\n\nconst MTTLobbyWidgets = ({\n emitSocketMessage,\n gameId,\n username,\n smallBlindCents,\n isTimerEnabled,\n timerLengthSeconds,\n handTimerLengthSeconds,\n activePlayers,\n isSpectating,\n isTournament,\n initialBlindCents,\n blindIncreaseMinutes,\n}) => {\n const {\n isMobile,\n setAllowKeyboardShortcuts,\n setExistsModal,\n sound,\n setSound,\n showVideoChat,\n setShowVideoChat,\n videoIsOnRight,\n setVideoIsOnRight,\n betButtonsOnRight,\n setBetButtonsOnRight,\n joinCall,\n setJoinCall,\n setCurrentModal,\n theme,\n setTheme,\n isFourColorDeck,\n setIsFourColorDeck,\n } = useContext(BrowserContext);\n const { isInCents } = useContext(GameContext);\n const clientPlayer = activePlayers.filter(\n (player) => player.playerName === username\n )[0];\n\n const adminPlayers = activePlayers.filter(\n (player) => player.playerRole === roles.ROLE_ADMIN\n );\n\n return (\n \n {/* {!isSpectating && clientPlayer && (\n \n )}\n {isSpectating && (\n \n )} */}\n \n \n Lipoker Multi-table Tournament\n \n {/* {`Blinds: ${formatCurrency(\n smallBlindCents,\n isInCents\n )}/${formatCurrency(2 * smallBlindCents, isInCents)}`} */}\n \n \n \n \n \n \n \n );\n};\n\n// a temporary fix, I think the positioning of all of these elements should be refactored to not be absolute positioned\nconst WidgetWrapper = styled.div`\n display: flex;\n position: absolute;\n top: 0;\n right: 0;\n`;\n\nconst HeaderWrapper = styled.div`\n position: absolute;\n ${(props) =>\n props.isMobile ? \"top: 5px; left: 13px;\" : \"top: 5px; left: 20px;\"};\n color: ${({ theme }) => theme.secondary};\n`;\n\nexport default MTTLobbyWidgets;\n","import React, { useState, Fragment } from \"react\";\nimport { colors } from \"../../../helpers/styles\";\nimport { PrimaryButton } from \"../../../shared/FormElements\";\nimport { LoadingWrapper, BoldText } from \"../../../shared/Text\";\nimport { roles } from \"../../../helpers/constants\";\nimport styled from \"styled-components\";\nimport { emit } from \"../../../helpers/socket\";\n\nconst StartMTTRoundAction = ({ clientRole, MTTId }) => {\n const [actionDone, setActionDone] = useState(false);\n\n const onStartRoundClick = () => {\n emit(\"start_mtt_round\", {\n mtt_id: MTTId,\n });\n setActionDone(true);\n };\n\n let startButton;\n\n if (actionDone) {\n startButton = ;\n } else {\n startButton = (\n \n Start Round\n \n );\n }\n return (\n \n {clientRole === roles.ROLE_MTT_ADMIN && startButton}\n {clientRole !== roles.ROLE_MTT_ADMIN && (\n \n Waiting for MTT Admin to {\"start round\"}\n \n )}\n \n );\n};\n\nconst StartMTTRoundWrapper = styled.div`\n text-align: right;\n`;\n\nexport default StartMTTRoundAction;\n","import React, { useContext } from \"react\";\nimport styled from \"styled-components\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n roles,\n} from \"../../../helpers/constants\";\nimport * as Actions from \"./actions\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\nimport { LoadingWrapper, BoldText } from \"../../../shared/Text\";\n\nconst ActionButtons = ({ clientUser }) => {\n const { ownStackCents, username, MTTOngoing, isInCents, MTTId } = useContext(\n GameContext\n );\n\n let actionButtons;\n\n if (!MTTOngoing) {\n actionButtons = (\n \n );\n } else {\n actionButtons = Waiting for Round to End ;\n }\n\n return (\n <>\n {actionButtons} \n >\n );\n};\n\nconst ActionWrapper = styled.div`\n position: absolute;\n z-index: 2;\n right: 20px;\n bottom: 20px;\n display: flex;\n flex-direction: column;\n align-items: right;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n right: 5px;\n bottom: 15px;\n position: fixed;\n }\n`;\n\nexport default ActionButtons;\n","import React, { useState } from \"react\";\nimport { faSyncAlt } from \"@fortawesome/free-solid-svg-icons\";\nimport { Icon } from \"../FormElements\";\nimport styled from \"styled-components\";\n\nconst RefreshButton = ({ refreshAction }) => {\n const [refreshOnChange, setRefreshOnChange] = useState(false);\n const [disableTransition, setDisableTransition] = useState(false);\n const [disableButton, setDisableButton] = useState(false);\n\n const refresh = () => {\n refreshAction();\n setRefreshOnChange(true);\n setDisableButton(true);\n\n setTimeout(() => {\n setDisableTransition(true);\n setRefreshOnChange(false);\n setTimeout(() => {\n setDisableTransition(false);\n setDisableButton(false);\n }, 50);\n }, 700);\n };\n\n return (\n \n \n \n );\n};\n\nconst RefreshOuter = styled.div`\n position: absolute;\n top: 0;\n right: 0;\n z-index: 3;\n display: flex;\n align-items: center;\n height: 25px;\n width: 25px;\n margin: 10px;\n justify-content: center;\n border-radius: 5px;\n background: ${({ theme }) => theme.secondary};\n cursor: pointer;\n svg {\n transition: 0.7s ease all;\n }\n &:hover {\n background: ${({ theme }) => theme.background};\n }\n ${(props) =>\n props.refreshOnChange &&\n `\n svg { transform: rotate(360deg); }\n `}\n ${(props) =>\n props.disableTransition &&\n `\n svg { transition: none; }\n `}\n ${(props) =>\n props.disableButton &&\n `\n pointer-events: none;\n background: ${({ theme }) => theme.secondaryLighter};\n `}\n`;\n\nexport default RefreshButton;\n","import React from \"react\";\nimport styled from \"styled-components\";\n\nconst Loading = ({ size = \"md\" }) => {\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nconst LoadingWrapper = styled.div`\n ${(props) =>\n props.size === \"lg\" &&\n `\n transform: scale(2);\n `}\n ${(props) =>\n props.size === \"sm\" &&\n `\n transform: scale(0.7);\n `}\n`;\n\nexport default Loading;\n","import React, { useEffect } from \"react\";\nimport styled from \"styled-components\";\nimport { MediumHeaderText } from \"../Text\";\nimport { font, gradientScrollStyles } from \"../../helpers/styles\";\nimport { mobileBreakpoint } from \"../../helpers/constants\";\nimport RefreshButton from \"./RefreshButton\";\nimport Loading from \"../Image/Loading\";\nimport Card from \"../Card\";\n\nconst Table = ({\n isMobile,\n header,\n rowData,\n italicizeCols,\n emptyText,\n onRowClick,\n selectedRow,\n getRows,\n isMTTAdmin,\n}) => {\n useEffect(() => {\n getRows();\n }, []);\n\n return (\n \n \n \n \n \n {header.map((item, i) => (\n {item} \n ))}\n \n \n \n \n \n \n {rowData &&\n rowData.map((row, i) => {\n console.log(Object.keys(row));\n return (\n onRowClick(i)}\n selected={selectedRow === i}\n >\n {header.map((colName, j) => (\n \n {colName === \"game id\" && !row[colName]\n ? \"Not in game\"\n : row[colName]}\n \n ))}\n \n {isMTTAdmin && \"EDIT\"}\n \n {/* Empty column for refresh button */}\n \n );\n })}\n \n \n {(rowData === null || rowData.length === 0) && (\n \n {rowData === null && }\n {rowData && rowData.length === 0 && (\n <>\n \n \n \n
\n {emptyText} \n >\n )}\n \n )}\n \n \n );\n};\n\nconst TableOuter = styled.div`\n position: relative;\n @media screen and (max-width: ${mobileBreakpoint}) {\n width: 90%;\n height: 100%;\n }\n`;\n\nconst TableWrapper = styled.div`\n position: relative;\n width: 50vw;\n height: -webkit-calc(100vh - 200px);\n height: -moz-calc(100vh - 200px);\n height: -o-calc(100vh - 200px);\n height: calc(100vh - 200px);\n border-radius: 5px;\n overflow: scroll;\n ${(props) =>\n props.theme &&\n `\n ${gradientScrollStyles(\"rgba(0,0,0,0.3)\", props.theme.secondary)}\n background-color: ${props.theme.secondary};\n border-bottom: 1px solid ${props.theme.secondary};\n `}\n box-shadow: ${({ theme }) => theme.boxShadow};\n @media screen and (max-width: ${mobileBreakpoint}) {\n width: 100%;\n height: 100%;\n }\n &::-webkit-scrollbar {\n display: none;\n }\n -ms-overflow-style: none;\n scrollbar-width: none;\n`;\n\nconst TableMain = styled.table`\n color: white;\n font-family: ${font.body};\n table-layout: auto;\n width: 100%;\n border-collapse: collapse;\n`;\n\nconst TableHeader = styled.thead``;\n\nconst TableHeaderCell = styled.th`\n background: ${({ theme }) => theme.primary};\n z-index: 2;\n top: 0;\n left: 0;\n position: sticky;\n padding: 8px 16px;\n padding-top: 12px;\n font-weight: bold;\n text-transform: uppercase;\n`;\n\nconst TableBody = styled.tbody`\n display: table-row-group;\n`;\n\nconst TableCell = styled.td`\n padding: 8px 16px;\n ${(props) => props.italicize && `font-style: italic;`}\n`;\n\nconst TableRow = styled.tr`\n transform: scale(1); /* position: relative doesn't work in tr, workaround */\n border: 1px dashed ${({ theme }) => theme.secondaryLighter};\n cursor: pointer;\n ${(props) =>\n props.selected &&\n props.theme &&\n `\n background: ${props.theme.secondaryLighter};\n `}\n &:hover {\n background: ${({ theme }) => theme.secondaryLighter};\n }\n`;\n\nconst EmptyText = styled.div`\n color: white;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-top: -45px;\n flex-direction: column;\n`;\n\nexport default Table;\n","import React, { useState, useContext, useEffect } from \"react\";\nimport { GameContext } from \"../../context/GameContext\";\nimport { BrowserContext } from \"../../context/BrowserContext\";\nimport ActionButtons from \"./ActionButtons\";\nimport styled from \"styled-components\";\nimport Table from \"../../shared/Table\";\nimport { mobileBreakpoint } from \"../../helpers/constants\";\nimport { PrimaryButton } from \"../../shared/FormElements\";\nimport { emit } from \"../../helpers/socket\";\n\nconst header = [\"name\", \"stack\", \"game id\"];\n\nconst MTTLobby = ({ data, getPlayers, MTTUsers }) => {\n const { username, MTTId, MTTOngoing } = useContext(GameContext);\n console.log(\"MTT Ongoing\", MTTOngoing);\n const { isMobile } = useContext(BrowserContext);\n\n const onUserClick = (index) => {\n const gameId = data[index].metadata.gameId;\n const currentURL = window.location.href;\n window.location.href = `${currentURL}/game/${gameId}?username=${username}&autojoin=true&spectate=true`;\n };\n\n const onStartClick = () => {\n emit(\"start_mtt_round\", {\n mtt_id: MTTId,\n });\n };\n const clientUser = MTTUsers.filter((user) => user.userName === username)[0];\n const MTTAdminUser = MTTUsers.filter(\n (user) => user.userRole === \"ROLE_MTT_ADMIN\"\n )[0];\n\n return (\n \n \n \n \n \n \n );\n};\n\nconst LobbyOuter = styled.div`\n width: 100%;\n height: 100%;\n text-align: center;\n align-items: center;\n display: flex;\n`;\n\nconst LobbyWrapper = styled.div`\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-bottom: 30px;\n padding-left: 50px;\n @media screen and (max-width: ${mobileBreakpoint}) {\n flex-direction: column-reverse;\n justify-content: flex-start;\n align-items: center;\n padding-left: 0;\n padding-top: 100px;\n height: 95%;\n }\n`;\n\nexport default MTTLobby;\n","import React, { Fragment, useState, useEffect } from \"react\";\nimport { sounds } from \"../../helpers/constants\";\n\nconst AllAudio = ({ setAudioToSoundMap }) => {\n const [audioInitialized, setAudioInitialized] = useState(false);\n\n let callAudio;\n let checkAudio;\n let dingAudio;\n let foldAudio;\n let raiseAudio;\n let winAudio;\n\n const _audioToSoundMap = {\n [sounds.CALL]: callAudio,\n [sounds.CHECK]: checkAudio,\n [sounds.DING]: dingAudio,\n [sounds.FOLD]: foldAudio,\n [sounds.RAISE]: raiseAudio,\n [sounds.WIN]: winAudio,\n };\n\n useEffect(() => {\n // initialize each sound\n Object.keys(_audioToSoundMap).map((soundName) => {\n _audioToSoundMap[soundName] = new Audio(\n `${process.env.PUBLIC_URL}/assets/sounds/${soundName}.mp3`\n );\n });\n\n // the first time the user does an interaction, \"mount/unlock\" the audio\n // to get around the fact that certain browsers require user interaction\n // to auto-play audio\n document.addEventListener(\"click\", unlockAudio);\n\n return () => {\n document.removeEventListener(\"click\", (e) => unlockAudio(e));\n };\n }, []);\n\n const unlockAudio = async (e) => {\n e.preventDefault();\n setTimeout(() => {\n if (!audioInitialized) {\n Object.keys(_audioToSoundMap).map((soundName) => {\n // play and immediately stop\n const audio = _audioToSoundMap[soundName];\n audio.volume = 0;\n audio.play();\n audio.pause();\n audio.volume = 1;\n audio.currentTime = 0;\n });\n setAudioToSoundMap(_audioToSoundMap);\n setAudioInitialized(true);\n document.removeEventListener(\"click\", unlockAudio);\n }\n }, 0);\n };\n\n return ;\n};\n\nexport default AllAudio;\n","import React, { useState, cloneElement } from \"react\";\nimport Notification from \"../Notification\";\n\nconst ModalNotifWrapper = ({\n modalConstant,\n children,\n setModal,\n setExistsModal,\n notification,\n}) => {\n const [showNotif, setShowNotif] = useState(false);\n\n const setShowFeedbackModal = (show) => {\n if (show) {\n setModal(modalConstant);\n } else {\n setModal(null);\n }\n setExistsModal(show);\n };\n\n const hideModal = (showThankYou) => {\n setShowFeedbackModal(false);\n if (showThankYou) {\n setShowNotif(true);\n }\n };\n\n return (\n <>\n setShowNotif(false)}>\n {notification}\n \n {cloneElement(children, { hideModal: hideModal })}\n >\n );\n};\n\nexport default ModalNotifWrapper;\n","import React from \"react\";\nimport { SmallBodyText } from \"../../../shared/Text\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport { faCheckCircle } from \"@fortawesome/free-solid-svg-icons\";\nimport { colors } from \"../../../helpers/styles\";\n\nexport const feedbackNotif = (\n \n \n Thank you for your feedback!\n \n);\n","/**\n * Modal Conductor should be at the root level,\n * and ensures that only one modal is shown at a time,\n * using a single variable to switch on\n * TODO: migrate all modals to use ModalConductor\n */\nimport React from \"react\";\nimport { modals } from \"../../../helpers/constants\";\nimport { refreshPage } from \"../../../helpers/utils\";\nimport {\n BuyInModal,\n FeedbackModal,\n RaisePercentInfoModal,\n DisconnectedModal,\n NoInternetModal,\n DifferentLogInModal,\n PatreonModal,\n} from \"../../Modals\";\nimport ModalNotifWrapper from \"../../../shared/Modal/ModalNotifWrapper\";\nimport { feedbackNotif } from \"./notifications\";\nimport { Offline, Online } from \"react-detect-offline\";\n\nconst ModalConductor = ({\n currentModal,\n setCurrentModal,\n emitSocketMessage,\n ownStackCents,\n gameId,\n username,\n isMobile,\n setExistsModal,\n isSpectating,\n provider,\n isInCents,\n}) => {\n return (\n <>\n setCurrentModal(null)}\n emitSocketMessage={emitSocketMessage}\n ownStackCents={ownStackCents}\n isInCents={isInCents}\n />\n {\n setCurrentModal(null);\n }}\n />\n \n \n \n \n \n \n \n \n setCurrentModal(modal)}\n setExistsModal={setExistsModal}\n notification={feedbackNotif}\n >\n \n \n setCurrentModal(null)}\n />\n >\n );\n};\n\nexport default ModalConductor;\n","import React, { useContext } from \"react\";\nimport AllAudio from \"./AllAudio\";\nimport ModalConductor from \"./ModalConductor\";\nimport { GlobalStyle } from \"../../helpers/styles\";\nimport { BrowserContext } from \"../../context/BrowserContext\";\nimport { connectEmitSocketMessage } from \"../../helpers/socket\";\nimport { GameContext } from \"../../context/GameContext\";\nimport FullStory from \"react-fullstory\";\nimport Heap from \"react-heap\";\nimport \"./polyfills\";\n\nconst FULLSTORY_ORG_ID = \"TFSSC\"; // Fill this in here\nconst FULLSTORY_NAMESPACE = \"FS\";\n\nconst Global = ({ gameId, provider }) => {\n const {\n sessionKey,\n username,\n ownStackCents,\n isSpectating,\n isInCents,\n } = useContext(GameContext);\n const {\n setAudioToSoundMap,\n currentModal,\n setCurrentModal,\n isMobile,\n setExistsModal,\n } = useContext(BrowserContext);\n const emitSocketMessage = connectEmitSocketMessage(\n sessionKey,\n gameId,\n username\n );\n return (\n <>\n \n \n \n \n \n >\n );\n};\n\nexport default Global;\n","import React, { useState, useEffect } from \"react\";\nimport Card from \"./\";\nimport CardBack from \"./CardBack\";\nimport sizes from \"./sizes\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst FlipCard = (props) => {\n const { isFlat, ...rest } = props;\n const [isFaceUp, setIsFaceUp] = useState(false);\n\n useEffect(() => {\n setTimeout(() => {\n setIsFaceUp(true);\n }, 400);\n }, []);\n\n return (\n \n \n \n \n \n \n \n \n \n \n );\n};\n\nconst FlipCardInner = styled.div`\n position: relative;\n width: 100%;\n height: 100%;\n text-align: center;\n transition: transform 0.2s cubic-bezier(0.47, 0, 0.75, 0.72);\n transform-style: preserve-3d;\n`;\n\nconst FlipCardOuter = styled.div`\n background-color: transparent;\n ${(props) =>\n props.size &&\n `\n height: ${sizes[props.size][\"height\"]};\n width: ${sizes[props.size][\"width\"]};\n `}\n perspective: 1000px;\n margin: 5px;\n ${(props) =>\n props.isFaceUp &&\n `\n ${FlipCardInner} {\n transform: rotateY(180deg);\n }\n `}\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n margin: 2px;\n }\n`;\n\nconst FlipCardInnerChild = styled.div`\n position: absolute;\n width: 100%;\n height: 100%;\n -webkit-backface-visibility: hidden; /* Safari */\n backface-visibility: hidden;\n margin: 0;\n border-radius: 5px;\n overflow: hidden;\n`;\n\nconst FlipCardFront = styled(FlipCardInnerChild)`\n transform: rotateY(180deg);\n`;\n\nconst FlipCardBack = styled(FlipCardInnerChild)``;\n\nexport default FlipCard;\n","import React, { Fragment, useState } from \"react\";\nimport { colors } from \"../../../helpers/styles\";\nimport { BackgroundButton, Icon } from \"../../../shared/FormElements\";\nimport { faCopy, faCheck, faTimes } from \"@fortawesome/free-solid-svg-icons\";\nimport { copyTextToClipboard } from \"../../../helpers/utils\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst DURATION = 1000;\nconst CopyJoinLink = () => {\n const [showSuccess, setShowSuccess] = useState(false);\n const [showFail, setShowFail] = useState(false);\n\n const copyGameInfo = (_) => {\n const url = window.location.href;\n copyTextToClipboard(url).then((success) => {\n _showCopyResult(success);\n });\n };\n\n const _showCopyResult = (success) => {\n if (success) {\n setShowSuccess(true);\n } else {\n setShowFail(true);\n }\n setTimeout(() => {\n if (success) {\n setShowSuccess(false);\n } else {\n setShowFail(false);\n }\n }, DURATION);\n };\n\n let inner;\n if (showSuccess) {\n inner = (\n \n \n COPIED \n \n );\n } else if (showFail) {\n inner = (\n \n \n FAILED \n \n );\n } else {\n inner = (\n \n \n COPY GAME LINK \n \n );\n }\n\n return (\n \n {inner}\n \n );\n};\n\nconst JoinLinkButton = styled(BackgroundButton)`\n color: ${({ theme }) => theme.text.widget};\n background: ${({ theme }) => theme.foreground};\n width: 165px;\n ${(props) =>\n props.actionDone &&\n `\n pointer-events: none;\n `}\n @media screen and (max-width: ${mobileBreakpoint}), screen and (max-height: ${mobileHeightBreakpoint}) {\n width: auto;\n }\n &:hover {\n background: ${({ theme }) => theme.background};\n }\n`;\nexport default CopyJoinLink;\n","import React, { useContext, useState } from \"react\";\nimport styled from \"styled-components\";\nimport { formatCurrency } from \"../../../helpers/utils\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport { colors } from \"../../../helpers/styles\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { MediumBodyText } from \"../../../shared/Text\";\nimport { useStateWithLocalStorageString } from \"../../../hooks/useStateWithLocalStorage\";\n\nconst CentralPot = () => {\n const { mainPotCents, betsOnTableSumCents, isInCents } = useContext(\n GameContext\n );\n let [potHovered, setPotHovered] = useState(false);\n let [justSwitchedPot, setJustSwitchedPot] = useState(false);\n const [potType, setPotType] = useStateWithLocalStorageString(\n \"centralPotType\",\n \"defaultPot\"\n );\n\n const potOptions = {\n defaultPot: {\n value: formatCurrency(mainPotCents, isInCents)\n ? formatCurrency(mainPotCents, isInCents)\n : 0,\n hoverText: \"Switch to Total Pot\",\n caption: \"Pot: \",\n switchTo: \"totalPot\",\n },\n totalPot: {\n value: formatCurrency(mainPotCents + betsOnTableSumCents, isInCents)\n ? formatCurrency(mainPotCents + betsOnTableSumCents, isInCents)\n : 0,\n hoverText: \"Switch to Pot\",\n caption: \"Total Pot: \",\n switchTo: \"defaultPot\",\n },\n };\n\n const switchPot = () => {\n setJustSwitchedPot(true);\n setPotType(potOptions[potType].switchTo);\n };\n\n let allPots = (\n setPotHovered(true)}\n onMouseLeave={() => {\n setPotHovered(false);\n setJustSwitchedPot(false);\n }}\n >\n {!potHovered\n ? `${potOptions[potType].caption}${potOptions[potType].value}`\n : !justSwitchedPot\n ? `${potOptions[potType].hoverText}`\n : `${potOptions[potType].caption}${potOptions[potType].value}`}\n \n );\n return <> {allPots} >;\n};\n\nconst MainPot = styled(MediumBodyText)`\n position: relative;\n cursor: pointer;\n color: white;\n cursor: pointer;\n width: 150px;\n height: 30px;\n font-size: 18px;\n background: ${({ theme }) => theme.primary};\n display: flex;\n border-radius: 5px;\n align-items: center;\n justify-content: center;\n color: ${colors.white};\n transition: all 300ms ease-in-out;\n -moz-transition: all 300ms ease-in-out;\n -webkit-transition: all 300ms ease-in-out;\n opacity: 1;\n z-index: -1;\n transform: rotateX(0deg);\n ${(props) =>\n props.potType === \"totalPot\" &&\n `\n transform: rotateX(360deg);\n `}\n\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 0.7em;\n height: 18px;\n width: 100px;\n }\n`;\n\n// const InvalidPot = styled(MainPot)`\n// background: #aaaaaa;\n// color: #555555;\n// `;\n\n// const PotToggleExplanation = styled(MainPot)`\n// // color: ${colors.white};\n// position: absolute;\n// cursor: pointer;\n// // transform: translate(0px, -1.5em);\n// transform: rotateX(180deg);\n// ${(props) =>\n// props.activated\n// ? `opacity: 1;\n// transform: rotateX(0deg);`\n// : `opacity: 0;`}\n// transition: all 300ms ease-in-out;\n// -moz-transition: all 300ms ease-in-out;\n// -webkit-transition: all 300ms ease-in-out;\n// z-index: -1;\n// `;\n\nexport default CentralPot;\n","import React, { useContext } from \"react\";\nimport styled from \"styled-components\";\nimport Card from \"../../../shared/Card\";\nimport FlipCard from \"../../../shared/Card/FlipCard\";\n\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport { convertCardStringToValues } from \"../../../helpers/utils\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\nimport CopyJoinLink from \"./CopyJoinLink\";\nimport CentralPot from \"../CentralPotView\";\n\nconst maxCardsNumber = 5;\n\nconst Table = ({ playersAreInHand, isMobile, iframeClientName }) => {\n const { faceUpHoleCards } = useContext(GameContext);\n const { isFourColorDeck } = useContext(BrowserContext);\n\n const emptyCards = new Array(\n maxCardsNumber - Object.keys(faceUpHoleCards).length\n ).fill();\n\n const innerTable = playersAreInHand ? (\n <>\n {Object.keys(faceUpHoleCards).map((_, i) => {\n const cardObject = convertCardStringToValues(faceUpHoleCards[i].card);\n return (\n \n );\n })}\n {emptyCards.map((_, i) => (\n \n ))}\n >\n ) : (\n iframeClientName === \"game\" && \n );\n\n return (\n \n \n \n \n \n \n {innerTable} \n \n \n \n );\n};\n\nconst PotWrapper = styled.div`\n display: flex;\n // top: 30px;\n cursor: pointer;\n position: absolute;\n width: auto;\n transform: translateY(-100%);\n top: -10px;\n justify-content: space-around;\n`;\n\nconst TableWrapper = styled.div`\n height: 100%;\n`;\n\nconst TableOuter = styled.div`\n position: absolute;\n width: 70%;\n height: 70%;\n left: 50%;\n top: 5%;\n -webkit-transform: translate(-50%);\n -moz-transform: translate(-50%);\n transform: translate(-50%);\n text-align: center;\n z-index: 0;\n background: ${({ theme }) => theme.table.inner};\n border: 10px solid ${({ theme }) => theme.table.outer};\n border-radius: 200px;\n display: flex;\n align-items: center;\n justify-content: center;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n top: 3%;\n }\n`;\n\nconst TableItems = styled.div`\n position: relative;\n display: flex;\n justify-content: space-between;\n flex-direction: column;\n align-items: center;\n width: 100%;\n`;\n\nconst CardsWrapper = styled.div`\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: center;\n`;\n\nexport default Table;\n","const offsets = {\n xCenterRight: \"75.9%\",\n xCenterLeft: \"24.1%\",\n xEdgeRight: \"94%\",\n xEdgeLeft: \"6%\",\n yTop: \"-18%\",\n yCenterTop: \"4%\",\n yCenterBottom: \"58%\",\n yBottom: \"80%\",\n};\n\nconst xBetEdge = 21;\nconst xBetCenterOff = 24.1;\n\nconst betOffsets = {\n yTop: \"11%\",\n yCenterTop: \"24%\",\n yCenterBottom: \"44%\",\n yBottom: `60%`,\n xCenterRight: `${100 - xBetCenterOff}%`,\n xFarRight: `${100 - xBetEdge}%`,\n xCenter: \"50%\",\n xCenterLeft: `${xBetCenterOff}%`,\n xFarLeft: `${xBetEdge}%`,\n};\n\nexport const playerStyles = {\n playerPos: {\n \"0\": {\n left: offsets.xCenterRight,\n top: offsets.yTop,\n },\n \"1\": {\n left: offsets.xEdgeRight,\n top: offsets.yCenterTop,\n },\n \"2\": {\n left: offsets.xEdgeRight,\n top: offsets.yCenterBottom,\n },\n \"3\": {\n left: offsets.xCenterRight,\n top: offsets.yBottom,\n },\n \"4\": {\n left: \"50%\",\n top: offsets.yBottom,\n },\n \"5\": {\n left: offsets.xCenterLeft,\n top: offsets.yBottom,\n },\n \"6\": {\n left: offsets.xEdgeLeft,\n top: offsets.yCenterBottom,\n },\n \"7\": {\n left: offsets.xEdgeLeft,\n top: offsets.yCenterTop,\n },\n \"8\": {\n left: offsets.xCenterLeft,\n top: offsets.yTop,\n },\n },\n centralPotPos: {\n left: \"50%\",\n top: \"25.5%\",\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n upperPotPos: {\n left: \"50%\",\n top: \"22.5%\",\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n lowerPotPos: {\n left: \"50%\",\n top: \"29.5%\",\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n playerBetPos: {\n \"0\": {\n left: betOffsets.xCenterRight,\n top: betOffsets.yTop,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"1\": {\n left: betOffsets.xFarRight,\n top: betOffsets.yCenterTop,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"2\": {\n left: betOffsets.xFarRight,\n bottom: betOffsets.yCenterBottom,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"3\": {\n left: betOffsets.xCenterRight,\n top: betOffsets.yBottom,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"4\": {\n left: betOffsets.xCenter,\n top: betOffsets.yBottom,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"5\": {\n left: betOffsets.xCenterLeft,\n top: betOffsets.yBottom,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"6\": {\n left: betOffsets.xFarLeft,\n bottom: betOffsets.yCenterBottom,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"7\": {\n left: betOffsets.xFarLeft,\n top: betOffsets.yCenterTop,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n \"8\": {\n left: betOffsets.xCenterLeft,\n top: betOffsets.yTop,\n transform: \"translate(-50%, -50%)\",\n msTransform: \"translate(-50%, -50%)\",\n WebkitTransform: \"translate(-50%, -50%)\",\n },\n },\n};\n","import React from \"react\";\nimport { colors, font } from \"../../helpers/styles\";\nimport styled from \"styled-components\";\n\nconst ActionBar = ({\n show,\n actionsList,\n style,\n handleMouseEnter,\n handleMouseLeave,\n}) => {\n return (\n \n {actionsList.map((action) => (\n \n {action.name} \n \n {action.inner}\n \n \n ))}\n \n );\n};\n\nconst ActionBarContent = styled.div`\n background: ${colors.lightGrey2};\n border: 1px solid ${({ theme }) => theme.foreground};\n border-radius: 30px;\n height: 60px;\n display: flex;\n justify-content: space-between;\n padding: 5px;\n ${(props) =>\n !props.show &&\n `\n opacity: 0;\n pointer-events: none;\n `}\n user-select: none;\n`;\n\nconst ActionBarItemLabel = styled.div`\n position: absolute;\n pointer-events: none;\n top: -10px;\n left: 50%;\n transform: translate(-50%, -100%);\n background: ${({ theme }) => theme.foreground};\n color: ${colors.black};\n font-weight: bold;\n font-family: ${font.body};\n font-size: 12px;\n border-radius: 5px;\n min-width: max-content;\n padding: 2px 5px;\n opacity: 0;\n`;\n\nconst ActionBarItem = styled.div`\n background: ${(props) =>\n props.selected\n ? ({ theme }) => theme.secondary\n : ({ theme }) => theme.primary};\n cursor: pointer;\n font-family: ${font.body};\n ${(props) =>\n props.isLoading &&\n `\n pointer-events: none;\n background: ${({ theme }) => theme.foreground};\n `}\n height: 30px;\n border-radius: 30px;\n color: ${colors.white};\n display: flex;\n align-items: center;\n justify-content: center;\n transition: 0.2s ease transform;\n transform-origin: bottom;\n padding: 4px;\n`;\n\nconst ActionBarItemWrapper = styled.div`\n position: relative;\n margin-left: 10px;\n &:first-child {\n margin: 0;\n }\n &:hover {\n ${ActionBarItem} {\n transform: scale(1.2);\n background: ${({ theme }) => theme.secondary};\n }\n ${ActionBarItemLabel} {\n opacity: 1;\n }\n }\n`;\n\nexport default ActionBar;\n","import React, { useState } from \"react\";\nimport ActionBar from \"../../../shared/ActionBar\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport {\n faSync,\n faBan,\n faCoins,\n faDollarSign,\n} from \"@fortawesome/free-solid-svg-icons\";\nimport { colors, font } from \"../../../helpers/styles\";\nimport { formatCurrency } from \"../../../helpers/utils\";\nimport styled from \"styled-components\";\n\nconst actionBarPositioning = {\n position: \"absolute\",\n height: \"auto\",\n top: \"-15px\",\n right: 0,\n transform: \"translate(50%,-100%)\",\n};\n\nconst BuyInActionBar = ({\n emitSocketMessage,\n name,\n pos,\n requestedBuyIn,\n pendingBuyIn,\n isInCents,\n}) => {\n const [showActionBar, setShowActionBar] = useState(false);\n const [actionBarHoverTimeout, setActionBarHoverTimeout] = useState(null);\n const [isApproved, setApproveBuyIn] = useState(false);\n\n const handleMouseEnter = () => {\n clearTimeout(actionBarHoverTimeout);\n setShowActionBar(true);\n };\n\n const handleMouseLeave = () => {\n setActionBarHoverTimeout(setTimeout(() => setShowActionBar(false), 1400));\n };\n\n const approveBuyIn = () => {\n setApproveBuyIn(true);\n setShowActionBar(false);\n emitSocketMessage(\"approve_buy_in\", {\n name: name,\n amount: parseInt(requestedBuyIn),\n });\n };\n\n const declineBuyIn = () => {\n setApproveBuyIn(true);\n setShowActionBar(false);\n emitSocketMessage(\"approve_buy_in\", {\n name: name,\n amount: parseInt(pendingBuyIn),\n });\n };\n\n const actionsList = [\n {\n name: \"Accept\",\n icon: faSync,\n inner: (\n <>\n {pendingBuyIn > 0 ? (\n \n ) : (\n \"+\"\n )}\n \n {formatCurrency(requestedBuyIn, isInCents)}\n \n >\n ),\n dataTestId: `accept-buyin${pos}`,\n onClick: () => approveBuyIn(),\n },\n {\n name: \"Decline\",\n icon: faBan,\n inner: ,\n dataTestId: `decline-buyin${pos}`,\n onClick: () => declineBuyIn(),\n },\n ];\n\n console.log(\"BuyInActionBar\", name, pos, requestedBuyIn, pendingBuyIn)\n return (\n <>\n setShowActionBar(!showActionBar)}\n onMouseEnter={handleMouseEnter}\n selected={showActionBar}\n data-testid={`buyin-toggle${pos}`}\n >\n {!isInCents && }\n {isInCents && (\n \n )}\n \n \n >\n );\n};\n\nconst ActionBarToggleButton = styled.div`\n width: 27px;\n height: 27px;\n font-family: ${font.header};\n cursor: pointer;\n font-size: 16px;\n border-radius: 50%;\n background-color: ${({ theme }) => theme.tertiary};\n color: ${colors.white};\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n right: 0;\n top: 0;\n transform: translate(50%, -50%);\n font-weight: bold;\n border: 1px solid ${colors.lightGrey2};\n ${(props) =>\n props.selected &&\n `\n background: ${colors.grey};\n color: ${colors.white};\n `}\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 16px;\n height: 16px;\n font-size: 0.5em;\n }\n user-select: none;\n`;\n\nconst BuyInWrapper = styled.div`\n margin-left: 1.5px;\n`;\n\nexport default BuyInActionBar;\n","import React, { Fragment } from \"react\";\nimport styled from \"styled-components\";\nimport Card, {\n cardHeight,\n mobileCardHeight,\n cardHeightRaw,\n mobileCardHeightRaw,\n} from \"../../../shared/Card\";\nimport { playerStyles } from \"./player_styles\";\nimport { SmallBodyText, SmallHeaderTextEllipsis } from \"../../../shared/Text\";\nimport { Icon } from \"../../../shared/FormElements\";\nimport BuyInActionBar from \"./BuyInActionBar\";\nimport { formatCurrency } from \"../../../helpers/utils\";\nimport { colors, font } from \"../../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n roles,\n} from \"../../../helpers/constants\";\nimport {\n convertCardStringToValues,\n posFromClientPOV,\n} from \"../../../helpers/utils\";\nimport { emitSocketMessage } from \"../../../helpers/socket\";\nimport {\n faUserCog,\n faStar,\n faEye,\n faEyeSlash,\n faCoins,\n} from \"@fortawesome/free-solid-svg-icons\";\n// import { GameContext } from \"../../context/GameContext\";\n\nconst Player = ({\n name,\n cards,\n balance,\n isClient,\n clientRole,\n playerRole,\n isInHand,\n isTurn,\n isWinner,\n isDealer,\n bestHand,\n pos,\n clientPos,\n pot,\n connected,\n requestedBuyIn,\n pendingBuyIn,\n isMobile,\n isFourColorDeck,\n emitSocketMessage,\n isSpectating,\n maxPlayerNumber,\n isGodModeEnabled,\n isGod,\n showCardsToGod,\n isInCents,\n}) => {\n let playerHand = ;\n let posStyle =\n playerStyles.playerPos[posFromClientPOV(pos, clientPos, maxPlayerNumber)];\n let betPosStyle =\n playerStyles.playerBetPos[\n posFromClientPOV(pos, clientPos, maxPlayerNumber)\n ];\n let nameLabel = (\n \n {playerRole === roles.ROLE_ADMIN && (\n \n )}\n {isTurn && isClient ? \"YOUR TURN\" : name} \n \n );\n\n if (cards) {\n playerHand = (\n \n {Object.keys(cards).map((key) => {\n const cardDisplay = cards[key].card;\n const cardObject = convertCardStringToValues(cardDisplay);\n return (\n \n );\n })}\n \n );\n } else if (!isClient && isInHand) {\n playerHand = (\n \n \n \n \n );\n }\n return (\n \n \n \n \n {isWinner && (\n \n )}\n \n {playerHand}\n \n \n {isDealer && (\n \n D \n \n )}\n\n {/* God Mode enabled -- display Player icons*/}\n {isGodModeEnabled &&\n /* Normal player showing cards to god */\n ((!isGod && showCardsToGod && (\n \n \n {\"Showing cards to God\"} \n \n )) ||\n /* Normal player not showing cards to god */\n (!isGod && !showCardsToGod && (\n \n \n {\"Not showing cards to God\"} \n \n )) ||\n /* Player is god of game */\n (isGod && (\n \n \n \n {\"God - can see certain player's cards\"}\n \n \n )))}\n\n {!isSpectating &&\n clientRole === roles.ROLE_ADMIN &&\n !(requestedBuyIn === null || requestedBuyIn === 0) && (\n \n )}\n {!(pendingBuyIn === null || pendingBuyIn === 0) && (\n +{formatCurrency(pendingBuyIn, isInCents)} \n )}\n {nameLabel}\n \n {isMobile && `/`}\n {connected\n ? `${formatCurrency(balance, isInCents)} `\n : \"DISCONNECTED\"}\n {!isInCents && (\n \n )}\n {!isMobile && !!bestHand && isInHand && ` / ${bestHand}`}\n \n \n {!isTurn && }\n \n {pot !== 0 && (\n \n {`${formatCurrency(pot, isInCents)} `}\n {!isInCents && }\n \n )}\n \n );\n};\n\nconst iconHeight = 30;\nconst mobileIconHeight = 12;\n\nconst PlayerWrapper = styled.div`\n position: absolute;\n display: block;\n width: 145px;\n height: 160px;\n -webkit-transform: translate(-50%, -50%);\n -moz-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 90px;\n height: 80px;\n }\n`;\n\nconst PlayerHeadingContent = styled.div`\n display: block;\n height: ${cardHeightRaw + iconHeight}px;\n\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: ${mobileCardHeightRaw + mobileIconHeight}px;\n }\n`;\n\nconst IconWrapper = styled.div`\n height: ${iconHeight}px;\n img {\n height: ${iconHeight}px;\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: ${mobileIconHeight}px;\n img {\n height: ${mobileIconHeight}px;\n margin-top: -10px;\n }\n }\n`;\n\nconst CardsWrapper = styled.div`\n height: ${cardHeight};\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: ${mobileCardHeight};\n }\n`;\n\nconst NameWrapper = styled.div`\n display: flex;\n flex-direction: row;\n`;\n\nexport const MainContent = styled.div`\n position: absolute;\n z-index: 3;\n bottom: 0;\n width: 100%;\n height: 60px;\n border-radius: 5px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n color: white;\n padding: 0 8px;\n background: ${({ theme }) => theme.secondary};\n border: 1px solid ${({ theme }) => theme.player.borderColor};\n ${(props) =>\n props.highlight &&\n props.theme &&\n `\n background: ${props.theme.primary};\n border: 1px solid ${props.theme.player.borderHighlightedColor};\n `}\n ${(props) =>\n props.disable &&\n props.theme &&\n `background: ${props.theme.player.disabledBackground};`}\n span {\n margin-top: 2px;\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n display: flex;\n flex-direction: row;\n height: 27px;\n width: 90px;\n padding: 0 6px;\n }\n`;\n\nconst MainContentShadow = styled(MainContent)`\n background: ${({ theme }) => theme.flatShadow};\n border: none;\n transform: translate(4px, 4px);\n z-index: 0;\n pointer-events: none;\n`;\n\nconst PlayerText = styled(SmallBodyText)`\n color: ${colors.white};\n text-align: center;\n background: none;\n line-height: 20px;\n margin: 0 auto;\n width: 60px; //making this 100% makes it off center\n font-weight: bold;\n`;\n\nconst PlayerPot = styled(PlayerText)`\n position: absolute;\n z-index: 2;\n background: ${({ theme }) => theme.player.pot};\n border-radius: 5px;\n color: white;\n transform: translate(-50%, -50%);\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n background: ${colors.grey};\n }\n`;\n\nconst DealerButton = styled.div`\n width: 20px;\n height: 20px;\n font-family: ${font.header};\n font-size: 14px;\n border-radius: 50%;\n background-color: ${colors.white};\n color: ${({ theme }) => theme.secondary};\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n left: 0;\n top: 0;\n transform: translate(650%, -50%);\n font-weight: bold;\n border: 1px solid ${({ theme }) => theme.foreground};\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 10px;\n height: 10px;\n font-size: 0.5em;\n }\n`;\n\nconst RoleLabel = styled.div`\n display: none;\n margin-left: 10px;\n pointer-events: none;\n background: ${({ theme }) => theme.foreground};\n color: ${({ theme }) => theme.text.label};\n font-weight: bold;\n font-family: ${font.body};\n font-size: 12px;\n border-radius: 5px;\n height: fit-content;\n min-width: max-content;\n padding: 2px 5px;\n`;\n\nconst RoleButton = styled.div`\n width: 20px;\n height: 20px;\n font-family: ${font.header};\n font-size: 14px;\n border-radius: 50%;\n background-color: ${colors.white};\n color: ${({ theme }) => theme.secondary};\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n left: 0;\n top: 0;\n transform: translate(-50%, -50%);\n font-weight: bold;\n border: 1px solid ${({ theme }) => theme.foreground};\n &:hover {\n cursor: pointer;\n background: ${({ theme }) => theme.primary};\n ${RoleLabel} {\n display: block;\n }\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 10px;\n height: 10px;\n font-size: 0.5em;\n }\n`;\n\nconst AddMoney = styled.div`\n font-family: ${font.body};\n font-size: 14px;\n border-radius: 5px;\n position: absolute;\n left: 0;\n top: 0;\n transform: translate(50%, -50%);\n background: ${({ theme }) => theme.primary};\n border: 1px solid ${({ theme }) => theme.foreground};\n padding: 2px 6px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n font-size: 8px;\n }\n`;\n\nconst NameText = styled(SmallHeaderTextEllipsis)`\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n text-align: left;\n }\n margin-left: 2px;\n`;\n\nexport default Player;\n","import React, { Fragment, useContext, useState, useEffect } from \"react\";\nimport Player, { MainContent } from \"./Player\";\nimport { playerStyles } from \"./player_styles\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\nimport { colors } from \"../../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n roles,\n} from \"../../../helpers/constants\";\nimport { setAutoCheckFoldEmit } from \"../../../helpers/socket\";\nimport { SmallHeaderText } from \"../../../shared/Text\";\nimport { test_players } from \"./test_players\";\nimport styled from \"styled-components\";\nimport { posFromClientPOV } from \"../../../helpers/utils\";\n\nconst IS_TESTING = false;\nconst PlayersAndPots = ({ emitSocketMessage, winningPlayers }) => {\n const {\n username,\n activePlayers,\n faceUpHoleCards,\n dealerPosition,\n isTimerEnabled,\n timerLengthSeconds,\n gameId,\n sessionKey,\n turnNonce,\n isSpectating,\n maxPlayerNumber,\n isGodModeEnabled,\n isInCents,\n } = useContext(GameContext);\n const { isMobile, isFourColorDeck } = useContext(BrowserContext);\n\n const [turnIndex, setTurnIndex] = useState(-1);\n const [turnName, setTurnName] = useState(\"\");\n const [isAnimating, setIsAnimating] = useState(false);\n const [restartingAnimation, setRestartingAnimation] = useState(false);\n const [holeCardCount, setHoleCardCount] = useState(0);\n const [checkFoldTimeoutObject, setCheckFoldTimeoutObject] = useState(null);\n const [timeLeft, setTimeLeft] = useState(timerLengthSeconds * 1000);\n const [endsAt, setEndsAt] = useState(Date.now() + 1000 * timerLengthSeconds);\n\n // does it make sense to have these variables outside useeffect\n const playersListOrigin = IS_TESTING ? test_players : activePlayers;\n const playersList = playersListOrigin.filter((player) => player.isAtTable);\n const clientPlayer = !isSpectating\n ? activePlayers.filter((player) => player.playerName === username)[0]\n : { playerRole: roles.ROLE_SPECTATOR };\n let emptySeats = new Array(maxPlayerNumber).fill(true);\n let newTurnIndex = -1;\n playersList.map((player, i) => {\n if (player.playerIsTurn) {\n newTurnIndex = player.pos;\n if (turnName !== player.playerName) {\n setTurnName(player.playerName);\n }\n }\n });\n let newHoleCardCount = faceUpHoleCards.length;\n let winningPlayersExist = winningPlayers && winningPlayers.length > 0;\n\n let onAnimationEnd = () => {\n setAutoCheckFoldEmit(\n username,\n gameId,\n sessionKey,\n turnName,\n turnNonce,\n (checkFoldTimeoutObject) => {\n setCheckFoldTimeoutObject(checkFoldTimeoutObject);\n }\n );\n };\n\n useEffect(() => {\n let shouldResetTimer =\n (newTurnIndex >= 0 && newTurnIndex !== turnIndex) ||\n holeCardCount !== newHoleCardCount;\n if (shouldResetTimer) {\n setIsAnimating(false);\n if (checkFoldTimeoutObject) {\n clearTimeout(checkFoldTimeoutObject);\n }\n setCheckFoldTimeoutObject(null);\n }\n if (newTurnIndex >= 0 && shouldResetTimer) {\n setRestartingAnimation(true);\n }\n if (restartingAnimation && newTurnIndex >= 0) {\n setRestartingAnimation(false);\n setIsAnimating(true);\n setTimeLeft(timerLengthSeconds * 1000);\n setEndsAt(Date.now() + 1000 * timerLengthSeconds);\n }\n setTurnIndex(newTurnIndex);\n setHoleCardCount(newHoleCardCount);\n }, [\n newTurnIndex,\n newHoleCardCount,\n restartingAnimation,\n isTimerEnabled,\n checkFoldTimeoutObject,\n winningPlayersExist,\n ]);\n\n useEffect(() => {\n if (isTimerEnabled && isAnimating) {\n if (timeLeft > 0) {\n let remainingTime = endsAt - Date.now();\n const id = setTimeout(() => setTimeLeft(remainingTime), 500);\n return () => clearTimeout(id);\n } else {\n onAnimationEnd();\n }\n }\n }, [timeLeft, isAnimating]);\n\n let clientPos =\n !isSpectating && activePlayers.length > 0\n ? (\n activePlayers.find((player) => {\n return player.playerName === username;\n }) || {}\n ).pos\n : 0;\n\n let players = (\n \n {\n /** Players */\n playersList.map((player, i) => {\n if (player) {\n emptySeats[player.pos] = false;\n return (\n \n \n \n );\n }\n })\n }\n {\n /** Open seats */\n emptySeats.map((isEmpty, i) => {\n if (isEmpty) {\n return (\n \n OPEN \n \n );\n }\n return ;\n })\n }\n \n );\n\n return (\n \n {players}\n {turnIndex !== -1 &&\n (isTimerEnabled ? (\n \n ) : (\n \n ))}\n \n );\n};\n\nconst TurnShadow = styled(MainContent)`\n transition: 0.6s ease all;\n position: absolute;\n z-index: -1;\n border: 2px dashed white;\n background: ${({ theme }) => theme.primary};\n border-radius: 30px;\n width: 180px;\n height: 90px;\n transform: translate(-90px, -50%);\n margin-top: 50px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 110px;\n height: 40px;\n margin-top: 27px;\n border-radius: 15px;\n transform: translate(-55px, -50%);\n }\n`;\n\nconst TurnShadowAnimated = styled(TurnShadow)`\n z-index: 4;\n transition: left 0.6s ease, top 0.6s ease, width 0.5s linear;\n padding: 0;\n ${(props) =>\n props.ratio &&\n `\n width: ${props.ratio > 0 ? 180 * props.ratio : 0}px;\n border: ${props.ratio > 0 ? 1 : 0}px solid white;\n height: 13px;\n transform: translate(-90px, 240%);\n `}\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n ${(props) =>\n props.ratio &&\n `\n width: ${props.ratio > 0 ? 110 * props.ratio : 0}px;\n border: ${props.ratio > 0 ? 1 : 0}px solid white;\n height: 15px;\n transform: translate(-55px, 220%);\n `}\n }\n`;\n\nconst OpenSeat = styled.div`\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${({ theme }) => theme.openSeat.background};\n color: ${colors.darkGrey2};\n border: 2px dashed ${({ theme }) => theme.openSeat.borderColor};\n width: 70px;\n height: 40px;\n border-radius: 20px;\n margin-top: 40px;\n -webkit-transform: translate(-50%, -50%);\n -moz-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 30px;\n width: 50px;\n z-index: -1;\n }\n`;\n\nexport default PlayersAndPots;\n","import React, { useRef, useState, useEffect } from \"react\";\nimport {\n Slider,\n PrimaryButton,\n InputText,\n KeyboardChar,\n} from \"../../../../shared/FormElements\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../../helpers/constants\";\nimport { faQuestionCircle } from \"@fortawesome/free-solid-svg-icons\";\nimport { colors } from \"../../../../helpers/styles\";\nimport { SecondaryButton, Icon } from \"../../../../shared/FormElements\";\nimport { modals } from \"../../../../helpers/constants\";\nimport { formatCurrency } from \"../../../../helpers/utils\";\nimport styled from \"styled-components\";\n\nconst RaiseView = ({\n isMobile,\n betOnTableCents,\n betsOnTableSumCents,\n smallBlindCents,\n minBetCents,\n minBetSliderCents,\n maxBetSliderCents,\n onBetClick,\n setShowRaiseView,\n callAmountCents,\n ownBetOnTableCents,\n betValueCents,\n setBetValueCents,\n bigBlindCents,\n mainPotCents,\n isPreFlop,\n setCurrentModal,\n isInCents,\n}) => {\n const betInputElement = useRef(null);\n const [betFocus, setBetFocus] = useState(false);\n const [potFocus, setPotFocus] = useState(false);\n\n useEffect(() => {\n if (!betFocus) {\n // round betValueCents\n if (betValueCents) {\n const betValueCentsFloat = parseInt(betValueCents);\n setBetValueCents(betValueCentsFloat);\n }\n }\n }, [betFocus]);\n\n const enterPressedForBet = (event) => {\n if (!isInCents && !/[0-9]/.test(event.key)) {\n event.preventDefault();\n }\n var code = event.keyCode || event.which;\n const bet = parseInt(betValueCents || minBetCents);\n if (\n code === 13 &&\n bet >= minBetSliderCents &&\n bet <= maxBetSliderCents &&\n bet !== 0\n ) {\n setBetValueCents(bet);\n onBetClick(bet);\n }\n };\n\n const getPercentPot = (betDollars) => {\n const betValueCentsFloat = betDollars\n ? parseFloat(betDollars)\n : parseFloat(betValueCents);\n const mainPotCentsFloat = parseFloat(mainPotCents);\n const betsOnTableSumCentsFloat = parseFloat(betsOnTableSumCents);\n if (mainPotCents + betsOnTableSumCents + callAmountCents === 0) {\n return \"\";\n }\n const percentPot = Math.round(\n ((betValueCentsFloat - callAmountCents - ownBetOnTableCents) /\n (mainPotCentsFloat + betsOnTableSumCentsFloat + callAmountCents)) *\n 100\n );\n return !isNaN(percentPot) ? percentPot : \"\";\n };\n\n const getBetValue = () => {\n const betValueDollarsFloat = parseFloat(betValueCents) / 100;\n if (isNaN(betValueDollarsFloat) || betValueDollarsFloat === null) {\n return \"\";\n }\n if (!isInCents) {\n return Math.round(betValueCents, 0);\n }\n return betFocus\n ? parseFloat(betValueCents) / 100\n : betValueDollarsFloat.toFixed(2);\n };\n\n const betValueFromPercentPot = (val) => {\n const newValue = parseFloat(val);\n const newBet =\n (newValue / 100) *\n parseFloat(mainPotCents + betsOnTableSumCents + callAmountCents) +\n callAmountCents +\n ownBetOnTableCents;\n return newBet;\n };\n\n const setBetFromPercentPot = (val) => {\n const newBet = betValueFromPercentPot(val);\n if (newBet > maxBetSliderCents) {\n setBetValueCents(maxBetSliderCents);\n } else {\n setBetValueCents(newBet);\n }\n };\n\n let raiseButtons;\n\n if (isPreFlop && minBetSliderCents / bigBlindCents < 5) {\n raiseButtons = [3, 4, 5].map((bigBlindCount) => {\n return (\n setBetValueCents(bigBlindCount * bigBlindCents)}\n disable={bigBlindCount * bigBlindCents < minBetSliderCents}\n >\n {`${bigBlindCount} BB`}\n \n );\n });\n } else {\n raiseButtons = [\n { view: \"+ 1/2 pot\", percent: 50 },\n { view: \"+ 3/4 pot\", percent: 75 },\n { view: \"+ pot\", percent: 100 },\n ].map((potObj) => {\n return (\n setBetFromPercentPot(potObj.percent)}\n disable={betValueFromPercentPot(potObj.percent) < minBetSliderCents}\n >\n {potObj.view}\n \n );\n });\n }\n\n return (\n <>\n \n \n \n \n setBetFromPercentPot(e.target.value)}\n value={getPercentPot()}\n placeholder={getPercentPot(minBetCents)}\n onFocus={() => setPotFocus(true)}\n onBlur={() => setPotFocus(false)}\n />\n \n % pot\n {callAmountCents !== 0 && (\n setCurrentModal(modals.RAISE_INFO)}\n />\n )}\n \n \n {raiseButtons}\n setBetValueCents(maxBetSliderCents)}\n >\n ALL-IN\n \n \n \n \n setBetValueCents(\n isInCents\n ? parseFloat(e.target.value).toFixed(2) * 100\n : e.target.value\n )\n }\n value={getBetValue()}\n onKeyPress={enterPressedForBet}\n ref={betInputElement}\n placeholder={formatCurrency(minBetCents, isInCents)}\n onFocus={() => setBetFocus(true)}\n onBlur={() => setBetFocus(false)}\n />\n betInputElement.current.focus()}\n style={{\n position: \"absolute\",\n left: \"10px\",\n top: \"0px\",\n transform: \"translate(0,-50%)\",\n }}\n />\n setBetValueCents(parseInt(e.target.value))}\n value={betValueCents ? betValueCents : minBetSliderCents}\n smallBlindCents={smallBlindCents}\n min={minBetSliderCents}\n max={maxBetSliderCents}\n style={isMobile && { marginBottom: 0, marginRight: \"15px\" }}\n setValue={setBetValueCents}\n betFocus={betFocus}\n potFocus={potFocus}\n />\n \n \n \n \n {betOnTableCents ? `Raise to` : `Bet`}\n \n setShowRaiseView(false)}\n >\n Back\n \n \n \n >\n );\n};\n\nconst FullViewWrapper = styled.div`\n display: flex;\n justify-content: space-between;\n`;\n\nconst RaiseOptionsWrapper = styled.div``;\n\nconst RightWrapper = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n margin-left: 5px;\n`;\n\nconst TopRaiseViewOptions = styled.div`\n display: flex;\n justify-content: space-between;\n margin-bottom: 10px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 40px;\n }\n`;\n\nconst BottomRaiseViewOptions = styled.div`\n position: relative;\n background: ${({ theme }) => theme.foreground};\n border-radius: 5px;\n display: flex;\n align-items: center;\n padding: 10px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 40px;\n }\n`;\n\nconst BetTextField = styled(InputText)`\n position: relative;\n font-weight: bold;\n text-align: center;\n width: 100px;\n height: 60px;\n box-sizing: border-box;\n border-radius: 5px;\n margin-right: 0;\n\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 25px;\n }\n`;\n\nconst FlatBetTextField = styled(BetTextField)`\n height: 30px;\n`;\n\nconst InfoQuestionMark = styled(Icon)`\n margin-left: 3px;\n color: ${colors.grey};\n size: sm;\n &:hover {\n color: ${({ theme }) => theme.primary};\n }\n`;\n\nexport default RaiseView;\n","import React from \"react\";\nimport { SmallHeaderText } from \"../../../../shared/Text\";\nimport { PrimaryButton } from \"../../../../shared/FormElements\";\nimport { colors } from \"../../../../helpers/styles\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst UnnecessaryFoldView = ({\n isMobile,\n onFoldClick,\n setShowUnnecessaryFoldView,\n}) => {\n return (\n <>\n \n {!isMobile\n ? `You're about to make an unnecessary fold. Are you sure?`\n : `Are you sure?`}\n \n \n FOLD\n \n setShowUnnecessaryFoldView(false)}\n >\n Back\n \n >\n );\n};\n\nconst InstructionsText = styled(SmallHeaderText)`\n position: absolute;\n top: -20px;\n text-align: right;\n color: ${colors.grey};\n padding-right: 10px;\n transform: translateY(-100%);\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n color: ${colors.darkGrey};\n top: -10px;\n }\n`;\n\nexport default UnnecessaryFoldView;\n","import styled from \"styled-components\";\n\nexport const ActionButtonWrapper = styled.div`\n max-width: 800px;\n width: 100%;\n display: flex;\n flex-direction: ${(props) =>\n props.betButtonsOnRight ? `row` : `row-reverse`};\n justify-content: space-evenly;\n align-items: center;\n margin: auto;\n`;\n","import React, { Fragment, useState, useEffect } from \"react\";\nimport { PrimaryButton } from \"../../../../shared/FormElements\";\nimport { formatCurrency } from \"../../../../helpers/utils\";\nimport RaiseView from \"./RaiseView\";\nimport UnnecessaryFoldView from \"./UnnecessaryFoldView\";\nimport { ActionButtonWrapper } from \"../shared\";\nimport { actions } from \"../../../../helpers/constants\";\n\nconst MainPlayActions = ({\n isMobile,\n smallBlindCents,\n betOnTableCents,\n ownBetOnTableCents,\n ownStackCents,\n minRaiseByCents,\n emitSocketAction,\n setShowChat,\n faceUpHoleCards,\n activePlayers,\n username,\n betsOnTableSumCents,\n mainPotCents,\n canUserCheck,\n callAmount,\n betButtonsOnRight,\n setCurrentModal,\n isInCents,\n}) => {\n const [betValueCents, setBetValueCents] = useState(null);\n const [actionDone, setActionDone] = useState(false);\n const [showRaiseView, _setShowRaiseView] = useState(false);\n const [showUnnecessaryFoldView, setShowUnnecessaryFoldView] = useState(false);\n\n const maxBetSliderCents = betOnTableCents\n ? ownBetOnTableCents + ownStackCents\n : ownStackCents;\n const minBetSliderCents = betOnTableCents\n ? betOnTableCents + minRaiseByCents\n : 2 * smallBlindCents;\n\n const minBetCents = betOnTableCents\n ? Math.min(\n ownStackCents + ownBetOnTableCents,\n betOnTableCents + minRaiseByCents\n )\n : Math.min(ownStackCents, betOnTableCents + minRaiseByCents);\n\n /**\n * If other players all have zero balance, don't show ability to re-raise\n */\n const otherPlayersHaveZeroBalance = () => {\n let allZeroBalance = true;\n activePlayers\n .filter((player) => player.isAtTable && player.playerName !== username)\n .forEach((player) => {\n if (player.playerBalance !== 0) {\n allZeroBalance = false;\n }\n });\n return allZeroBalance;\n };\n\n const canUserBet =\n betOnTableCents < ownStackCents + ownBetOnTableCents &&\n !otherPlayersHaveZeroBalance();\n\n const canUserRaiseAllIn = minBetSliderCents >= maxBetSliderCents;\n\n useEffect(() => {\n // reset action done when cards are dealt\n setActionDone(false);\n }, [faceUpHoleCards]);\n\n const onCallClick = (e) => {\n emitSocketAction(actions.CALL, -1);\n setActionDone(true);\n };\n\n const onBetClick = () => {\n const bet = betValueCents || minBetCents;\n\n const isBetInRange = bet <= maxBetSliderCents && bet >= minBetSliderCents;\n\n const isRaiseAllIn = minBetSliderCents > maxBetSliderCents;\n\n if (isBetInRange || isRaiseAllIn) {\n const cents = parseInt(bet);\n emitSocketAction(actions.RAISE, cents);\n setActionDone(true);\n setShowRaiseView(false);\n }\n };\n\n const setShowRaiseView = (show) => {\n _setShowRaiseView(show);\n setShowChat(!show);\n };\n\n const onFoldClick = (e) => {\n emitSocketAction(actions.FOLD, -1);\n setActionDone(true);\n };\n\n if (actionDone) {\n return ;\n }\n\n if (showRaiseView) {\n return (\n \n \n \n );\n }\n\n if (showUnnecessaryFoldView) {\n return (\n \n \n \n );\n }\n\n return (\n \n {canUserBet && (\n {\n if (canUserRaiseAllIn) {\n // Raise all-in\n onBetClick();\n } else {\n setShowRaiseView(true);\n }\n }}\n >\n {canUserRaiseAllIn\n ? \"Raise all-in\"\n : betOnTableCents\n ? `Raise to`\n : `Bet`}\n \n )}\n\n \n {canUserCheck\n ? `Check`\n : `Call ${formatCurrency(callAmount, isInCents)}`}\n \n\n {\n if (canUserCheck) {\n setShowUnnecessaryFoldView(true);\n } else {\n onFoldClick(e);\n }\n }}\n secondaryColor\n keyboardChar=\"F\"\n dataTestId=\"fold-button\"\n >\n Fold\n \n \n );\n};\n\nexport default MainPlayActions;\n","export const backendUrl = \"127.0.0.1:5000\"; // not used yet\n\nexport const buyInConfig = {\n UPPER_BB: 300,\n DEFAULT_BB: 100,\n LOWER_BB: 50,\n};\n","import React, { useState, Fragment } from \"react\";\nimport { PrimaryButton, InputText } from \"../../../shared/FormElements\";\nimport { SmallHeaderText } from \"../../../shared/Text\";\nimport { colors } from \"../../../helpers/styles\";\nimport { buyInConfig } from \"../../../helpers/config\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../../helpers/constants\";\nimport styled from \"styled-components\";\nimport { formatCurrency } from \"../../../helpers/utils\";\n\nconst BuyInActions = ({ smallBlindCents, emitSocketMessage, isInCents }) => {\n const [buyInValueDollars, setBuyInValueDollars] = useState(\"\");\n const [actionDone, setActionDone] = useState(false);\n const [showInvalidBuyIn, setShowInvalidBuyIn] = useState(false);\n\n const onEnterPressedForBuyIn = (event) => {\n if (!isInCents && !/[0-9]/.test(event.key)) {\n event.preventDefault();\n }\n var code = event.keyCode || event.which;\n if (code === 13) {\n onBuyInClick();\n }\n };\n\n const onBuyInChange = (e) => {\n setBuyInValueDollars(e.target.value);\n };\n\n const sendSocketBuyIn = (buyInCents) => {\n emitSocketMessage(\"request_buy_in\", { amount: parseInt(buyInCents) });\n setBuyInValueDollars(\"\");\n setActionDone(true);\n };\n\n const onBuyInClick = (e) => {\n let buyInCents = parseFloat(parseFloat(buyInValueDollars).toFixed(2));\n if (isInCents) {\n buyInCents *= 100;\n }\n if (isNaN(buyInCents)) {\n buyInCents = 0;\n }\n\n const bigBlindCents = smallBlindCents * 2;\n if (parseInt(buyInCents) === 0) {\n buyInCents = bigBlindCents * buyInConfig.DEFAULT_BB;\n }\n if (\n (buyInCents >= _getLowerBound() && buyInCents <= _getUpperBound()) ||\n showInvalidBuyIn\n ) {\n sendSocketBuyIn(buyInCents);\n } else {\n setShowInvalidBuyIn(true);\n }\n };\n\n const _getUpperBound = (inDollars) => {\n const bigBlindCents = smallBlindCents * 2;\n return bigBlindCents * buyInConfig.UPPER_BB;\n };\n\n const _getLowerBound = (inDollars) => {\n const bigBlindCents = smallBlindCents * 2;\n return bigBlindCents * buyInConfig.LOWER_BB;\n };\n\n if (actionDone) {\n return ;\n }\n return (\n \n \n {showInvalidBuyIn ? (\n `Typical buy in range is between (${formatCurrency(\n _getLowerBound(isInCents),\n isInCents\n )}, ${formatCurrency(\n _getUpperBound(isInCents),\n isInCents\n )}). Are you sure?`\n ) : (\n <>\n Tap "B" on your keyboard \n \n to buy in \n >\n )}\n \n \n \n Buy In\n \n {showInvalidBuyIn && (\n setShowInvalidBuyIn(false)}\n >\n Back\n \n )}\n \n );\n};\n\nconst BuyInWrapper = styled.div`\n display: flex;\n`;\n\nconst BuyInTextField = styled(InputText)`\n width: 100px;\n height: 60px;\n font-weight: bold;\n text-align: center;\n margin-right: 5px;\n\n &::placeholder {\n color: ${colors.grey};\n }\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n height: 40px;\n }\n`;\n\nconst InstructionsText = styled(SmallHeaderText)`\n position: absolute;\n top: -100%;\n text-align: right;\n color: ${colors.grey};\n padding-right: 10px;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n display: none;\n }\n`;\n\nexport default BuyInActions;\n","import React from \"react\";\nimport { PrimaryButton } from \"../../../../shared/FormElements\";\nimport { withTheme } from \"styled-components\";\nimport generateQueuedActions from \"./options\";\nimport { ActionButtonWrapper } from \"../shared\";\n\nconst PreTurnActions = ({\n updateQueuedAction,\n selectedAction,\n canUserCheck,\n callAmount,\n betButtonsOnRight,\n theme,\n isInCents,\n}) => {\n const queuedActions = generateQueuedActions(\n canUserCheck,\n callAmount,\n isInCents\n );\n\n return (\n \n {queuedActions.map((queuedAction) => {\n const selected =\n selectedAction &&\n selectedAction.actionType === queuedAction.actionType &&\n selectedAction.betValue === queuedAction.betValue;\n\n return (\n \n selected\n ? updateQueuedAction(null)\n : updateQueuedAction({\n actionType: queuedAction.actionType,\n betValue: queuedAction.betValue,\n })\n }\n selected={selected}\n >\n {queuedAction.displayName}\n \n );\n })}\n \n );\n};\n\nexport default withTheme(PreTurnActions);\n","import { formatCurrency } from \"../../../../helpers/utils\";\nimport { actions } from \"../../../../helpers/constants\";\n\nexport default function generateQueuedActions(\n canUserCheck,\n callAmount,\n isInCents\n) {\n let queuedActions = [\n {\n actionType: actions.CALL,\n betValue: callAmount,\n keyboardChar: \"C\",\n displayName: `Call ${formatCurrency(callAmount, isInCents)}`,\n hideIfUserCanCheck: true,\n },\n {\n actionType: actions.CHECKFOLD,\n betValue: -1,\n keyboardChar: \"X\",\n displayName: \"Check/Fold\",\n showIfUserCanCheck: true,\n },\n {\n actionType: actions.CHECK,\n betValue: -1,\n keyboardChar: \"C\",\n displayName: \"Check\",\n showIfUserCanCheck: true,\n },\n {\n actionType: actions.FOLD,\n betValue: -1,\n keyboardChar: \"F\",\n displayName: \"Fold\",\n },\n ];\n\n return queuedActions.filter((queuedAction) => {\n if (canUserCheck) return !queuedAction.hideIfUserCanCheck;\n else return !queuedAction.showIfUserCanCheck;\n });\n}\n","import React, { useState, Fragment } from \"react\";\nimport { colors } from \"../../../helpers/styles\";\nimport { PrimaryButton } from \"../../../shared/FormElements\";\nimport { LoadingWrapper, BoldText } from \"../../../shared/Text\";\nimport { roles } from \"../../../helpers/constants\";\nimport styled from \"styled-components\";\n\nconst StartGameAction = ({\n emitSocketMessage,\n adminName,\n clientRole,\n restart = false,\n}) => {\n const [actionDone, setActionDone] = useState(false);\n\n const sendSocketStartGame = () => {\n emitSocketMessage(\"start_game\", {});\n setActionDone(true);\n };\n\n const onStartGameClick = (e) => {\n sendSocketStartGame();\n };\n\n let startButton;\n\n if (actionDone) {\n startButton = ;\n } else {\n startButton = (\n \n {restart ? \"Next hand\" : \"Start Game!\"}\n \n );\n }\n return (\n \n {clientRole === roles.ROLE_ADMIN && startButton}\n {clientRole !== roles.ROLE_ADMIN && (\n \n Waiting for {adminName} to{\" \"}\n {restart ? \"go to next hand\" : \"start game\"}\n \n )}\n \n );\n};\n\nconst StartGameWrapper = styled.div`\n text-align: right;\n`;\n\nexport default StartGameAction;\n","import React, { useState, useEffect } from \"react\";\nimport { font, colors } from \"../../../helpers/styles\";\nimport { PrimaryButton } from \"../../../shared/FormElements\";\nimport styled from \"styled-components\";\n\nconst ShowHandAction = ({\n emitSocketMessage,\n clientPlayer,\n numWinners,\n stillInHand,\n showingHand,\n isSpectating,\n handTimerLengthSeconds,\n}) => {\n const [actionDone, setActionDone] = useState(false);\n const [hideContent, setHideContent] = useState(false);\n const [seconds, setSeconds] = useState(handTimerLengthSeconds);\n const [endsAt, dummyEndsAt] = useState(\n Date.now() + 1000 * handTimerLengthSeconds\n );\n\n const sendSocketShowHand = () => {\n emitSocketMessage(\"player_fields\", {\n fields: { cards: clientPlayer.cards },\n });\n setActionDone(true);\n };\n\n const onShowHandClick = (e) => {\n sendSocketShowHand();\n };\n\n useEffect(() => {\n if (seconds > 0) {\n let remainingTime = Math.floor((endsAt - Date.now()) / 1000);\n const timer = setTimeout(() => setSeconds(remainingTime), 1000);\n return () => clearTimeout(timer);\n } else {\n setSeconds(null);\n setHideContent(true);\n }\n }, [seconds]);\n\n if (hideContent) {\n return <>>;\n }\n\n let showHandButton;\n let newHandText = (\n \n {seconds} seconds until new hand\n \n );\n if (isSpectating) {\n return {newHandText} ;\n }\n\n let showHandText = <>>;\n\n if (clientPlayer.showingHand || actionDone) {\n showHandText = (\n Your cards are showing \n );\n }\n if (!actionDone && !showingHand && numWinners > 0 && clientPlayer.cards) {\n showHandButton = (\n \n Show Cards\n \n );\n } else {\n showHandButton = <>>;\n }\n\n return (\n <>\n {showHandButton}\n \n {showHandText}\n {newHandText}\n \n >\n );\n};\n\nconst RightAlign = styled.div`\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n`;\n\nconst BoldText = styled.div`\n display: flex;\n margin-left: 5px;\n margin-right: 3px;\n color: ${({ theme }) => theme.primary};\n`;\n\nconst ShowHandTextWrapper = styled.div`\n display: flex;\n flex-direction: row;\n font-family: ${font.header};\n font-size: 22px;\n margin-right: 24px;\n margin-bottom: 10px;\n color: ${colors.darkGrey2};\n letter-spacing: 0.3px;\n font-weight: bold;\n`;\n\nconst ShowHandTextWrapper2 = styled(ShowHandTextWrapper)`\n color: ${({ theme }) => theme.tertiary};\n`;\n\nexport default ShowHandAction;\n","import React, { useContext, useState } from \"react\";\nimport styled from \"styled-components\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n roles,\n modals,\n} from \"../../../helpers/constants\";\nimport { GameContext } from \"../../../context/GameContext\";\nimport { BrowserContext } from \"../../../context/BrowserContext\";\nimport * as Actions from \"./actions\";\nimport { LoadingWrapper, BoldText } from \"../../../shared/Text\";\n\nconst ActionButtons = ({ emitSocketMessage, emitSocketAction, numWinners }) => {\n const {\n smallBlindCents,\n maxPotCents,\n stillInHand,\n stillInGame,\n ownPotCents,\n showBuyIn,\n minRaise,\n ownStackCents,\n faceUpHoleCards,\n activePlayers,\n username,\n mainPotCents,\n betsOnTableSumCents,\n updateQueuedAction,\n queuedAction,\n willExecuteQueuedAction,\n ongoing,\n isSpectating,\n isGod,\n isTournament,\n handTimerLengthSeconds,\n paused,\n isInCents,\n } = useContext(GameContext);\n\n const {\n isMobile,\n setShowChat,\n betButtonsOnRight,\n setCurrentModal,\n } = useContext(BrowserContext);\n\n const clientPlayer = activePlayers.filter(\n (player) => player.playerName === username\n )[0];\n const adminPlayer = activePlayers.filter(\n (player) => player.playerRole === roles.ROLE_ADMIN\n )[0];\n const numPlayersInGame = activePlayers.filter(\n (player) => player.playerIsInGame\n ).length;\n const numValidPlayers = activePlayers.filter(\n (player) => player.playerBalance > 0\n ).length;\n const numRequestedBuyIns = activePlayers.filter(\n (player) => player.requestedBuyIn > 0\n ).length;\n const numPlayersSittingIn = activePlayers.filter(\n (player) => player.queuedAction !== \"ACTION_STAND\"\n ).length;\n const numPlayersConnected = activePlayers.filter((player) => player.connected)\n .length;\n const isMyTurn = clientPlayer ? clientPlayer.playerIsTurn : false;\n const canUserCheck = !(maxPotCents && ownPotCents < maxPotCents);\n const callAmount = maxPotCents - ownPotCents;\n const [patreonShown, setPatreonShown] = useState(false);\n\n let actionButtons;\n\n if (isSpectating) {\n if (numWinners > 0) {\n actionButtons = (\n \n );\n } else {\n if (isGod) {\n actionButtons = (\n You are currently spectating as god \n );\n } else {\n actionButtons = (\n You are currently spectating \n );\n }\n }\n } else if (showBuyIn && !isTournament) {\n actionButtons = (\n \n );\n } else if (\n stillInHand &&\n isMyTurn &&\n !willExecuteQueuedAction &&\n numWinners === 0\n ) {\n actionButtons = (\n \n );\n } else if (stillInHand && !isMyTurn && numWinners === 0) {\n actionButtons = (\n \n );\n } else if (clientPlayer && !stillInGame && ownStackCents > 0) {\n actionButtons = (\n You are currently sitting out \n );\n } else if (ongoing && numWinners > 0 && stillInGame) {\n if (\n clientPlayer.isWinner &&\n clientPlayer.playerBalance > 0 &&\n // on 5th win and then every 10th win\n (clientPlayer.handsWon === 5 || clientPlayer.handsWon % 10 === 0)\n ) {\n setCurrentModal(modals.PATREON);\n }\n\n actionButtons = (\n \n );\n } else if (\n !ongoing &&\n numPlayersSittingIn >= 2 &&\n numPlayersInGame >= 2 &&\n numValidPlayers >= 2 &&\n numPlayersConnected >= 2 &&\n adminPlayer\n ) {\n actionButtons = (\n \n );\n } else if (!ongoing && clientPlayer) {\n if (clientPlayer.playerRole === roles.ROLE_ADMIN) {\n if (numRequestedBuyIns > 0) {\n actionButtons = (\n Waiting for you to approve buy ins \n );\n } else if (numValidPlayers < 2) {\n actionButtons = (\n Waiting for others to buy in \n );\n }\n } else {\n if (clientPlayer.requestedBuyIn > 0 && adminPlayer) {\n actionButtons = (\n \n Waiting for {adminPlayer.playerName} to approve\n buy in\n \n );\n }\n }\n } else if (!stillInHand) {\n actionButtons = null;\n if (clientPlayer && clientPlayer.requestedBuyIn > 0 && adminPlayer) {\n actionButtons = (\n \n Waiting for {adminPlayer.playerName} to approve\n buy in\n \n );\n }\n }\n\n return (\n <>\n \n {actionButtons}\n \n >\n );\n};\n\nconst ActionWrapper = styled.div`\n position: absolute;\n z-index: 2;\n ${(props) => (props.betButtonsOnRight ? `right: 20px` : `left: 20px`)}\n bottom: 20px;\n display: flex;\n flex-direction: column;\n align-items: right;\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n right: 5px;\n bottom: 15px;\n position: fixed;\n }\n`;\n\nexport default ActionButtons;\n","import React, { useContext } from \"react\";\nimport Table from \"./Table\";\nimport PlayersAndPots from \"./PlayersAndPots\";\nimport ActionButtons from \"./ActionButtons\";\nimport styled from \"styled-components\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n} from \"../../helpers/constants\";\nimport { BrowserContext } from \"../../context/BrowserContext\";\nimport { removeScrollbars } from \"../../shared/Overlay\";\n\nconst GameWindow = ({\n emitSocketAction,\n emitSocketMessage,\n activePlayers,\n iframeClientName,\n}) => {\n const { showVideoChat, isMobile } = useContext(BrowserContext);\n const playersAreInHand =\n activePlayers.filter((player) => player.playerIsInHand).length > 0;\n const winningPlayers = activePlayers.filter((player) => player.isWinner);\n\n return (\n \n \n \n \n \n \n \n );\n};\n\nconst GameWrapper = styled.div`\n height: 100%;\n width: ${(props) => (props.showVideoChat ? `85` : `100`)}%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n position: fixed;\n overflow: scroll; // overriden by removeScrollbars\n ${removeScrollbars}\n`;\n\nconst TableWrapper = styled.div`\n height: 50vh;\n width: 75vw;\n margin-top: -10vh;\n position: relative;\n text-align: center;\n display: block;\n z-index: -2;\n transform: translateY(30px);\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n width: 90vw;\n height: 55vh;\n }\n`;\n\nexport default GameWindow;\n","import React, { Component, Fragment } from \"react\";\nimport \"react-toastify/dist/ReactToastify.css\";\nimport queryString from \"query-string\";\n\nimport styled, { ThemeProvider } from \"styled-components\";\nimport Chat from \"./Chat\";\nimport * as Modal from \"./Modals\";\nimport VideoChat from \"./VideoChat\";\nimport FixedWidgets from \"./FixedWidgets\";\nimport MTTLobbyWidgets from \"./MTTLobbyWidgets\";\nimport MTTLobby from \"./MTTLobby\";\nimport Global from \"./Global\";\nimport \"bootstrap/dist/css/bootstrap.min.css\";\nimport \"typeface-rambla\";\nimport { hot, setConfig } from \"react-hot-loader\";\nimport { modals } from \"../helpers/constants\";\nimport {\n mobileBreakpoint,\n mobileHeightBreakpoint,\n mobileBreakpointRaw,\n mobileHeightBreakpointRaw,\n themes,\n blinds,\n timer,\n} from \"../helpers/constants\";\nimport themesDict, { defaultTheme } from \"../helpers/theme\";\nimport {\n initSocket,\n forceDisconnect,\n emit,\n socketOff,\n setOnConnect,\n emitEnsureInRoom,\n setOnDisconnect,\n setOnStateChange,\n setOnMTTStateChange,\n setOnLeaderboard,\n setOnSound,\n setBootInactive,\n setOnDistributeChat,\n setOnDifferentLogIn,\n setOnSettingsChange,\n socketIsConnected,\n setOnPlayerStateChange,\n setOnSpectatorStateChange,\n setOnPlayerFieldsChange,\n connectEmitSocketMessage,\n connectEmitSocketAction,\n setMTTGameEnd,\n setMTTPlayers,\n setOnMTTUsersState,\n} from \"../helpers/socket\";\nimport api, { checkAlive } from \"../helpers/api\";\nimport { refreshPage } from \"../helpers/utils\";\nimport {\n initLogger,\n logJoinGameEvent,\n logCreateGameEvent,\n setLoggerIdentity,\n logError,\n} from \"../helpers/logger\";\n\nimport GameWindow from \"./GameWindow\";\nimport { getMTTPlayers, setMTTUsersState } from \"../actions/game\";\n\nclass App extends Component {\n constructor(props) {\n super(props);\n this.state = {\n // this is actually a prop, so can't be in context\n // to avoid prop drilling, use withRouter() on relevant components\n gameId: \"\", // stores url parameter\n MTTId: \"\",\n };\n\n // meant to be called from settings or newgame (not join!)\n // to remove overlay, and only the newgame that involves making a new game\n this._cancelPopup = () => {\n if (this.state.gameId.length > 0) {\n this.props.context.updateDisplay(\"play\");\n }\n this.props.browserContext.setCurrentModal(null);\n // TODO: call endpoint that caches to backend\n };\n this._openNewGameDisplay = () => {\n // this.props.context.updateDisplay(\"newGame\");\n return checkAlive(\n (resolve) => {\n this.props.context.updateDisplay(\"newGame\");\n return true;\n },\n (reject) => {\n this.props.browserContext.setCurrentModal(\n modals.DISCONNECTED_SERVER_NOT_IN_GAME\n );\n return false;\n }\n );\n };\n\n this._openNewMTTDisplay = () => {\n return checkAlive(\n (resolve) => {\n this.props.context.updateDisplay(\"newMTT\");\n return true;\n },\n (reject) => {\n this.props.browserContext.setCurrentModal(\n modals.DISCONNECTED_SERVER_NOT_IN_GAME\n );\n return false;\n }\n );\n };\n\n this._getOverallStats = async () => {\n try {\n const { data } = await api.get(\"/get_overall_stats\", {\n params: {},\n });\n this.setState({\n numGames: data.numGames.toLocaleString(),\n numPlayers: data.numPlayers.toLocaleString(),\n });\n } catch (error) {\n logError(error);\n console.error(error);\n }\n };\n\n this._openSettingsDisplay = () => {\n this.props.context.updateDisplay(\"settings\");\n };\n // this.backend = '172.17.170.145:5000' // use a global url to avoid changing later\n\n // player i uses cards i and i + 1, activePlayers in state\n // is stored as dictionary with 0:{...info}\n // example is {0:{playerPot: 3, playerName: \"Bob\", playerBalance: 14},\n // 1:{playerPot: 3, playerName: \"Joe\", playerBalance: 13}}\n // this is only for the display in Players and Cards.js, will eventually migrate to\n // a dictionary with players with a position value for where on the table they are\n // instead of the key\n\n this._joinGame = async (\n username,\n userPassword,\n gamePassword,\n isGod = false\n ) => {\n const response = await this.props.context.joinGame(\n this.state.gameId,\n username,\n userPassword,\n gamePassword,\n isGod\n );\n if (response.error) {\n return {\n error: response.error,\n type: response?.error?.response?.data?.type,\n field: response?.error?.response?.data?.field,\n };\n } else {\n const newState = await this.props.context.nextState();\n\n initSocket(this.state.gameId, this.state.MTTId, () => {\n emit(\"join\", {\n username: newState.username,\n game_id: this.state.gameId,\n key: newState.sessionKey,\n }); // join room on socket\n\n if (newState.isTournament && newState.totalBuyinCents === 0) {\n //and if new user (not re-join)\n emit(\"request_buy_in\", {\n username: newState.username,\n game_id: this.state.gameId,\n key: newState.sessionKey,\n amount: newState.autoBuyinCents,\n autobuy: true,\n });\n }\n\n this.props.browserContext.setAllowKeyboardShortcuts(true);\n this.props.browserContext.setExistsModal(false);\n\n setLoggerIdentity(\n newState.username,\n this.state.gameId,\n this.props.iframeClientName\n );\n\n logJoinGameEvent();\n\n this._initSocketListeners();\n });\n return {};\n }\n };\n\n this._spectateGame = async (\n username,\n userPassword,\n gamePassword,\n isGod = false\n ) => {\n const response = await this.props.context.spectateGame(\n this.state.gameId,\n username,\n userPassword,\n gamePassword,\n isGod\n );\n if (response.error) {\n return {\n error: response.error,\n type: response?.error?.response?.data?.type,\n field: response?.error?.response?.data?.field,\n };\n } else {\n const newState = await this.props.context.nextState();\n initSocket(this.state.gameId, this.state.MTTId, () => {\n emit(\"spectate\", {\n username: newState.username,\n game_id: this.state.gameId,\n key: newState.sessionKey,\n }); // join room on socket\n\n this.props.browserContext.setAllowKeyboardShortcuts(true);\n this.props.browserContext.setExistsModal(false);\n\n setLoggerIdentity(\n newState.username,\n this.state.gameId,\n this.props.iframeClientName\n );\n\n this._initSocketListenersSpectators();\n });\n return {};\n }\n };\n\n this._joinMTT = async (username, userPassword, MTTPassword) => {\n const response = await this.props.context.joinMTT(\n this.state.MTTId,\n username,\n userPassword,\n MTTPassword\n );\n if (response.error) {\n return {\n error: response.error,\n type: response?.error?.response?.data?.type,\n field: response?.error?.response?.data?.field,\n };\n } else {\n const newState = await this.props.context.nextState();\n\n initSocket(this.state.gameId, this.state.MTTId, () => {\n emit(\"join_mtt\", {\n username: newState.username,\n mtt_id: this.state.MTTId,\n user_password: newState.userPassword,\n key: newState.sessionKey,\n }); // join room on socket\n\n this._initSocketListeners();\n });\n return {};\n }\n };\n\n this._initSocketListeners = () => {\n socketOff();\n this._setChatRecieveSocket();\n this._setGameStateSocket();\n this._setUpdateLeaderboardSocket();\n this._setSoundsSocket();\n this._setBootInactiveSocket();\n this._setDisconectSocket();\n this._setDifferentLogInSocket();\n this._setReconnectSocket();\n this._setSettingsSocket();\n this._setPlayerStateChangeSocket();\n this._setSpectatorStateChangeSocket();\n this._setOnPlayerFieldsChangeSocket();\n this._setMTTGameEnd();\n this._setMTTPlayers();\n this._setOnMTTUsersStateSocket();\n this._setMTTStateSocket();\n };\n\n this._initSocketListenersSpectators = () => {\n socketOff();\n this._setChatRecieveSocket();\n this._setGameStateSocket();\n this._setUpdateLeaderboardSocket();\n this._setDisconectSocket();\n this._setDifferentLogInSocket();\n this._setReconnectSocket();\n this._setSettingsSocket();\n this._setPlayerStateChangeSocket();\n this._setSpectatorStateChangeSocket();\n this._setOnPlayerFieldsChangeSocket();\n this._setMTTGameEnd();\n this._setMTTPlayers();\n this._setOnMTTUsersStateSocket();\n this._setMTTStateSocket();\n };\n\n this._setBootInactiveSocket = () => {\n if (this.props.context.username) {\n // if (this.props.context.username && this.state.gameId) {\n setBootInactive(\n this.props.context.username,\n this.state.gameId,\n (disconnectedUser) => {\n this.props.context.setUserDisconnected(disconnectedUser);\n }\n );\n }\n };\n\n this._setChatRecieveSocket = () => {\n if (this.props.context.username) {\n //if (this.props.context.username && this.state.gameId) {\n setOnDistributeChat((chat_obj) => {\n let timeout = 0;\n if (\"timeout\" in chat_obj) {\n timeout = chat_obj[\"timeout\"];\n }\n setTimeout(() => {\n this.props.context.receiveChatMessage(chat_obj);\n }, timeout * 1000);\n });\n }\n };\n\n this._setDifferentLogInSocket = () => {\n setOnDifferentLogIn(() => {\n // Auto-reconnect code,\n // OLD: prone to looping when server class db fields change and db needs to be migrated locally\n // OLD: Reconnect loop needed or else it reconnects with a socket that can send to the server but not recieve\n\n this.props.context.updateDisplay(\"different_log_in\");\n });\n };\n\n this._setReconnectSocket = () => {\n setOnConnect(() => {\n emitEnsureInRoom(\n this.props.context.username,\n this.state.gameId,\n this.props.context.sessionKey,\n this.props.context.isSpectating,\n () => {\n this._cancelPopup();\n // No identify method for fs, since reidentifying causes a split\n // if (!this.props.context.isSpectating) {\n // this._initSocketListeners();\n // } else {\n // this._initSocketListenersSpectators();\n // }\n }\n );\n });\n };\n\n this._setDisconectSocket = () => {\n setOnDisconnect(() => {\n // Auto-reconnect code\n // OLD: prone to looping when server class db fields change and db needs to be migrated locally\n // OLD: Reconnect loop needed or else it reconnects with a socket that can send to the server but not recieve\n\n // if alive and connected, then don't set to refresh\n // else, set it to refresh\n return checkAlive(\n (resolve) => {\n setTimeout(() => {\n if (!socketIsConnected()) {\n this.props.context.updateDisplay(\"disconnected\");\n }\n }, 1200);\n },\n (reject) => {\n setTimeout(() => {\n if (!socketIsConnected()) {\n this.props.context.updateDisplay(\"disconnected\");\n }\n }, 1200);\n }\n );\n });\n };\n\n this._setGameStateSocket = () => {\n setOnStateChange((newGameState) => {\n let timeout = newGameState[\"timeout\"];\n setTimeout(() => {\n this.props.context.updateGameStateFromSocket(newGameState);\n }, timeout * 1000);\n });\n };\n\n this._setMTTStateSocket = () => {\n setOnMTTStateChange((newMTTState) => {\n let timeout = newMTTState[\"timeout\"];\n console.log(\"mtt end timeout\", timeout);\n setTimeout(() => {\n this.props.context.updateMTTStateFromSocket(newMTTState);\n }, timeout * 1000);\n });\n };\n\n this._setUpdateLeaderboardSocket = () => {\n setOnLeaderboard((leaderboard_resp) => {\n let timeout = 0;\n if (\"timeout\" in leaderboard_resp) {\n timeout = leaderboard_resp[\"timeout\"];\n }\n setTimeout(() => {\n this.props.context.updateLeaderboard(leaderboard_resp);\n }, timeout * 1000);\n });\n };\n\n this._setSoundsSocket = () => {\n setOnSound((data) => {\n this.props.browserContext.playSound(data.action);\n });\n };\n\n this._setMTTGameEnd = () => {\n setMTTGameEnd((data) => {\n setTimeout(() => {\n this.props.context.updateDisplay(\"MTTGameEnd\");\n }, data[\"timeout\"] * 1000);\n });\n };\n\n this._setMTTPlayers = () => {\n setMTTPlayers((data) => {\n this.props.context.updateMTTState(data.gameId);\n if (data[\"reason\"] == \"mtt_distribute\") {\n this.props.context.updateDisplay(\"MTTDistribute\");\n }\n setTimeout(() => {\n emit(\"start_mtt_games_in_round\", {\n mtt_id: this.props.context.MTTId,\n });\n }, 7 * 1000);\n\n // initSocket(data.gameId, this.state.MTTId, () => {\n // this._initSocketListeners();\n // });\n });\n };\n\n this._setOnMTTUsersStateSocket = () => {\n setOnMTTUsersState((data) => {\n this.props.context.setMTTUsersState(data.users, data.reason);\n });\n };\n\n this._createGame = async (\n smallBlindCents,\n gamePassword,\n preferredGameId = \"\",\n iframeClientName = \"game\",\n isTimerEnabled = false,\n timerLengthSeconds = 30,\n handTimerLengthSeconds = timer.MEDIUM_HAND_TIMER_SECONDS,\n isGodModeEnabled = false,\n isTournament = false,\n autoBuyinCents = 1000,\n blindIncreaseSeconds = 600,\n isInCents = true,\n cb = null\n ) => {\n try {\n const gameId = await this.props.context.createGame(\n smallBlindCents,\n gamePassword,\n preferredGameId,\n isTimerEnabled,\n timerLengthSeconds,\n handTimerLengthSeconds,\n isGodModeEnabled,\n isTournament,\n autoBuyinCents,\n Math.max(blindIncreaseSeconds, 60),\n isInCents\n );\n forceDisconnect();\n let pathname = `/${iframeClientName}/${gameId}`;\n this.props.history.push({\n pathname: pathname,\n search: this.props.location.search,\n });\n logCreateGameEvent();\n if (cb) {\n cb();\n }\n } catch (error) {\n logError(error);\n console.error(error);\n }\n };\n\n this._createMTT = async (\n smallBlindCents,\n MTTPassword,\n timerLengthSeconds = 30,\n startingStack = 1000,\n blindIncreaseSeconds = 600,\n isInCents = true,\n cb = null\n ) => {\n try {\n const MTTId = await this.props.context.createMTT(\n smallBlindCents,\n MTTPassword,\n timerLengthSeconds,\n startingStack,\n blindIncreaseSeconds,\n isInCents\n );\n forceDisconnect();\n let pathname = `mtt/${MTTId}`;\n this.props.history.push({\n pathname: pathname,\n search: this.props.location.search,\n });\n if (cb) {\n cb();\n }\n } catch (error) {\n logError(error);\n console.error(error);\n }\n };\n\n this._setSettingsSocket = () => {\n setOnSettingsChange((data) => {\n this.props.context.setGameSettings(\n data.small_blind,\n data.is_timer_enabled,\n data.timer_length_seconds,\n data.hand_timer_length_seconds,\n data.session_reset\n );\n });\n };\n\n this._setOnPlayerFieldsChangeSocket = () => {\n setOnPlayerFieldsChange((data) => {\n this.props.context.setPlayerFields(data.username, data.fields);\n });\n };\n\n this._setPlayerStateChangeSocket = () => {\n setOnPlayerStateChange((data) => {\n const timeout = data[\"timeout\"];\n setTimeout(async () => {\n this.props.context.setPlayerState(data.players, data.reason);\n // only notify once it switches to their turn, not on every state change while it's their turn\n const turnSoundReasons = [\n \"REASON_ACTION\",\n \"REASON_DEAL\",\n \"REASON_FLOP\",\n \"REASON_NEW_HAND\",\n ];\n const clientPlayer = data.players.filter(\n (player) => player.name === this.props.context.username\n )[0];\n const isMyTurn = clientPlayer ? clientPlayer.turn : false;\n\n const willBecomeUserTurn =\n isMyTurn && turnSoundReasons.includes(data.reason);\n\n if (willBecomeUserTurn) {\n // Important: Don't do anything until the player state is updated\n // Queued actions may need to be cancelled based on the previous bet\n await this.props.context.nextState();\n if (this.props.context.willExecuteQueuedAction) {\n this.props.context.executeQueuedAction();\n } else {\n // Play a turn sound if it is the user's turn and they don't have any queued actions\n this.props.browserContext.playSoundDelay(\"DING\", 500);\n }\n }\n }, timeout * 1000);\n });\n };\n\n this._setSpectatorStateChangeSocket = () => {\n setOnSpectatorStateChange((data) => {\n const timeout = data[\"timeout\"];\n setTimeout(async () => {\n this.props.context.setSpectatorState(data.spectators, data.reason);\n }, timeout * 1000);\n });\n };\n\n this._parseQueryParams = () => {\n const params = queryString.parse(this.props.location.search);\n // This only sends user to join/spectate a game if autojoin is true, otherwise some form of NewPlayerModal will be displayed\n if (params.autojoin === \"true\") {\n if (params.username) {\n if (params.spectate === \"true\") {\n //?username=&autojoin=true&spectate=true\n this._spectateGame(\n decodeURI(params.username),\n \"\",\n this.props.context.hasGamePassword\n ? this.props.context.prefilledGamePassword\n : \"\"\n );\n } else {\n //?username=&autojoin=true&spectate=false\n //if game is not full, join game (implement checker in the future)\n this._joinGame(\n decodeURI(params.username),\n \"\",\n this.props.context.hasGamePassword\n ? this.props.context.prefilledGamePassword\n : \"\"\n );\n }\n }\n }\n };\n }\n\n checkMobile = () => {\n const isMobile =\n window.innerWidth <= mobileBreakpointRaw ||\n window.innerHeight <= mobileHeightBreakpointRaw;\n this.props.browserContext.setIsMobile(isMobile);\n };\n\n componentWillUnmount() {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // this function smh\n static getDerivedStateFromProps(props, prevState) {\n let gameId = props.match.params.gameId\n ? props.match.params.gameId\n : props.context.gameId\n ? props.context.gameId\n : \"\";\n let MTTId = props.match.params.MTTId\n ? props.match.params.MTTId\n : props.context.MTTId\n ? props.context.MTTId\n : \"\";\n return {\n ...prevState,\n gameId,\n MTTId,\n };\n }\n\n async componentDidMount() {\n console.log(\"Didmount called\");\n\n // Load the stats for the site intro page\n this._getOverallStats();\n\n document.onreadystatechange = () => {\n console.log(\"Didmount state called\");\n this.props.context.updateDisplay(\"loading\");\n if (document.readyState === \"complete\") {\n if (this.state.MTTId) {\n this.props.context.checkMTTExists(this.state.MTTId);\n } else if (!this.state.gameId) {\n this.props.context.updateDisplay(\"initial\");\n } else {\n this.props.context.checkGameExists(\n this.state.gameId,\n this.props.iframeClientName,\n (exists) => {\n if (!exists) {\n this._createGame(\n blinds.DEFAULT_SMALL_BLIND_CENTS,\n \"\",\n this.state.gameId,\n this.props.iframeClientName,\n false,\n 30,\n 8,\n false,\n false,\n 1000,\n 600,\n true,\n this._parseQueryParams\n );\n console.log(\"Created game\");\n } else if (this.props.context.display === \"login\") {\n this._parseQueryParams();\n }\n }\n );\n }\n }\n };\n\n this.checkMobile();\n window.addEventListener(\"resize\", this.checkMobile);\n initLogger();\n }\n\n render() {\n if (this.props.context.display === \"loading\") return ;\n if (this.props.context.display === \"disconnected\") {\n if (this.state.gameId.length > 0) {\n this.props.browserContext.setCurrentModal(\n modals.DISCONNECTED_SERVER_IN_GAME\n );\n } else {\n this.props.browserContext.setCurrentModal(\n modals.DISCONNECTED_SERVER_NOT_IN_GAME\n );\n }\n }\n if (this.props.context.display === \"different_log_in\") {\n this.props.browserContext.setCurrentModal(modals.DIFFERENT_LOG_IN);\n }\n\n let currentOverlay = <>>; // this is used for modals that are mandatory parts of game flow, ModalController is for interrupts of default game flow\n if (\n this.props.context.display === \"initial\" &&\n !this.state.gameId &&\n !this.state.MTTId\n ) {\n currentOverlay = (\n \n );\n }\n\n if (this.props.context.display === \"login\") {\n const params = queryString.parse(this.props.location.search);\n currentOverlay = (\n \n );\n }\n if (this.props.context.display === \"settings\") {\n currentOverlay = (\n \n );\n }\n if (this.props.context.display === \"newGame\") {\n currentOverlay = (\n \n );\n }\n if (this.props.context.display === \"newMTT\") {\n currentOverlay = (\n \n );\n }\n if (this.props.context.display === \"MTTGameEnd\") {\n currentOverlay = ;\n }\n\n if (this.props.context.display === \"MTTDistribute\") {\n currentOverlay = (\n \n );\n }\n \n if (this.state.MTTId) {\n console.log(\"MTT ID: \", this.state.MTTId);\n }\n \n const emitSocketMessage = connectEmitSocketMessage(\n this.props.context.sessionKey,\n this.state.gameId,\n this.props.context.username\n );\n\n const emitSocketAction = connectEmitSocketAction(\n this.props.context.sessionKey,\n this.state.gameId,\n this.props.context.username\n );\n console.log(\"Display: \", this.props.context.display);\n\n return (\n \n <>\n \n {currentOverlay}\n \n \n \n {!this.state.MTTId ||\n this.props.context.display == \"play\" ||\n this.props.context.display == \"MTTGameEnd\" ? (\n <>\n \n \n {\" \"}\n >\n ) : (\n <>\n \n \n \n >\n )}\n \n {/* Intentionally prop drilling for memo performance optimization. Will do without prop drilling later. */}\n \n \n >\n \n );\n }\n}\n\nconst Wrapper = styled.div`\n height: 100vh;\n background: ${({ theme }) => theme.background};\n\n display: flex;\n ${(props) =>\n props.reverseOrder &&\n `\n flex-direction: row-reverse;\n `}\n @media screen and (max-width: ${mobileBreakpoint}),\n screen and (max-height: ${mobileHeightBreakpoint}) {\n right: 5px;\n bottom: 71px;\n }\n`;\n\nlet CentralLayout = styled.div`\n height: 100%;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n justify-content: space-around;\n align-items: center;\n position: relative;\n ${(props) => (props.fullWidth ? \"width: 100%;\" : \"width: 85%;\")}\n`;\n\nexport const LeaderboardWidthPercent = 15;\n\nsetConfig({ reloadHooks: true });\nexport default hot(module)(App);\n","import App from \"./components/App\";\nimport classnames from \"classnames\";\nimport ReactDOM from \"react-dom\";\nimport React from \"react\";\nimport { BrowserRouter as Router, Route } from \"react-router-dom\";\nimport \"./index.css\";\nimport \"typeface-rambla\";\nimport { GameProvider, GameConsumer } from \"./context/GameContext\";\nimport { BrowserProvider, BrowserConsumer } from \"./context/BrowserContext\";\nimport { initSentry } from \"./helpers/logger\";\ninitSentry();\n\nReactDOM.render(\n \n \n \n \n {(context) => (\n \n {(browserContext) => (\n \n
(\n \n )}\n />\n\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n (\n \n )}\n />\n \n )}\n \n )}\n \n \n \n ,\n document.getElementById(\"root\")\n);\n"],"sourceRoot":""}