lodash#flatMap TypeScript Examples

The following examples show how to use lodash#flatMap. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: score.ts    From fishbowl with MIT License 6 votes vote down vote up
export function teamScore(
  team: Team,
  turns: CurrentGameSubscription["games"][0]["turns"],
  players: CurrentGameSubscription["games"][0]["players"]
) {
  const teamPlayerIds = filter(players, (player) => player.team === team).map(
    (player) => player.id
  )
  const teamTurns = filter(turns, (turn) =>
    teamPlayerIds.includes(turn.player_id)
  )
  return flatMap(teamTurns, (turn) => turn.completed_card_ids).length
}
Example #2
Source File: traceSummary.ts    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
export function traceSummary(spans: any = []) {
  if (spans.length === 0 || !spans[0].timestamp) {
    return null;
  }
  const duration = traceDuration(spans) || 0; // 获取span时间轴长(最大时间-最小时间)
  const endpoints = fp.flow(
    // 筛选出所有的终端(获取binaryAnnotations中的serviceName)
    fp.flatMap(endpointsForSpan),
    fp.uniqWith(endpointEquals),
  )(spans);
  const { traceId, timestamp } = spans[0];

  const spanTimestamps = getSpanTimestamps(spans);

  const errorType = getTraceErrorType(spans);
  return {
    traceId,
    timestamp,
    duration,
    spanTimestamps,
    endpoints,
    errorType,
  };
}
Example #3
Source File: traceSummary.ts    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
function getSpanTimestamps(spans: any) {
  return flatMap(spans, (span) =>
    getServiceNames(span).map((serviceName) => ({
      name: serviceName,
      timestamp: span.timestamp,
      duration: span.duration,
    })),
  );
}
Example #4
Source File: traceSummary.ts    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
// What's the total duration of the spans in this trace?
// 最后结束的(时间戳+耗时最大)- 第一个调用的时间戳
export function traceDuration(spans: any) {
  function makeList({ timestamp, duration }: any) {
    if (!timestamp) {
      return [];
    } else if (!duration) {
      return [timestamp];
    }
    return [timestamp, timestamp + duration];
  }
  // turns (timestamp, timestamp + duration) into an ordered list
  const timestamps = fp.flow(fp.flatMap(makeList), fp.sortBy(identity))(spans);

  if (timestamps.length < 2) {
    return null;
  }
  const firstTime = head(timestamps);
  const lastTime = last(timestamps);
  return lastTime - firstTime;
}
Example #5
Source File: turn.ts    From fishbowl with MIT License 6 votes vote down vote up
export function drawableCards(
  turns: CurrentGameSubscription["games"][0]["turns"],
  cards: CurrentGameSubscription["games"][0]["cards"]
) {
  const allCompletedCardIds = flatMap(turns, (turn) => turn.completed_card_ids)

  const maxCount = max(values(countBy(allCompletedCardIds)))

  let completedCardIdsForRound = filter(
    groupBy(allCompletedCardIds),
    (arr) => arr.length === maxCount
  ).map((arr) => arr[0])

  const remainingIdsForRound = difference(
    cards.map((card) => card.id),
    completedCardIdsForRound
  )

  if (remainingIdsForRound.length === 0) {
    return cards
  } else {
    return filter(cards, (card) => remainingIdsForRound.includes(card.id))
  }
}
Example #6
Source File: quick-fix.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
export function diagnosticToCodeActionFix(
  document: TextDocument,
  diagnostics: Diagnostic[],
  ui5Model: UI5SemanticModel
): CodeAction[] {
  const documentText = document.getText();
  // We prefer to parse the document again to avoid cache state handling
  const { cst, tokenVector } = parse(documentText);
  const xmlDocAst = buildAst(cst as DocumentCstNode, tokenVector);
  const codeActions = flatMap(diagnostics, (diagnostic) => {
    switch (diagnostic.code) {
      case validations.NON_STABLE_ID.code: {
        // non stable id
        return computeCodeActionsForQuickFixStableId({
          document,
          xmlDocument: xmlDocAst,
          nonStableIdDiagnostic: diagnostic,
          ui5Model,
        });
      }
      default:
        return [];
    }
  });

  return codeActions;
}
Example #7
Source File: Template.tsx    From easy-email with MIT License 6 votes vote down vote up
export function Template(props: TemplateProps) {
  let formatChildren = props.children;

  if (Array.isArray(formatChildren)) {
    formatChildren = flatMap(formatChildren);
  }

  return (
    <MjmlBlock
      attributes={omit(props, ['data', 'children', 'value'])}
      type={BasicType.TEMPLATE}
      value={{ idx: props.idx }}
    >
      {formatChildren}
    </MjmlBlock>
  );
}
Example #8
Source File: cardinality-of-aggregation.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
/**
 *  @param aggregationElem The aggregation from which we will get all its subElements at the same depth.
 *
 * For example - Given XMLSnippet aggregationElem param `headerToolbar`.
 * The function will return an array of `Toolbar` and `tnt:ToolHeader` elements
 *
 * ```
 * <headerToolbar>
 *     <Toolbar></Toolbar>
 * </headerToolbar>
 * <headerToolbar>
 *     <tnt:ToolHeader></tnt:ToolHeader>
 * </headerToolbar>
 * ```
 **/
