Skip to content

Commit 66d1b91

Browse files
committed
refactor: Moving components from HistoryLogGroup.tsx to ContributorAvatars.tsx and HistoryLogGroupEntries.tsx
1 parent 0f85ada commit 66d1b91

3 files changed

Lines changed: 136 additions & 122 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { ComponentProps, useState } from 'react';
2+
import { Avatar, Stack } from '@openedx/paragon';
3+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
4+
import { LibraryPublishContributor } from '@src/library-authoring/data/api';
5+
import messages from './messages';
6+
7+
const MAX_VISIBLE_CONTRIBUTORS = 5;
8+
9+
interface ContributorAvatarProps {
10+
username?: string;
11+
src?: string;
12+
className: string;
13+
size: ComponentProps<typeof Avatar>['size'];
14+
}
15+
16+
interface ContributorsAvatarsProps {
17+
contributors: LibraryPublishContributor[];
18+
}
19+
20+
export const ContributorAvatar = ({
21+
username,
22+
src,
23+
className,
24+
size,
25+
}: ContributorAvatarProps) => {
26+
const intl = useIntl();
27+
const [imgError, setImgError] = useState(false);
28+
return (
29+
<Avatar
30+
className={className}
31+
size={size}
32+
src={imgError ? undefined : src}
33+
alt={username || intl.formatMessage(messages.historyEntryDefaultUser)}
34+
onError={() => setImgError(true)}
35+
/>
36+
);
37+
};
38+
39+
export const ContributorsAvatars = ({ contributors }: ContributorsAvatarsProps) => {
40+
const visible = contributors.slice(0, MAX_VISIBLE_CONTRIBUTORS);
41+
return (
42+
<Stack direction="horizontal" gap={2} className="ml-4.5">
43+
<div className="contributors-avatars">
44+
{visible.map(({ username, profileImageUrls }) => (
45+
<ContributorAvatar
46+
key={username}
47+
size="xs"
48+
className="contributors-avatar"
49+
username={username}
50+
src={profileImageUrls.small}
51+
/>
52+
))}
53+
</div>
54+
<span className="small">
55+
<FormattedMessage
56+
{...messages.historyContributors}
57+
values={{ count: contributors.length }}
58+
/>
59+
</span>
60+
</Stack>
61+
);
62+
};

src/library-authoring/generic/history-log/HistoryLogGroup.tsx

Lines changed: 5 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import { ComponentProps, ReactNode, useState } from 'react';
1+
import { ReactNode } from 'react';
22
import moment from 'moment';
33
import classNames from 'classnames';
44
import { Avatar, Collapsible, Icon, Stack, useToggle } from '@openedx/paragon';
55
import { KeyboardArrowDown, KeyboardArrowUp } from '@openedx/paragon/icons';
6-
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
6+
import { useIntl } from '@edx/frontend-platform/i18n';
77

88
import { useLibraryPublishHistoryEntries } from '@src/library-authoring/data/apiHooks';
99
import { LoadingSpinner } from '@src/generic/Loading';
1010

11-
import { LibraryHistoryEntry, LibraryPublishContributor, LibraryPublishHistoryGroup } from '../../data/api';
11+
import { LibraryHistoryEntry, LibraryPublishHistoryGroup } from '../../data/api';
1212
import messages from './messages';
1313
import { getItemIcon } from '@src/generic/block-type-utils';
14-
15-
const MAX_VISIBLE_CONTRIBUTORS = 5;
14+
import { ContributorsAvatars } from './ContributorAvatars';
15+
import { HistoryLogGroupEntries } from './HistoryLogGroupEntries';
1616

