4.0 KB131 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {ReactNode} from 'react';
9import type {CommitInfo} from '../types';
10import type {CommitMessageFields, FieldConfig, FieldsBeingEdited, TypeaheadKind} from './types';
11
12import {Subtle} from 'isl-components/Subtle';
13import {Tooltip} from 'isl-components/Tooltip';
14import type {TypeaheadResult} from 'isl-components/Types';
15import {randomId} from 'shared/utils';
16import serverApi from '../ClientToServerAPI';
17import {InlineBadge} from '../InlineBadge';
18import {YouAreHereLabel} from '../YouAreHereLabel';
19import {t, T} from '../i18n';
20import platform from '../platform';
21import {RelativeDate} from '../relativeDate';
22
23export function CommitTitleByline({commit}: {commit: CommitInfo}) {
24 const createdByInfo = (
25 // TODO: determine if you're the author to say "you"
26 <T replace={{$author: commit.author}}>Created by $author</T>
27 );
28 return (
29 <Subtle className="commit-info-title-byline">
30 {commit.isDot ? <YouAreHereLabel /> : null}
31 {commit.phase === 'public' ? <PublicCommitBadge /> : null}
32 <OverflowEllipsis shrink>
33 <Tooltip trigger="hover" component={() => createdByInfo}>
34 {createdByInfo}
35 </Tooltip>
36 </OverflowEllipsis>
37 <OverflowEllipsis>
38 <Tooltip trigger="hover" title={commit.date.toLocaleString()}>
39 <RelativeDate date={commit.date} />
40 </Tooltip>
41 </OverflowEllipsis>
42 </Subtle>
43 );
44}
45
46function PublicCommitBadge() {
47 return (
48 <Tooltip
49 placement="bottom"
50 title={t(
51 "This commit has already been pushed to an append-only remote branch and can't be modified locally.",
52 )}>
53 <InlineBadge>
54 <T>Public</T>
55 </InlineBadge>
56 </Tooltip>
57 );
58}
59
60export function OverflowEllipsis({children, shrink}: {children: ReactNode; shrink?: boolean}) {
61 return <div className={`overflow-ellipsis${shrink ? ' overflow-shrink' : ''}`}>{children}</div>;
62}
63
64export function SmallCapsTitle({children}: {children: ReactNode}) {
65 return <div className="commit-info-small-title">{children}</div>;
66}
67
68export function Section({
69 children,
70 className,
71 ...rest
72}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>) {
73 return (
74 <section {...rest} className={'commit-info-section' + (className ? ' ' + className : '')}>
75 {children}
76 </section>
77 );
78}
79
80export function getFieldToAutofocus(
81 fields: Array<FieldConfig>,
82 fieldsBeingEdited: FieldsBeingEdited,
83 lastFieldsBeingEdited: FieldsBeingEdited | undefined,
84): keyof CommitMessageFields | undefined {
85 for (const field of fields) {
86 const isNewlyBeingEdited =
87 fieldsBeingEdited[field.key] &&
88 (lastFieldsBeingEdited == null || !lastFieldsBeingEdited[field.key]);
89 if (isNewlyBeingEdited) {
90 return field.key;
91 }
92 }
93 return undefined;
94}
95
96export function getOnClickToken(
97 field: FieldConfig & {type: 'field'},
98): ((token: string) => unknown) | undefined {
99 if (field.getUrl == null) {
100 return undefined;
101 }
102
103 return token => {
104 const url = field.getUrl?.(token);
105 url && platform.openExternalLink(url);
106 };
107}
108
109export function convertFieldNameToKey(fieldName: string): string {
110 return fieldName.toLowerCase().replace(/\s/g, '-');
111}
112
113export async function fetchNewSuggestions(
114 kind: TypeaheadKind,
115 text: string,
116): Promise<{values: Array<TypeaheadResult>; fetchStartTimestamp: number}> {
117 const now = Date.now();
118 if (text.trim().length < 2) {
119 // no need to do a fetch on zero- or one-char input...
120 // it's slow and doesn't give good suggestions anyway
121 return {values: [], fetchStartTimestamp: now};
122 }
123 const id = randomId();
124 serverApi.postMessage({type: 'typeahead', kind, id, query: text});
125 const values = await serverApi.nextMessageMatching(
126 'typeaheadResult',
127 message => message.id === id,
128 );
129 return {values: values.result, fetchStartTimestamp: now};
130}
131