function getAllSubElementsAtSameDepth(
  aggregationElem: XMLElement
): XMLElement[] {
  // We can never reach this case - if it's the root tag, it will never be an aggregation
  /* istanbul ignore next */
  if (aggregationElem.parent.type !== "XMLElement") {
    return [];
  }

  const aggregationParent = aggregationElem.parent;
  const sameNameAggregations = filter(
    aggregationParent.subElements,
    (_) => _.name === aggregationElem.name && !isEmpty(_.subElements)
  );

  const allSubElementsAtSameDepth = flatMap(sameNameAggregations, (_) => {
    return _.subElements;
  });

  return allSubElementsAtSameDepth;
}
Example #9
Source File: non-unique-id.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
export function validateNonUniqueID(xmlDoc: XMLDocument): NonUniqueIDIssue[] {
  const idCollector = new IdsCollectorVisitor();
  accept(xmlDoc, idCollector);
  const idsToXMLElements = idCollector.idsToXMLElements;
  const duplicatedIdsRecords = pickBy(idsToXMLElements, (_) => _.length > 1);

  const allIDsIssues: NonUniqueIDIssue[] = flatMap(
    duplicatedIdsRecords,
    buildIssuesForSingleID
  );

  return allIDsIssues;
}
Example #10
Source File: find-classes-matching-type.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
export function classIsOfType(
  clazz: UI5Class,
  type: UI5Class | UI5Interface
): boolean {
  const clazzAndSuperClasses = getSuperClasses(clazz).concat([clazz]);
  const superInterfaces = flatMap(clazzAndSuperClasses, (_) => _.implements);

  return (
    includes(clazzAndSuperClasses, type) || includes(superInterfaces, type)
  );
}
Example #11
Source File: turn.ts    From fishbowl with MIT License 5 votes vote down vote up
export function completedCardIds(
  turns: CurrentGameSubscription["games"][0]["turns"]
) {
  return flatMap(turns, (turn) => turn.completed_card_ids)
}
Example #12
Source File: validate-xml-views.ts    From ui5-language-assistant with Apache License 2.0 5 votes vote down vote up
visitXMLAttribute(node: XMLAttribute): void {
    const nodeIssues = flatMap(this.validators.attribute, (_) =>
      _(node, this.model)
    );
    this.collectedIssues = this.collectedIssues.concat(nodeIssues);
  }