1717
export interface HistoryLogGroupTitleProps {
1818
titleMessage: string | ReactNode;
@@ -34,47 +34,13 @@ export interface HistoryDraftLogGroupProps {
3434
entries: LibraryHistoryEntry[];
3535
}
3636

37-
export interface HistoryLogGroupEntriesProps {
38-
entries: LibraryHistoryEntry[];
39-
}
40-
4137
export interface HistoryPublishLogGroupProps extends LibraryPublishHistoryGroup {
4238
itemId: string;
4339
// When true, hides the vertical connector line rendered below this group. Used for the last group
4440
// in the history log to avoid a dangling connector with nothing below it.
4541
hideLogVert?: boolean;
4642
}
4743

48-
interface ContributorAvatarProps {
49-
username?: string;
50-
src?: string;
51-
className: string;
52-
size: ComponentProps<typeof Avatar>['size'];
53-
}
54-
55-
interface ContributorsAvatarsProps {
56-
contributors: LibraryPublishContributor[];
57-
}
58-
59-
const ContributorAvatar = ({
60-
username,
61-
src,
62-
className,
63-
size,
64-
}: ContributorAvatarProps) => {
65-
const intl = useIntl();
66-
const [imgError, setImgError] = useState(false);
67-
return (
68-
<Avatar
69-
className={className}
70-
size={size}
71-
src={imgError ? undefined : src}
72-
alt={username || intl.formatMessage(messages.historyEntryDefaultUser)}
73-
onError={() => setImgError(true)}
74-
/>
75-
);
76-
};
77-
7844
const HistoryLogGroupTitle = ({
7945
titleMessage,
8046
dateMessage,
@@ -105,64 +71,6 @@ const HistoryLogGroupTitle = ({
10571
);
10672
};
10773

108-
const HistoryLogGroupEntries = ({
109-
entries,
110-
}: HistoryLogGroupEntriesProps) => {
111-
const intl = useIntl();
112-
113-
const getEntryMessage = (entry: LibraryHistoryEntry) => {
114-
switch (entry.action) {
115-
case 'edited':
116-
return messages.historyEditEntry;
117-
case 'renamed':
118-
return messages.historyRenameEntry;
119-
case 'created':
120-
return messages.historyCreatedEntry;
121-
default:
122-
return messages.historyEditEntry;
123-
}
124-
};
125-
126-
return (
127-
<Stack gap={0}>
128-
<div className="history-log-vert" />
129-
{entries.map((entry, index, arr) => {
130-
const isLast = index === arr.length - 1;
131-
const entryMessage = getEntryMessage(entry);
132-
133-
return (
134-
<div key={entry.changedAt}>
135-
<Stack direction="horizontal" gap={2} className="ml-1.5">
136-
<ContributorAvatar
137-
username={entry.contributor?.username || intl.formatMessage(messages.historyEntryDefaultUser)}
138-
src={entry.contributor?.profileImageUrls.medium}
139-
className="history-log-group-avatar small-avatar"
140-
size="sm"
141-
/>
142-
<Stack>
143-
<Stack direction="horizontal" gap={1}>
144-
<FormattedMessage
145-
{...entryMessage}
146-
values={{
147-
user: entry.contributor?.username ?? intl.formatMessage(messages.historyEntryDefaultUser),
148-
displayName: <span className="history-log-title text-truncate">{entry.title}</span>,
149-
icon: <Icon src={getItemIcon(entry.itemType)} />,
150-
}}
151-
/>
152-
</Stack>
153-
<span className="small text-gray-500">
154-
{moment(entry.changedAt).fromNow()}
155-
</span>
156-
</Stack>
157-
</Stack>
158-
{!isLast && <div className="history-log-vert" />}
159-
</div>
160-
);
161-
})}
162-
</Stack>
163-
);
164-
};
165-
16674
export const HistoryCreatedLogGroup = ({
16775
user,
16876
displayName,
@@ -223,31 +131,6 @@ export const HistoryDraftLogGroup = ({
223131
);
224132
};
225133

226-
const ContributorsAvatars = ({ contributors }: ContributorsAvatarsProps) => {
227-
const visible = contributors.slice(0, MAX_VISIBLE_CONTRIBUTORS);
228-
return (
229-
<Stack direction="horizontal" gap={2} className="ml-4.5">
230-
<div className="contributors-avatars">
231-
{visible.map(({ username, profileImageUrls }) => (
232-
<ContributorAvatar
233-
key={username}
234-
size="xs"
235-
className="contributors-avatar"
236-
username={username}
237-
src={profileImageUrls.small}
238-
/>
239-
))}
240-
</div>
241-
<span className="small">
242-
<FormattedMessage
243-
{...messages.historyContributors}
244-
values={{ count: contributors.length }}
245-
/>
246-
</span>
247-
</Stack>
248-
);
249-
};
250-
251134
export const HistoryPublishLogGroup = ({
252135
itemId,
253136
publishLogUuid,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
2+
import { LibraryHistoryEntry } from '@src/library-authoring/data/api';
3+
import messages from './messages';
4+
import { Icon, Stack } from '@openedx/paragon';
5+
import { ContributorAvatar } from './ContributorAvatars';
6+
import { getItemIcon } from '@src/generic/block-type-utils';
7+
import moment from 'moment';
8+
9+
export interface HistoryLogGroupEntriesProps {
10+
entries: LibraryHistoryEntry[];
11+
}
12+
13+
export const HistoryLogGroupEntries = ({
14+
entries,
15+
}: HistoryLogGroupEntriesProps) => {
16+
const intl = useIntl();
17+
18+
const getEntryMessage = (entry: LibraryHistoryEntry) => {
19+
switch (entry.action) {
20+
case 'edited':
21+
return messages.historyEditEntry;
22+
case 'renamed':
23+
return messages.historyRenameEntry;
24+
case 'created':
25+
return messages.historyCreatedEntry;
26+
default:
27+
return messages.historyEditEntry;
28+
}
29+
};
30+
31+
return (
32+
<Stack gap={0}>
33+
<div className="history-log-vert" />
34+
{entries.map((entry, index, arr) => {
35+
const isLast = index === arr.length - 1;
36+
const entryMessage = getEntryMessage(entry);
37+
38+
return (
39+
<div key={entry.changedAt}>
40+
<Stack direction="horizontal" gap={2} className="ml-1.5">
41+
<ContributorAvatar
42+
username={entry.contributor?.username || intl.formatMessage(messages.historyEntryDefaultUser)}
43+
src={entry.contributor?.profileImageUrls.medium}
44+
className="history-log-group-avatar small-avatar"
45+
size="sm"
46+
/>
47+
<Stack>
48+
<Stack direction="horizontal" gap={1}>
49+
<FormattedMessage
50+
{...entryMessage}
51+
values={{
52+
user: entry.contributor?.username ?? intl.formatMessage(messages.historyEntryDefaultUser),
53+
displayName: <span className="history-log-title text-truncate">{entry.title}</span>,
54+
icon: <Icon src={getItemIcon(entry.itemType)} />,
55+
}}
56+
/>
57+
</Stack>
58+
<span className="small text-gray-500">
59+
{moment(entry.changedAt).fromNow()}
60+
</span>
61+
</Stack>
62+
</Stack>
63+
{!isLast && <div className="history-log-vert" />}
64+
</div>
65+
);
66+
})}
67+
</Stack>
68+
);
69+
};

0 commit comments

Comments
 (0)