Example #13
Source File: traceConvert.ts    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
childrenToList = (entry: Entry) => {
  const fpSort = (fn: ((a: Entry, b: Entry) => number) | undefined) => (list: Entry[]) => list.sort(fn);
  const deepChildren: Array<Entry['span']> = fp.flow(
    fpSort((e1: Entry, e2: Entry) => compareSpan(e1.span, e2.span)),
    fp.flatMap(childrenToList),
  )(entry.children || []);
  return [entry.span, ...deepChildren];
}
Example #14
Source File: traceConvert.ts    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
traceConvert = (traces: MONITOR_TRACE.ITrace): Trace => {
  const { spans: oldSpans, duration: durationNs, depth, spanCount, serviceCount } = traces;
  if (!oldSpans?.length) {
    return {} as Trace;
  }
  const summary = traceSummary(oldSpans);
  const duration = durationNs / 1000;
  const spanDepths = toSpanDepths(oldSpans);
  const groupByParentId = groupBy(oldSpans, (s) => s.parentSpanId);
  let traceTimestamp = 0;

  const spans = flatMap(getRootSpans(oldSpans), (rootSpan) =>
    childrenToList(createSpanTreeEntry(rootSpan, oldSpans)),
  ).map((span, index) => {
    if (!index) {
      traceTimestamp = span.startTime;
    }
    const spanDuration = span.duration / 1000;
    const spanStartTs = span.startTime || traceTimestamp;
    const spanDepth = spanDepths[span.id] || 1;
    const width = ((spanDuration || 0) / duration) * 100;
    const errorType = span.tags.error ? 'critical' : 'none';

    const left = ((spanStartTs - traceTimestamp) / durationNs) * 100;

    return {
      ...span,
      spanId: span.id,
      parentId: span.parentSpanId || null,
      duration: spanDuration,
      durationStr: mkDurationStr(spanDuration),
      left: left >= 100 ? 0 : left,
      width: width < 0.1 ? 0.1 : width,
      depth: (spanDepth + 2) * 5,
      depthClass: (spanDepth - 1) % 6,
      children: (groupByParentId[span.id] || []).map((s) => s.id).join(','),
      annotations: (span.annotations || []).map((a) => ({
        isCore: Constants.CORE_ANNOTATIONS.indexOf(a.value) !== -1,
        left: ((a.timestamp - spanStartTs) / spanDuration) * 100,
        message: a.message,
        value: ConstantNames[a.value] || a.value,
        timestamp: a.timestamp,
        relativeTime: mkDurationStr(a.timestamp - traceTimestamp),
        width: 8,
      })),
      errorType,
    };
  });
  const timeMarkers = [0, 0.2, 0.4, 0.6, 0.8, 1].map((p, index) => ({
    index,
    time: mkDurationStr(duration * p),
  }));
  const timeMarkersBackup = timeMarkers;
  const spansBackup = spans;
  return {
    ...summary,
    totalSpans: spanCount,
    services: serviceCount,
    duration: mkDurationStr(duration),
    depth,
    spans,
    spansBackup,
    timeMarkers,
    timeMarkersBackup,
  };
}
Example #15
Source File: validate-xml-views.ts    From ui5-language-assistant with Apache License 2.0 5 votes vote down vote up
visitXMLElement(node: XMLElement): void {
    const nodeIssues = flatMap(this.validators.element, (_) =>
      _(node, this.model)
    );
    this.collectedIssues = this.collectedIssues.concat(nodeIssues);
  }
Example #16
Source File: validate-xml-views.ts    From ui5-language-assistant with Apache License 2.0 5 votes vote down vote up
visitXMLDocument(node: XMLDocument): void {
    const nodeIssues = flatMap(this.validators.document, (_) =>
      _(node, this.model)
    );
    this.collectedIssues = this.collectedIssues.concat(nodeIssues);
  }
Example #17
Source File: spread-sheet-totals-spec.ts    From S2 with MIT License 4 votes vote down vote up
describe('Spreadsheet Totals Tests', () => {
  let spreadsheet: PivotSheet;
  const dataCfg = assembleDataCfg();

  beforeEach(() => {
    spreadsheet = new PivotSheet(getContainer(), dataCfg, assembleOptions());
  });

  test('should render total nodes on row header', () => {
    spreadsheet.setOptions({ totals: TOTALS_OPTIONS });
    spreadsheet.render();
    const totalNodes = spreadsheet.facet.layoutResult.rowNodes.filter(
      (node) => node.isTotals,
    );
    expect(totalNodes).toHaveLength(3);

    // grand total
    const grandTotalNode = totalNodes.filter(
      (node) => node.isGrandTotals && node.level === 0,
    );
    expect(grandTotalNode).toBeDefined();

    // sub total
    const provinceSubTotalNodes = totalNodes.filter(
      (node) => node.field === 'city' && node.level === 1,
    );
    expect(provinceSubTotalNodes).toHaveLength(2); // 四川、浙江
  });

  test('should render total nodes on col header', () => {
    spreadsheet.setOptions({ totals: TOTALS_OPTIONS });
    spreadsheet.render();
    const totalNodes = spreadsheet.facet.layoutResult.colNodes.filter(
      (node) => node.isTotals,
    );
    expect(totalNodes).toHaveLength(3);

    // grand total
    const grandTotalNode = totalNodes.filter(
      (node) => node.isGrandTotals && node.level === 0,
    );
    expect(grandTotalNode).toBeDefined();

    // sub total
    const typeSubTotalNodes = totalNodes.filter(
      (node) => node.field === 'sub_type' && node.level === 1,
    );
    expect(typeSubTotalNodes).toHaveLength(2); // 家具、办公用品
  });

  test('should not render grand total nodes', () => {
    spreadsheet.setOptions({
      totals: merge({}, TOTALS_OPTIONS, {
        row: {
          showGrandTotals: false,
        },
        col: {
          showGrandTotals: false,
        },
      }),
    });
    spreadsheet.render();

    const { rowNodes, colNodes } = spreadsheet.facet.layoutResult;
    const totalNodes = flatMap([rowNodes, colNodes], (nodes) => {
      return nodes.filter((node) => node.isTotals);
    });

    expect(totalNodes.filter((node) => node.isGrandTotals)).toHaveLength(0);
    expect(totalNodes).toHaveLength(4);
  });

  test('should not render sub total nodes when always=false', () => {
    const anotherDataCfg = assembleDataCfg() as S2DataConfig;
    /**
     * 构建专用数据集
     * 行头:浙江省下有多个城市、四川省下只有成都
     * 列头:办公用品下有两个维度,家具下只有桌子
     */
    const filterCond = (item) =>
      (item.province === '浙江省' || item.city === '成都市') &&
      (item.type === '办公用品' || item.sub_type === '桌子');
    anotherDataCfg.data = anotherDataCfg.data.filter(filterCond);
    anotherDataCfg.totalData = anotherDataCfg.totalData.filter(filterCond);

    spreadsheet.setDataCfg(anotherDataCfg);
    spreadsheet.setOptions({
      totals: merge({}, TOTALS_OPTIONS, {
        row: {
          showSubTotals: {
            always: false,
          },
        },
        col: {
          showSubTotals: {
            always: false,
          },
        },
      } as S2Options['totals']),
    });
    spreadsheet.render();

    const findSubTotalNode = (
      nodes: Node[],
      parentLabel: string,
      subTotalDimension: string,
    ) => {
      return nodes.find(
        (node) =>
          node.parent.label === parentLabel &&
          node.field === subTotalDimension &&
          node.isSubTotals,
      );
    };

    const { rowNodes, colNodes } = spreadsheet.facet.layoutResult;

    // 当子维度只有一个时,不展示小计节点
    expect(findSubTotalNode(rowNodes, '浙江省', 'city')).toBeDefined();
    expect(findSubTotalNode(rowNodes, '四川省', 'city')).toBeUndefined();
    expect(findSubTotalNode(colNodes, '家具', 'sub_type')).toBeUndefined();
    expect(findSubTotalNode(colNodes, '办公用品', 'sub_type')).toBeDefined();
  });
});
Example #18
Source File: index.tsx    From fishbowl with MIT License 4 votes vote down vote up
function EndGame() {
  const { t } = useTranslation()
  const currentGame = React.useContext(CurrentGameContext)
  const currentPlayer = React.useContext(CurrentPlayerContext)
  const titleClasses = useTitleStyle()
  const [redirectHome, setRedirectHome] = React.useState(false)

  const turnsByPlayer = new Map()
  currentGame.turns.forEach((turn) => {
    turnsByPlayer.set(
      turn.player_id,
      reject(
        (turnsByPlayer.get(turn.player_id) || []).concat([
          turn.completed_card_ids,
        ]),
        (arr) => isEmpty(arr)
      )
    )
  })

  const scoresByPlayer = new Map()
  turnsByPlayer.forEach((value, key) => {
    scoresByPlayer.set(key, flatMap(value).length)
  })

  let highScore = -1
  scoresByPlayer.forEach((value, key) => {
    if (value > highScore) {
      highScore = value
    }
  })

  const highScorePlayers = filter(
    currentGame.players,
    (player) => scoresByPlayer.get(player.id) === highScore
  )

  const redScore = teamScore(Team.Red, currentGame.turns, currentGame.players)
  const blueScore = teamScore(Team.Blue, currentGame.turns, currentGame.players)

  const tie = redScore === blueScore
  const winningTeam = redScore > blueScore ? Team.Red : Team.Blue

  const shareContent = t(
    "end.shareContent",
    "Just had a great time playing {{ url }} online, you should check it out!",
    { url: "fishbowl-game.com" }
  )

  return (
    <>
      {redirectHome && <Redirect to={routes.root}></Redirect>}
      <Grid container direction="column" spacing={2}>
        <Grid item style={{ textAlign: "center" }}>
          <Typography variant="h4" className={titleClasses.title}>
            {t("end.title", "Game Over")}
          </Typography>
        </Grid>
        <Grid item style={{ textAlign: "center" }}>
          <Box style={{ fontSize: "24px", lineHeight: "0.9" }}>
            {<span style={{ color: TeamColor[Team.Red] }}>{redScore}</span>}
            {" - "}
            {<span style={{ color: TeamColor[Team.Blue] }}>{blueScore}</span>}
          </Box>
        </Grid>
        <Grid item>
          <Divider variant="fullWidth"></Divider>
        </Grid>
        <Grid item>
          {tie
            ? t("end.result.tie", "It's a tie! Play again to break it.")
            : t("end.result.win", "{{ teamName }} wins! Bask in the glory.", {
                teamName: t(
                  `teams.teamName.${winningTeam}`,
                  `Team ${winningTeam}`
                ).toLocaleUpperCase(),
              })}
        </Grid>
        {!isEmpty(highScorePlayers) && (
          <Grid item>
            <Trans
              t={t}
              i18nKey="end.highScore"
              count={highScorePlayers.length}
              values={{ highScore }}
              tOptions={{
                defaultValue_plural:
                  "<0>{{playerUsernames}}</0> put the team on their back. They got their team to guess the most number of cards ({{ highScore }}!), across all rounds.",
              }}
            >
              <PlayerChipList players={highScorePlayers}>
                {{ playerUsernames: null }}
              </PlayerChipList>
              {
                " put the team on their back. They got their team to guess the most number of cards ({{ highScore }}!), across all rounds."
              }
            </Trans>
          </Grid>
        )}
        <Grid item>
          {t("end.yourScore", "You scored {{ score }} across all rounds.", {
            score: scoresByPlayer.get(currentPlayer.id) || 0,
          })}
        </Grid>
        <Grid item>
          <Divider variant="fullWidth"></Divider>
        </Grid>
        <Grid
          item
          container
          direction="column"
          justify="center"
          alignItems="center"
        >
          <Box pb={2}>
            {t(
              "end.thanks.share",
              "Thanks for playing -- if you had fun, share it with your friends!"
            )}
          </Box>

          <Grid container justify="center" spacing={2}>
            <Grid item>
              <TwitterShareButton
                url={"fishbowl-game.com"}
                title={shareContent}
              >
                <TwitterIcon size={50} round />
              </TwitterShareButton>
            </Grid>
            <Grid item>
              <FacebookShareButton
                url={"fishbowl-game.com"}
                title={shareContent}
              >
                <FacebookIcon size={50} round />
              </FacebookShareButton>
            </Grid>
          </Grid>
          <Trans t={t} i18nKey="end.thanks.support">
            <Box pb={1} pt={2}>
              Or support the project by
            </Box>
            <Box py={2}>
              <BuyMeACoffeeButton>Buying us a coffee</BuyMeACoffeeButton>
            </Box>
            <Box py={1}>
              <Link href="https://forms.gle/L9qWMsnAUghXqqxE9" target="_blank">
                sharing your feedback
              </Link>
              , and playing again soon!
            </Box>
          </Trans>
        </Grid>
        <Grid item>
          <Divider variant="fullWidth"></Divider>
        </Grid>
        <Grid item container justify="center">
          <Box py={1}>
            <Button variant="outlined" onClick={() => setRedirectHome(true)}>
              {t("end.playAgainButton", "Play Again")}
            </Button>
          </Box>
        </Grid>
      </Grid>
    </>
  )
}
Example #19
Source File: index.tsx    From fishbowl with MIT License 4 votes vote down vote up
function Play() {
  const { t } = useTranslation()
  const titleClasses = useTitleStyle()
  const currentGame = React.useContext(CurrentGameContext)
  const currentPlayer = React.useContext(CurrentPlayerContext)

  const [startReview] = useStartReviewMutation()

  const [
    hasDismissedInstructionCard,
    setHasDismissedInstructionCard,
  ] = React.useState(false)

  const completedCardIds = flatMap(
    currentGame.turns,
    (turn) => turn.completed_card_ids
  )

  const activeTurn = last(currentGame.turns)
  const activePlayer = currentGame.players.find(
    (player) => player.id === activeTurn?.player_id
  )

  const [activeTurnPlayState, setActiveTurnPlayState] = React.useState(
    playStateFromTurn(activeTurn)
  )
  React.useEffect(() => {
    setActiveTurnPlayState(playStateFromTurn(activeTurn))
  }, [activeTurn])

  const secondsLeft = useSecondsLeft(activeTurnPlayState)

  // countdown timer
  React.useEffect(() => {
    if (
      activeTurnPlayState === ActiveTurnPlayState.Playing &&
      secondsLeft <= 0
    ) {
      setActiveTurnPlayState(ActiveTurnPlayState.Reviewing)
      if (currentPlayer.id === activeTurn?.player_id && activeTurn?.id) {
        startReview({
          variables: {
            currentTurnId: activeTurn.id,
          },
        })
      }
    }
  }, [secondsLeft]) // eslint-disable-line react-hooks/exhaustive-deps

  if (!activeTurn || !activePlayer) {
    return null
  }

  const numCompletedCards = completedCardIds.length
  const totalNumCards = currentGame.cards.length
  const numRounds = currentGame.rounds.length

  if (numCompletedCards === numRounds * totalNumCards) {
    return <NoMoreRounds />
  }

  const roundMarkers = [...Array(numRounds).keys()]
  let roundNum = Math.floor(numCompletedCards / totalNumCards)
  const currentRoundId = currentGame.rounds[roundNum].id
  const nextRoundId = currentGame.rounds[roundNum + 1]?.id

  let roundMarker = numCompletedCards / totalNumCards
  let round = ""
  if (roundMarkers.includes(roundMarker)) {
    const value = capitalize(currentGame.rounds[roundMarker].value)
    round = GameRound[value as GameRound]
    if (round) {
      round = t(`howToPlay.round.${round.toLowerCase()}.name`, round)
    } else {
      round = value
    }
  }

  const yourTurn = activePlayer.id === currentPlayer.id
  const yourTeamTurn =
    activePlayer.team ===
    currentPlayerTeam(currentPlayer.id, currentGame.players)

  let titleText = null
  let content = null
  if (yourTurn) {
    titleText = t("play.yourTurn.title", "Your Turn")
    content = (
      <YourTurnContent
        yourTeamPlayers={filter(
          currentGame.players,
          (player) => activePlayer.team === player.team
        )}
        cardsInBowl={drawableCards(currentGame.turns, currentGame.cards)}
        secondsLeft={secondsLeft}
        activePlayer={activePlayer}
        activeTurn={activeTurn}
        activeTurnPlayState={activeTurnPlayState}
        onStart={() => {
          setActiveTurnPlayState(ActiveTurnPlayState.Playing)
        }}
        onOutOfCards={() => {
          setActiveTurnPlayState(ActiveTurnPlayState.Reviewing)
          startReview({
            variables: {
              currentTurnId: activeTurn.id,
            },
          })
        }}
        currentRoundId={currentRoundId}
        nextRoundId={nextRoundId}
      />
    )
  } else if (yourTeamTurn) {
    titleText = t("play.yourTeam.title", "You're Guessin'")
    content = (
      <YourTeamTurnContent
        activePlayer={activePlayer}
        activeTurn={activeTurn}
      />
    )
  } else {
    titleText = t("play.otherTeam.title", "You're Chillin'")
    content = (
      <OtherTeamContent activePlayer={activePlayer} activeTurn={activeTurn} />
    )
  }

  return (
    <Grid container direction="column" alignItems="center" spacing={2}>
      <Grid item>
        <Typography variant="h4" className={titleClasses.title}>
          {titleText}
        </Typography>
      </Grid>
      <Grid item>
        <Divider
          variant="middle"
          style={{
            width: 150,
            height: 2,
            backgroundColor:
              TeamColor[
                currentPlayerTeam(currentPlayer.id, currentGame.players) as Team
              ] || grey[600],
          }}
        />
      </Grid>

      {round && !hasDismissedInstructionCard && (
        <Grid item>
          <Box mb={1}>
            <GameRoundInstructionCard
              round={round}
              roundNumber={Number(roundMarker + 1)}
              onDismiss={() => {
                setHasDismissedInstructionCard(true)
              }}
            />
          </Box>
        </Grid>
      )}
      <Grid item>
        <TurnContextPanel
          secondsLeft={Math.round(Math.max(secondsLeft, 0)) || 0}
        />
      </Grid>
      <Grid item>{content}</Grid>

      {currentPlayer.role === PlayerRole.Host && (
        <Grid item>
          <HostControls
            activePlayer={activePlayer}
            activeTurn={activeTurn}
            currentRoundId={currentRoundId}
          />
        </Grid>
      )}
    </Grid>
  )
}