From bf76f377a68ddbb4d3d4fd17e5be6c8d58f4ecc1 Mon Sep 17 00:00:00 2001 From: Chaiwat Trisuwan Date: Sat, 13 Apr 2024 12:07:59 +0700 Subject: [PATCH] Release/v4.0.0 beta.2 (#54) * chore: ASC-00000 - css module typescript config (#233) * fix: ASC-21254 - align api signature for draft page (#210) * fix: can't upload story * fix: story image renderer * fix: align comment tray component props * fix: align story tab component props * fix: change export name to align signature api * fix: ddraft page * fix: align signature api * fix: fill color * fix: ASC-21249 - align story tab component props (#203) * fix: can't upload story * fix: story image renderer * fix: align comment tray component props * fix: align story tab component props * fix: change export name to align signature api * fix: ddraft page * fix: align signature api * fix: story tab * fix: ASC-21260 - align comment tray component props (#202) * fix: can't upload story * fix: story image renderer * fix: align comment tray component props * fix: align story tab component props * fix: change export name to align signature api * fix: ddraft page * fix: align signature api * fix: story tab * fix: remove unused * fix: comment tray * fix: ASC-21404 - AmityViewStoryPage signature alignment (#209) * fix z-index * fix: signature alignment * fix: view story page * feat: add to support 6 users for storybook (#238) * feat: ASC-00000 - use more env from secrets (#239) * feat: add to support 6 users for storybook * fix: add to read env from secrets * feat: ASC-00000 - add more storybook user (#240) * feat: add to support 6 users for storybook * fix: add to read env from secrets * fix: add use secret on staging * fix: permission (#244) * fix: ASC-21393 - refactor customization 4.0 (#211) * feat: add generateShadeColors func * feat: add AmityUIKitManager * chore: add tsdoc to AmityUIKitManager * fix: merge from develop * fix: AmityUIKit manager * fix: remove unused * fix: revert uikit 3 * fix: remove unused * fix: export * fix: move to main v4 * fix: move to v4 * fix: export path * Refactor theme generation logic in ThemeProvider.tsx * fix: theme color generation * fix: theme provider * fix: remove unused * feat: add Typography component with css * fix: css * fix: add queryClient provider * fix: import * fix: import * fix: v4 * fix: conflict * fix: draft page * fix: setup * fix: export * fix: move mergedNavigationBehavior * fix: remove unused * fix: remove unused * fix: uikit manager * fix: uncomment * fix: remove unused * fix: remove unused * fix: remove unused * fix: remove unused files * fix: remove * fix: login * fix: css var * fix: css var * fix: condition * fix: css var * fix: css var * fix: hook * fix: type * fix: ASC-00000 - V4 fix customization (#248) * fix: config structure * fix: add hyperlink config * fix: add validate * fix: config and hyperlink * fix: hyperlink * fix: css * fix: border radius * fix: hyperlink * fix: hyperlink * fix: add storybook user * fix: aspect ratio * fix: hyperlink config css var Co-authored-by: Kiattirat Sujjapongse * fix: css var module Co-authored-by: Kiattirat Sujjapongse * fix: ASC-21789 - create story on view story page doesn't work (#249) * fix: video * fix: uncomment --------- Co-authored-by: Kiattirat Sujjapongse --------- Co-authored-by: Kiattirat Sujjapongse * feat(chat): ASC-0000 - Chat V4 big chunk 1 (#214) * feat: add livechat ui * feat: add chatButton and desktop view * fix: bg color * feat: move story to be under V4 storybook folder * feat: add customComponent * fix: change chat icon * fix: remove unread notification * fix: remove un-used hook * fix: remove un-used * fix: change style to module css * fix: remove unused * feat: add prefix`asc` to default css config * fix: remove chatIcon * fix: modal not open smoothly at the first time * fix: remove un-used styling * feat: update color pallate * feat: update spacing * feat: update color and fontSize * fix: box shadow on header * feat: re-structure folder to v4 * fix: import global css * fix: use typograhy module css * fix: remove unused * feat: add message list and bubbles * fix: wrap element * fix: change file name * feat: add compose bar * feat: add home indicator * feat: add sending a mesage action * fix: sorting message to be asc * feat: add badge for moderator * fix: overflow message * fix: sortBy segmentAsc * feat: add message list pagination * feat(chat): add draft-js as a compose bar * feat: add loading indicator * feat(chat): fix submit * feat(chat): fix compose bar container * feat: add viewing reply message * fix: deleted message display * fix: remove comment * feat(chat): use default compose bar / remove draft-js * feat(chat): remove draft * feat(chat-state): asc-20903 done loading state * feat(chat-state): asc-20903 restructure * feat(chat-state): asc-20903 apply infinite scroll * feat(chat-state): asc-20903 include error from sdk api to local hook * feat(chat-state): asc-20903 add error state * feat(chat-state): asc-20903 add error state * feat(chat-mention): asc-00000 create message with mention * feat: add popover action * feat: add hover state for message action * feat: add flag and mention button * feat(chat-mention): asc-00000 click to mention * feat: add more action * fix: merge * fix: hide mention and flag * fix: ASC-20213 - fix message list (#219) * fix: message list of the same user * fix: z-index * feat: confirm modal v3 to support dark mode * fix: change to use i18n * fix: TODO text display on Modal content * fix: merge className * fix: remove unused * fix: bring overflow-y back Co-authored-by: Kiattirat Sujjapongse --------- Co-authored-by: Kiattirat Sujjapongse * fix(chat): fix pagination issue (#223) * feat(chat): ASC-21243 - create message with reply message (#218) * feat(chat): reply placeholder * feat(chat): create message with reply * feat: ASC-00000 - dark mode hard code (#225) * fix: dark theme color palatte * fix: header color styling * fix: colors on message bubble * fix: color on popover * fix: color on optionIcon * fix(chat): asc-00000 - fix loading state css broken (#221) * fix(chat): asc-00000 loading state css missing * fix(chat): asc-00000 loading state css missing * feat(chat): ASC-20222 - connection offline alert (#213) * feat(chat): asc-20222 - connection state * feat(chat): asc-20222 - add more connection * feat(chat): asc-20222 - update PR * feat(chat): asc-20222 - update PR * feat: update PR * fix(chat): ASC-00000 - update mention payload (#224) * fix(chat): update mention payload * fix(chat): mention ALL * fix: sample app to open sheet every selection * fix: hide heart reaction * feat: ASC-20213 - fix popover still show (#235) * fix: popover still show * fix: bring just ArrowTop code back * fix: resize messagelist container (#232) * feat: ASC-20213 - highlight mention message (#236) * fix: popover still show * fix: bring just ArrowTop code back * feat: add hilight for mention message * fix: truncate long text of replied message * fix: change getTextWithMention to be a component * fix: message not show as a new line * feat: ASC-20213 - fix consequence message list (#237) * fix: remove consequence message list * fix: remove unused * feat(chat): ASC-00000 - api alignment for chat header (#241) * feat(chat): api alignment chat header * Update src/v4/chat/components/AmityLiveChatHeader/livechatHeader.stories.tsx * feat(chat): ASC-00000 - export AmityLiveChatHeader * fix: ASC-21776 - reply message display on compose bar is over the screen (#242) * fix: reply display message over the screen * feat: remove comment * feat(chat): duplicate useSocialMention => useMention and revert * feat(chat): update pr * fix(chat): ASC-00000 - change theme for message composer (#234) * fix(chat): ASC-00000 - duplicate v4 * fix(chat): ASC-00000 - check message length before submit * fix(chat): ASC-00000 -update styling * fix(chat): ASC-00000 - update styling * fix(chat): prettier remove code by accident * fix(chat): remove styled component * fix(chat): asc-21737 - update condition to prevent create duplicate message (#245) * fix(chat): asc-21791 - fix mention payload (#243) * feat(chat): update pr * feat: ASC-20213 - message bubble api alignment (#247) * fix: message bubble api aligment * fix: to use action as params for message bubble * feat: export elements * fix: remove unused * fix: change type name Co-authored-by: Kiattirat Sujjapongse * fix: type name * feat: expose component * fix: wrong type name * fix: modify expose component * fix: storybook's name --------- Co-authored-by: Kiattirat Sujjapongse * fix(chat): ASC-21796 - query chat after connection back to normal + api alignment (#246) * fix(chat): asc-21796 reload data when connection back to normal * feat(chat): api alignment for message list * feat(chat): export * feat(chat): api align live chat page * feat(chat): api align for composer * feat(chat): update pr * feat(chat): update pr * feat(chat): remove all v4 component from useCustomComponent * feat(chat): update pr - remove chat header * feat(chat): ASC-00000 - compose bar api alignment (#250) * feat(chat): composebar alignment * feat(chat): update pr * fix(chat): revert code (#251) * feat: ASC-20213 - modal notification v4 (#252) * fix: add confirm modal v4 * fix: change to use Notification v4 * fix: remove unused * fix: duplicate icon to v4 * fix: move confirm model component * fix: revert modal and confirm v3 components * fix: revert notification v3 * fix: move Icon to v4 and remove unused icon * fix(chat): revert code * feat: ASC-20213 - fix delete message display (#256) * fix: move component to v4 folder * fix: delete message * feat(chat): update pr * fix(chat): ASC-21734 - Fix UI Bug (#255) * fix(chat): update mention ui * fix(chat): update mention ui * fix(chat): update delete word * fix(chat): update mention ui * fix(chat): update mention ui * Update src/i18n/en.json Co-authored-by: Pitchaya T. <33589608+ptchayap@users.noreply.github.com> * feat(chat): update mention logic * feat(chat): update pr * feat(chat): revert text * feat(chat): update mention logic * fix(chat): popup width * fix(chat): font-color in light mode * fix(chat): placeholder * fix(chat): fix moderator * feat(chat): fix mention * feat(chat): fix avatar in reply * feat(chat): update pr * feat(chat): use rem * feat(chat): update pr --------- Co-authored-by: Pitchaya T. <33589608+ptchayap@users.noreply.github.com> * fix: ASC-20213 - bug staging (#257) * fix: bug staging * fix: revert overflow-x * feat: ASC-20213 - livechat configuration merge (#253) * feat: add generateShadeColors func * feat: add AmityUIKitManager * chore: add tsdoc to AmityUIKitManager * fix: merge from develop * fix: AmityUIKit manager * fix: remove unused * fix: revert uikit 3 * fix: remove unused * fix: export * fix: move to main v4 * fix: move to v4 * fix: export path * Refactor theme generation logic in ThemeProvider.tsx * fix: theme color generation * fix: theme provider * fix: remove unused * feat: add Typography component with css * fix: css * fix: add queryClient provider * fix: import * fix: import * fix: color palette * fix: change global.css file * fix: v4 * fix: conflict * fix: draft page * fix: setup * fix: export * fix: move mergedNavigationBehavior * fix: remove unused * fix: remove unused * fix: uikit manager * fix: uncomment * fix: remove unused * fix: remove unused * fix: remove unused * fix: remove unused files * fix: remove * fix: login * fix: typography and style files * fix: typography * fix: message type * fix: type * fix: add background to livechat page * feat: add config for message bubble * feat: fix cannot load next page * fix: type * feat: add placeholder for compose bar configuration * feat: revert storybook user * fix: use primary color from theme * fix: nested component style * fix: to pass class * fix: change to be module.css * fix: remove unused file * fix: remove unused config * fix: typography * fix: default user avatar * feat: upgrade ts-sdk * fix: revet theme file --------- Co-authored-by: Chaiwat Trisuwan * fix(chat): change tag --------- Co-authored-by: ptchaya_p Co-authored-by: ptchaya_p Co-authored-by: Pitchaya T <33589608+ptchayap@users.noreply.github.com> Co-authored-by: Chaiwat Trisuwan * feat(chat): ASC-0000 - export compose bar (#258) * feat: export v4 provider (#259) * Release/v4.0.0 beta.1 (#262) * fix: update ts-sdk version * chore(release): 4.0.0-beta.1 --------- Co-authored-by: Chaiwat Trisuwan Co-authored-by: bmo-amity-bot * fix: ASC-00000 - export type (#263) * fix: export type * fix: update version * fix: ASC-00000 - fix provider v4 props (#261) * fix: provider * fix: provider * fix: export * fix: export * fix: type * fix: pnpm lock * fix: deps * fix: export wrong path (#264) * chore(release): 4.0.0-beta.2 * chore(release): 4.0.0-beta.2 --------- Co-authored-by: Bonn Co-authored-by: Pitchaya T <33589608+ptchayap@users.noreply.github.com> Co-authored-by: Kiattirat Sujjapongse Co-authored-by: ptchaya_p Co-authored-by: ptchaya_p Co-authored-by: bmo-amity-bot --- .github/workflows/dev.yaml | 4 + .github/workflows/staging.yaml | 4 + .storybook/decorators/UiKitDecorator.tsx | 60 +-- .storybook/preview.ts | 2 + .vscode/settings.json | 3 + CHANGELOG.md | 2 + amity-uikit.config.json | 169 +++++---- package.json | 3 +- pnpm-lock.yaml | 250 ++++++++++++ .../useChannelModeratorsCollection.ts | 16 + src/chat/hooks/useMessage.ts | 12 + .../components/InputText/InsideInputText.tsx | 1 + src/core/components/SideMenuSection/index.tsx | 1 + .../components/SocialMentionItem/index.tsx | 4 +- src/core/hooks/useLiveCollection.ts | 4 + src/core/providers/UiKitProvider/index.tsx | 74 ++-- .../providers/UiKitProvider/theme/index.ts | 2 +- src/core/v4/components/Typography/index.ts | 25 -- src/global.d.ts | 5 + src/i18n/en.json | 23 +- src/icons/Mention.tsx | 10 + src/icons/index.ts | 1 + src/index.ts | 24 +- src/social/components/Comment/index.tsx | 2 +- .../components/CommentComposeBar/index.tsx | 1 - .../CommunityInfo/UICommunityInfo.tsx | 27 +- src/social/components/CommunityInfo/index.tsx | 11 +- src/social/hooks/useConnectionStates.ts | 31 ++ src/social/pages/Application/index.tsx | 91 ++--- src/social/pages/CommunityFeed/index.tsx | 90 +---- .../v4/components/CommentTray/CommentTray.tsx | 133 ------- .../components/ReactionList/ReactionList.tsx | 99 ----- .../v4/components/StoryTab/StoryTab.tsx | 104 ----- src/social/v4/components/index.ts | 3 - .../v4/elements/CommentButton/styles.tsx | 28 -- src/social/v4/hooks/index.ts | 1 - .../Renderers/Wrappers/Header/index.tsx | 99 ----- src/social/v4/pages/DraftsPage/DraftsPage.tsx | 241 ------------ src/social/v4/pages/DraftsPage/index.tsx | 1 - .../v4/pages/StoryPage/ViewStoriesPage.tsx | 16 - src/social/v4/pages/StoryPage/index.tsx | 1 - src/social/v4/pages/index.ts | 2 - .../v4/providers/CustomizationProvider.tsx | 251 ------------ src/styles.d.ts | 4 - .../components/AmityLiveChatHeader/index.tsx | 55 +++ .../livechatHeader.stories.tsx | 30 ++ .../AmityLiveChatHeader/styles.module.css | 24 ++ .../AmityLiveChatMessageComposeBar/index.tsx | 206 ++++++++++ .../livechatMessageComposeBar.stories.tsx | 36 ++ .../styles.module.css | 72 ++++ .../AmityLiveChatMessageList/index.tsx | 145 +++++++ .../livechatMessageList.stories.tsx | 33 ++ .../styles.module.css | 39 ++ .../index.tsx | 33 ++ .../messageReceiver.stories.tsx | 39 ++ .../AmityLiveChatMessageSenderView/index.tsx | 33 ++ .../messageSender.stories.tsx | 39 ++ .../chat/components/HomeIndicator/index.tsx | 12 + .../HomeIndicator/styles.module.css | 12 + .../MessageAction/index.tsx | 134 +++++++ .../MessageAction/styles.module.css | 61 +++ .../MessageBubble/index.tsx | 43 +++ .../MessageBubble/styles.module.css | 47 +++ .../MessageBubbleContainer/index.tsx | 41 ++ .../MessageBubbleContainer/styles.module.css | 23 ++ .../MessageTextWithMention/index.tsx | 78 ++++ .../MessageTextWithMention/styles.module.css | 3 + .../LiveChatMessageContent/index.tsx | 66 ++++ .../LiveChatMessageContent/styles.module.css | 54 +++ .../LivechatLoadingIndicator/index.tsx | 178 +++++++++ src/v4/chat/components/UserAvatar/index.tsx | 68 ++++ src/v4/chat/components/index.ts | 8 + .../collections/useMessagesCollection.ts | 18 + src/v4/chat/hooks/useChannel.ts | 12 + src/v4/chat/hooks/useChannelPermission.ts | 20 + src/v4/chat/hooks/useChatInfo.ts | 114 ++++++ src/v4/chat/hooks/useCommunity.ts | 15 + src/v4/chat/hooks/useMention.ts | 140 +++++++ src/v4/chat/hooks/useMessageFlaggedByMe.ts | 54 +++ src/v4/chat/hooks/useMessagesList.ts | 13 + .../ChatContainer/ChatLoadingState.tsx | 33 ++ .../ChatContainer/ChatReadyState.tsx | 50 +++ .../ChatContainer/ReplyMessagePlaceholder.tsx | 41 ++ .../AmityLiveChatPage/ChatContainer/index.tsx | 10 + .../ChatContainer/styles.module.css | 92 +++++ src/v4/chat/pages/AmityLiveChatPage/index.tsx | 25 ++ .../livechatPage.stories.tsx | 86 +++++ .../pages/AmityLiveChatPage/styles.module.css | 30 ++ src/v4/chat/pages/index.ts | 1 + src/v4/core/AmityUIKitManager.ts | 155 ++++++++ .../components/BottomSheet/BottomSheet.tsx | 0 .../core}/components/BottomSheet/index.ts | 0 .../core}/components/BottomSheet/styles.tsx | 1 + .../core/components/Button/Button.module.css | 55 +++ src/v4/core/components/Button/Button.tsx | 30 ++ src/v4/core/components/Button/index.ts | 1 + src/v4/core/components/Button/ui.stories.tsx | 67 ++++ src/v4/core/components/ConfirmModal/index.tsx | 91 +++++ .../components/ConfirmModal/styles.module.css | 29 ++ .../v4 => v4/core}/components/Icon/Icon.tsx | 11 +- .../v4 => v4/core}/components/Icon/index.ts | 0 .../components/InputText/InsideInputText.tsx | 225 +++++++++++ src/v4/core/components/InputText/index.tsx | 47 +++ .../components/InputText/styles.module.css | 108 ++++++ .../core/components/InputText/ui.stories.jsx | 65 ++++ src/v4/core/components/Modal/index.tsx | 55 +++ .../core/components/Modal/styles.module.css | 72 ++++ src/v4/core/components/Notification/index.tsx | 80 ++++ .../components/Notification/styles.module.css | 44 +++ src/v4/core/components/Popover/index.tsx | 25 ++ .../core/components/Popover/styles.module.css | 11 + .../components/SocialMentionItem/index.tsx | 154 ++++++++ .../SocialMentionItem/styles.module.css | 38 ++ .../core/components/Typography/Typography.tsx | 78 ++++ src/v4/core/components/Typography/index.ts | 1 + .../core/components/Typography/ui.stories.tsx | 69 ++++ src/{core/v4 => v4/core}/components/index.ts | 0 src/v4/core/providers/AmityUIKitProvider.tsx | 166 ++++++++ .../core/providers/CustomizationProvider.tsx | 314 +++++++++++++++ .../core}/providers/PageBehaviorProvider.tsx | 32 +- src/v4/core/providers/ThemeProvider.tsx | 221 +++++++++++ src/v4/core/providers/UIStyles.module.css | 27 ++ src/v4/core/providers/index.css | 56 +++ src/v4/core/providers/index.ts | 1 + src/v4/icons/ArrowTop.tsx | 9 + src/v4/icons/Badge.tsx | 20 + src/v4/icons/Bin.tsx | 9 + src/v4/icons/Check.tsx | 20 + src/v4/icons/Close.tsx | 23 ++ src/v4/icons/Community.tsx | 22 ++ src/v4/icons/ConnectionSpinner.tsx | 52 +++ src/v4/icons/Copy.tsx | 19 + src/v4/icons/ExclamationCircle.tsx | 24 ++ src/v4/icons/Flag.tsx | 19 + src/v4/icons/HeartReaction.tsx | 36 ++ src/v4/icons/Kebub.tsx | 19 + src/v4/icons/Mention.tsx | 19 + src/v4/icons/Reaction.tsx | 26 ++ src/v4/icons/Redo.tsx | 19 + src/v4/icons/Remove.tsx | 23 ++ src/v4/icons/Reply.tsx | 19 + src/v4/icons/UserRegular.tsx | 23 ++ .../CommentEdition/CommentEdition.tsx | 6 +- .../components/CommentEdition/index.ts | 0 .../components/CommentEdition/styles.tsx | 2 +- .../CommentTray/CommentTray.module.css | 50 +++ .../components/CommentTray/CommentTray.tsx | 62 +++ .../social}/components/CommentTray/index.ts | 0 .../components/CommentTray/ui.stories.tsx | 98 +++++ .../HyperLinkConfig.module.css | 100 +++++ .../HyperLinkConfig/HyperLinkConfig.tsx | 124 +++--- .../components/HyperLinkConfig/index.ts | 0 .../components/HyperLinkConfig/styles.tsx | 3 +- .../ReactionList/ReactionList.module.css | 61 +++ .../components/ReactionList/ReactionList.tsx | 95 +++++ .../social}/components/ReactionList/index.ts | 0 .../components/ReactionList/styles.tsx | 0 .../social}/components/ReactionList/types.ts | 0 .../StoryTab/CreateNewStoryButton.tsx | 3 +- .../social}/components/StoryTab/StoryRing.tsx | 10 +- .../components/StoryTab/StoryTab.module.css | 85 +++++ .../social/components/StoryTab/StoryTab.tsx | 118 ++++++ .../social}/components/StoryTab/index.ts | 0 .../social}/components/StoryTab/styles.tsx | 0 .../ViewStoryPage/ViewStoryPage.module.css | 356 ++++++++++++++++++ .../components/ViewStoryPage}/index.tsx | 106 +++--- src/v4/social/components/index.ts | 5 + .../social}/constants/default-theme-v4.ts | 0 .../elements/ActionButton/ActionButton.tsx | 0 .../social}/elements/ActionButton/index.ts | 0 .../AspectRatioButton/AspectRatioButton.tsx | 9 +- .../elements/AspectRatioButton/index.ts | 0 .../elements/AspectRatioButton/styles.tsx | 2 +- .../elements/BackButton/BackButton.module.css | 15 + .../elements/BackButton/BackButton.tsx | 8 +- .../social}/elements/BackButton/index.ts | 0 .../social}/elements/BackButton/styles.tsx | 2 +- .../elements/CancelButton/CancelButton.tsx | 15 +- .../social}/elements/CancelButton/index.ts | 0 .../social}/elements/CancelButton/styles.tsx | 0 .../elements/CloseButton/CloseButton.tsx | 11 +- .../social}/elements/CloseButton/index.ts | 0 .../social}/elements/CloseButton/styles.tsx | 2 +- .../CommentButton/CommentButton.module.css | 26 ++ .../elements/CommentButton/CommentButton.tsx | 20 +- .../social}/elements/CommentButton/index.ts | 0 .../CreateStoryButton/CreateStoryButton.tsx | 5 +- .../elements/CreateStoryButton/index.ts | 0 .../elements/CreateStoryButton/styles.tsx | 2 +- .../social}/elements/HyperLink/HyperLink.tsx | 0 .../social}/elements/HyperLink/index.ts | 0 .../social}/elements/HyperLink/styles.tsx | 2 +- .../HyperLinkButton/HyperLinkButton.tsx | 5 +- .../social}/elements/HyperLinkButton/index.ts | 0 .../elements/HyperLinkButton/styles.tsx | 2 +- .../ImpressionButton/ImpressionButton.tsx | 3 +- .../elements/ImpressionButton/index.ts | 0 .../elements/ImpressionButton/styles.tsx | 0 .../OverflowMenuButton/OverflowMenuButton.tsx | 5 +- .../elements/OverflowMenuButton/index.ts | 0 .../elements/OverflowMenuButton/styles.tsx | 2 +- .../elements/ReactButton/ReactButton.tsx | 4 +- .../social}/elements/ReactButton/index.ts | 0 .../social}/elements/ReactButton/styles.tsx | 0 .../elements/SaveButton/SaveButton.tsx | 3 +- .../social}/elements/SaveButton/index.ts | 0 .../social}/elements/SaveButton/styles.tsx | 0 .../ShareStoryButton.module.css | 34 ++ .../ShareStoryButton/ShareStoryButton.tsx | 23 +- .../elements/ShareStoryButton/index.ts | 0 .../elements/ShareStoryButton/styles.tsx | 2 +- .../elements/SpeakerButton/SpeakerButton.tsx | 7 +- .../social}/elements/SpeakerButton/index.ts | 0 .../social}/elements/SpeakerButton/styles.tsx | 2 +- .../v4 => v4/social}/elements/index.ts | 0 .../collections/useReactionsCollection.tsx | 0 src/v4/social/hooks/index.ts | 2 + src/v4/social/hooks/useGetActiveStories.tsx | 79 ++++ src/v4/social/hooks/useGetStoryByStoryId.ts | 15 + .../v4 => v4/social}/icons/aspect_ratio.tsx | 0 src/{social/v4 => v4/social}/icons/index.ts | 0 .../internal-components/Badege/Badge.tsx | 0 .../internal-components/Badege/index.ts | 0 .../internal-components/Badege/styles.tsx | 0 .../internal-components/Badege/types.tsx | 0 .../Comment/CommentText.tsx | 0 .../internal-components/Comment/UIComment.tsx | 0 .../internal-components/Comment/index.tsx | 29 +- .../internal-components/Comment/styles.tsx | 1 + .../CommentComposeBar/CommentComposeBar.tsx | 11 +- .../CommentComposeBar/index.tsx | 0 .../CommentComposeBar/styles.tsx | 0 .../CommentList/CommentList.tsx | 27 +- .../internal-components/CommentList/index.ts | 0 .../Playground/Playground.tsx | 5 + .../internal-components/Playground/index.ts | 1 + .../internal-components/Spinner/Spinner.tsx | 0 .../internal-components/Spinner/index.ts | 0 .../internal-components/Spinner/styles.tsx | 0 .../StoryCommentComposeBar.tsx | 48 +-- .../StoryCommentComposeBar/index.ts | 0 .../StoryCommentComposeBar/styles.tsx | 0 .../StoryViewer/Renderers/AutoPlayContent.tsx | 0 .../StoryViewer/Renderers/Default.tsx | 0 .../StoryViewer/Renderers/Image.tsx | 122 +++--- .../Renderers/Renderers.module.css | 234 ++++++++++++ .../StoryViewer/Renderers/Video.tsx | 102 ++--- .../Wrappers/Footer/Footer.module.css | 58 +++ .../Renderers/Wrappers/Footer/index.tsx | 44 +-- .../Renderers/Wrappers/Footer/styles.tsx | 4 +- .../Wrappers/Header/Header.module.css | 219 +++++++++++ .../Renderers/Wrappers/Header/index.tsx | 95 +++++ .../StoryViewer/Renderers/index.tsx | 0 .../StoryViewer/Renderers/styles.tsx | 4 +- .../StoryViewer/Renderers/types.ts | 0 .../StoryViewer/styles.tsx | 6 +- .../pages/DraftsPage/DraftsPage.module.css | 53 +++ src/v4/social/pages/DraftsPage/DraftsPage.tsx | 268 +++++++++++++ src/v4/social/pages/DraftsPage/index.tsx | 1 + .../social}/pages/DraftsPage/styles.tsx | 0 .../pages/StoryPage/CommunityFeedStory.tsx | 298 +++++++++++++++ .../social/pages/StoryPage/ViewStoryPage.tsx | 28 ++ src/v4/social/pages/StoryPage/index.tsx | 1 + src/v4/social/pages/index.ts | 2 + src/v4/social/providers/StoryProvider.tsx | 28 ++ src/v4/social/theme.ts | 6 + src/v4/styles/global.css | 168 +++++++++ src/v4/styles/typography.module.css | 48 +++ src/v4/utils/copyMessage.tsx | 11 + src/v4/utils/deleteMessage.ts | 3 + src/v4/utils/flagMessage.ts | 3 + src/v4/utils/generateShadeColors.ts | 55 +++ src/v4/utils/index.ts | 4 + tsconfig.json | 9 +- 274 files changed, 8870 insertions(+), 1748 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/chat/hooks/collections/useChannelModeratorsCollection.ts create mode 100644 src/chat/hooks/useMessage.ts delete mode 100644 src/core/v4/components/Typography/index.ts create mode 100644 src/icons/Mention.tsx create mode 100644 src/social/hooks/useConnectionStates.ts delete mode 100644 src/social/v4/components/CommentTray/CommentTray.tsx delete mode 100644 src/social/v4/components/ReactionList/ReactionList.tsx delete mode 100644 src/social/v4/components/StoryTab/StoryTab.tsx delete mode 100644 src/social/v4/components/index.ts delete mode 100644 src/social/v4/elements/CommentButton/styles.tsx delete mode 100644 src/social/v4/hooks/index.ts delete mode 100644 src/social/v4/internal-components/StoryViewer/Renderers/Wrappers/Header/index.tsx delete mode 100644 src/social/v4/pages/DraftsPage/DraftsPage.tsx delete mode 100644 src/social/v4/pages/DraftsPage/index.tsx delete mode 100644 src/social/v4/pages/StoryPage/ViewStoriesPage.tsx delete mode 100644 src/social/v4/pages/StoryPage/index.tsx delete mode 100644 src/social/v4/pages/index.ts delete mode 100644 src/social/v4/providers/CustomizationProvider.tsx delete mode 100644 src/styles.d.ts create mode 100644 src/v4/chat/components/AmityLiveChatHeader/index.tsx create mode 100644 src/v4/chat/components/AmityLiveChatHeader/livechatHeader.stories.tsx create mode 100644 src/v4/chat/components/AmityLiveChatHeader/styles.module.css create mode 100644 src/v4/chat/components/AmityLiveChatMessageComposeBar/index.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageComposeBar/livechatMessageComposeBar.stories.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageComposeBar/styles.module.css create mode 100644 src/v4/chat/components/AmityLiveChatMessageList/index.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageList/livechatMessageList.stories.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageList/styles.module.css create mode 100644 src/v4/chat/components/AmityLiveChatMessageReceiverView/index.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageReceiverView/messageReceiver.stories.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageSenderView/index.tsx create mode 100644 src/v4/chat/components/AmityLiveChatMessageSenderView/messageSender.stories.tsx create mode 100644 src/v4/chat/components/HomeIndicator/index.tsx create mode 100644 src/v4/chat/components/HomeIndicator/styles.module.css create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageAction/index.tsx create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageAction/styles.module.css create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageBubble/index.tsx create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageBubble/styles.module.css create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageBubbleContainer/index.tsx create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageBubbleContainer/styles.module.css create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageTextWithMention/index.tsx create mode 100644 src/v4/chat/components/LiveChatMessageContent/MessageTextWithMention/styles.module.css create mode 100644 src/v4/chat/components/LiveChatMessageContent/index.tsx create mode 100644 src/v4/chat/components/LiveChatMessageContent/styles.module.css create mode 100644 src/v4/chat/components/LivechatLoadingIndicator/index.tsx create mode 100644 src/v4/chat/components/UserAvatar/index.tsx create mode 100644 src/v4/chat/components/index.ts create mode 100644 src/v4/chat/hooks/collections/useMessagesCollection.ts create mode 100644 src/v4/chat/hooks/useChannel.ts create mode 100644 src/v4/chat/hooks/useChannelPermission.ts create mode 100644 src/v4/chat/hooks/useChatInfo.ts create mode 100644 src/v4/chat/hooks/useCommunity.ts create mode 100644 src/v4/chat/hooks/useMention.ts create mode 100644 src/v4/chat/hooks/useMessageFlaggedByMe.ts create mode 100644 src/v4/chat/hooks/useMessagesList.ts create mode 100644 src/v4/chat/pages/AmityLiveChatPage/ChatContainer/ChatLoadingState.tsx create mode 100644 src/v4/chat/pages/AmityLiveChatPage/ChatContainer/ChatReadyState.tsx create mode 100644 src/v4/chat/pages/AmityLiveChatPage/ChatContainer/ReplyMessagePlaceholder.tsx create mode 100644 src/v4/chat/pages/AmityLiveChatPage/ChatContainer/index.tsx create mode 100644 src/v4/chat/pages/AmityLiveChatPage/ChatContainer/styles.module.css create mode 100644 src/v4/chat/pages/AmityLiveChatPage/index.tsx create mode 100644 src/v4/chat/pages/AmityLiveChatPage/livechatPage.stories.tsx create mode 100644 src/v4/chat/pages/AmityLiveChatPage/styles.module.css create mode 100644 src/v4/chat/pages/index.ts create mode 100644 src/v4/core/AmityUIKitManager.ts rename src/{core/v4 => v4/core}/components/BottomSheet/BottomSheet.tsx (100%) rename src/{core/v4 => v4/core}/components/BottomSheet/index.ts (100%) rename src/{core/v4 => v4/core}/components/BottomSheet/styles.tsx (98%) create mode 100644 src/v4/core/components/Button/Button.module.css create mode 100644 src/v4/core/components/Button/Button.tsx create mode 100644 src/v4/core/components/Button/index.ts create mode 100644 src/v4/core/components/Button/ui.stories.tsx create mode 100644 src/v4/core/components/ConfirmModal/index.tsx create mode 100644 src/v4/core/components/ConfirmModal/styles.module.css rename src/{core/v4 => v4/core}/components/Icon/Icon.tsx (78%) rename src/{core/v4 => v4/core}/components/Icon/index.ts (100%) create mode 100644 src/v4/core/components/InputText/InsideInputText.tsx create mode 100644 src/v4/core/components/InputText/index.tsx create mode 100644 src/v4/core/components/InputText/styles.module.css create mode 100644 src/v4/core/components/InputText/ui.stories.jsx create mode 100644 src/v4/core/components/Modal/index.tsx create mode 100644 src/v4/core/components/Modal/styles.module.css create mode 100644 src/v4/core/components/Notification/index.tsx create mode 100644 src/v4/core/components/Notification/styles.module.css create mode 100644 src/v4/core/components/Popover/index.tsx create mode 100644 src/v4/core/components/Popover/styles.module.css create mode 100644 src/v4/core/components/SocialMentionItem/index.tsx create mode 100644 src/v4/core/components/SocialMentionItem/styles.module.css create mode 100644 src/v4/core/components/Typography/Typography.tsx create mode 100644 src/v4/core/components/Typography/index.ts create mode 100644 src/v4/core/components/Typography/ui.stories.tsx rename src/{core/v4 => v4/core}/components/index.ts (100%) create mode 100644 src/v4/core/providers/AmityUIKitProvider.tsx create mode 100644 src/v4/core/providers/CustomizationProvider.tsx rename src/{social/v4 => v4/core}/providers/PageBehaviorProvider.tsx (54%) create mode 100644 src/v4/core/providers/ThemeProvider.tsx create mode 100644 src/v4/core/providers/UIStyles.module.css create mode 100644 src/v4/core/providers/index.css create mode 100644 src/v4/core/providers/index.ts create mode 100644 src/v4/icons/ArrowTop.tsx create mode 100644 src/v4/icons/Badge.tsx create mode 100644 src/v4/icons/Bin.tsx create mode 100644 src/v4/icons/Check.tsx create mode 100644 src/v4/icons/Close.tsx create mode 100644 src/v4/icons/Community.tsx create mode 100644 src/v4/icons/ConnectionSpinner.tsx create mode 100644 src/v4/icons/Copy.tsx create mode 100644 src/v4/icons/ExclamationCircle.tsx create mode 100644 src/v4/icons/Flag.tsx create mode 100644 src/v4/icons/HeartReaction.tsx create mode 100644 src/v4/icons/Kebub.tsx create mode 100644 src/v4/icons/Mention.tsx create mode 100644 src/v4/icons/Reaction.tsx create mode 100644 src/v4/icons/Redo.tsx create mode 100644 src/v4/icons/Remove.tsx create mode 100644 src/v4/icons/Reply.tsx create mode 100644 src/v4/icons/UserRegular.tsx rename src/{social/v4 => v4/social}/components/CommentEdition/CommentEdition.tsx (91%) rename src/{social/v4 => v4/social}/components/CommentEdition/index.ts (100%) rename src/{social/v4 => v4/social}/components/CommentEdition/styles.tsx (99%) create mode 100644 src/v4/social/components/CommentTray/CommentTray.module.css create mode 100644 src/v4/social/components/CommentTray/CommentTray.tsx rename src/{social/v4 => v4/social}/components/CommentTray/index.ts (100%) create mode 100644 src/v4/social/components/CommentTray/ui.stories.tsx create mode 100644 src/v4/social/components/HyperLinkConfig/HyperLinkConfig.module.css rename src/{social/v4 => v4/social}/components/HyperLinkConfig/HyperLinkConfig.tsx (70%) rename src/{social/v4 => v4/social}/components/HyperLinkConfig/index.ts (100%) rename src/{social/v4 => v4/social}/components/HyperLinkConfig/styles.tsx (97%) create mode 100644 src/v4/social/components/ReactionList/ReactionList.module.css create mode 100644 src/v4/social/components/ReactionList/ReactionList.tsx rename src/{social/v4 => v4/social}/components/ReactionList/index.ts (100%) rename src/{social/v4 => v4/social}/components/ReactionList/styles.tsx (100%) rename src/{social/v4 => v4/social}/components/ReactionList/types.ts (100%) rename src/{social/v4 => v4/social}/components/StoryTab/CreateNewStoryButton.tsx (90%) rename src/{social/v4 => v4/social}/components/StoryTab/StoryRing.tsx (90%) create mode 100644 src/v4/social/components/StoryTab/StoryTab.module.css create mode 100644 src/v4/social/components/StoryTab/StoryTab.tsx rename src/{social/v4 => v4/social}/components/StoryTab/index.ts (100%) rename src/{social/v4 => v4/social}/components/StoryTab/styles.tsx (100%) create mode 100644 src/v4/social/components/ViewStoryPage/ViewStoryPage.module.css rename src/{social/v4/internal-components/StoryViewer => v4/social/components/ViewStoryPage}/index.tsx (78%) create mode 100644 src/v4/social/components/index.ts rename src/{social/v4 => v4/social}/constants/default-theme-v4.ts (100%) rename src/{social/v4 => v4/social}/elements/ActionButton/ActionButton.tsx (100%) rename src/{social/v4 => v4/social}/elements/ActionButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/AspectRatioButton/AspectRatioButton.tsx (77%) rename src/{social/v4 => v4/social}/elements/AspectRatioButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/AspectRatioButton/styles.tsx (90%) create mode 100644 src/v4/social/elements/BackButton/BackButton.module.css rename src/{social/v4 => v4/social}/elements/BackButton/BackButton.tsx (92%) rename src/{social/v4 => v4/social}/elements/BackButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/BackButton/styles.tsx (89%) rename src/{social/v4 => v4/social}/elements/CancelButton/CancelButton.tsx (79%) rename src/{social/v4 => v4/social}/elements/CancelButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/CancelButton/styles.tsx (100%) rename src/{social/v4 => v4/social}/elements/CloseButton/CloseButton.tsx (78%) rename src/{social/v4 => v4/social}/elements/CloseButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/CloseButton/styles.tsx (91%) create mode 100644 src/v4/social/elements/CommentButton/CommentButton.module.css rename src/{social/v4 => v4/social}/elements/CommentButton/CommentButton.tsx (74%) rename src/{social/v4 => v4/social}/elements/CommentButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/CreateStoryButton/CreateStoryButton.tsx (90%) rename src/{social/v4 => v4/social}/elements/CreateStoryButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/CreateStoryButton/styles.tsx (91%) rename src/{social/v4 => v4/social}/elements/HyperLink/HyperLink.tsx (100%) rename src/{social/v4 => v4/social}/elements/HyperLink/index.ts (100%) rename src/{social/v4 => v4/social}/elements/HyperLink/styles.tsx (93%) rename src/{social/v4 => v4/social}/elements/HyperLinkButton/HyperLinkButton.tsx (93%) rename src/{social/v4 => v4/social}/elements/HyperLinkButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/HyperLinkButton/styles.tsx (86%) rename src/{social/v4 => v4/social}/elements/ImpressionButton/ImpressionButton.tsx (93%) rename src/{social/v4 => v4/social}/elements/ImpressionButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/ImpressionButton/styles.tsx (100%) rename src/{social/v4 => v4/social}/elements/OverflowMenuButton/OverflowMenuButton.tsx (85%) rename src/{social/v4 => v4/social}/elements/OverflowMenuButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/OverflowMenuButton/styles.tsx (91%) rename src/{social/v4 => v4/social}/elements/ReactButton/ReactButton.tsx (95%) rename src/{social/v4 => v4/social}/elements/ReactButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/ReactButton/styles.tsx (100%) rename src/{social/v4 => v4/social}/elements/SaveButton/SaveButton.tsx (95%) rename src/{social/v4 => v4/social}/elements/SaveButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/SaveButton/styles.tsx (100%) create mode 100644 src/v4/social/elements/ShareStoryButton/ShareStoryButton.module.css rename src/{social/v4 => v4/social}/elements/ShareStoryButton/ShareStoryButton.tsx (69%) rename src/{social/v4 => v4/social}/elements/ShareStoryButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/ShareStoryButton/styles.tsx (92%) rename src/{social/v4 => v4/social}/elements/SpeakerButton/SpeakerButton.tsx (91%) rename src/{social/v4 => v4/social}/elements/SpeakerButton/index.ts (100%) rename src/{social/v4 => v4/social}/elements/SpeakerButton/styles.tsx (92%) rename src/{social/v4 => v4/social}/elements/index.ts (100%) rename src/{social/v4 => v4/social}/hooks/collections/useReactionsCollection.tsx (100%) create mode 100644 src/v4/social/hooks/index.ts create mode 100644 src/v4/social/hooks/useGetActiveStories.tsx create mode 100644 src/v4/social/hooks/useGetStoryByStoryId.ts rename src/{social/v4 => v4/social}/icons/aspect_ratio.tsx (100%) rename src/{social/v4 => v4/social}/icons/index.ts (100%) rename src/{social/v4 => v4/social}/internal-components/Badege/Badge.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/Badege/index.ts (100%) rename src/{social/v4 => v4/social}/internal-components/Badege/styles.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/Badege/types.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/Comment/CommentText.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/Comment/UIComment.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/Comment/index.tsx (93%) rename src/{social/v4 => v4/social}/internal-components/Comment/styles.tsx (99%) rename src/{social/v4 => v4/social}/internal-components/CommentComposeBar/CommentComposeBar.tsx (94%) rename src/{social/v4 => v4/social}/internal-components/CommentComposeBar/index.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/CommentComposeBar/styles.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/CommentList/CommentList.tsx (88%) rename src/{social/v4 => v4/social}/internal-components/CommentList/index.ts (100%) create mode 100644 src/v4/social/internal-components/Playground/Playground.tsx create mode 100644 src/v4/social/internal-components/Playground/index.ts rename src/{social/v4 => v4/social}/internal-components/Spinner/Spinner.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/Spinner/index.ts (100%) rename src/{social/v4 => v4/social}/internal-components/Spinner/styles.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/StoryCommentComposeBar/StoryCommentComposeBar.tsx (71%) rename src/{social/v4 => v4/social}/internal-components/StoryCommentComposeBar/index.ts (100%) rename src/{social/v4 => v4/social}/internal-components/StoryCommentComposeBar/styles.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/AutoPlayContent.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/Default.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/Image.tsx (73%) create mode 100644 src/v4/social/internal-components/StoryViewer/Renderers/Renderers.module.css rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/Video.tsx (81%) create mode 100644 src/v4/social/internal-components/StoryViewer/Renderers/Wrappers/Footer/Footer.module.css rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/Wrappers/Footer/index.tsx (68%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/Wrappers/Footer/styles.tsx (98%) create mode 100644 src/v4/social/internal-components/StoryViewer/Renderers/Wrappers/Header/Header.module.css create mode 100644 src/v4/social/internal-components/StoryViewer/Renderers/Wrappers/Header/index.tsx rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/index.tsx (100%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/styles.tsx (98%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/Renderers/types.ts (100%) rename src/{social/v4 => v4/social}/internal-components/StoryViewer/styles.tsx (99%) create mode 100644 src/v4/social/pages/DraftsPage/DraftsPage.module.css create mode 100644 src/v4/social/pages/DraftsPage/DraftsPage.tsx create mode 100644 src/v4/social/pages/DraftsPage/index.tsx rename src/{social/v4 => v4/social}/pages/DraftsPage/styles.tsx (100%) create mode 100644 src/v4/social/pages/StoryPage/CommunityFeedStory.tsx create mode 100644 src/v4/social/pages/StoryPage/ViewStoryPage.tsx create mode 100644 src/v4/social/pages/StoryPage/index.tsx create mode 100644 src/v4/social/pages/index.ts create mode 100644 src/v4/social/providers/StoryProvider.tsx create mode 100644 src/v4/social/theme.ts create mode 100644 src/v4/styles/global.css create mode 100644 src/v4/styles/typography.module.css create mode 100644 src/v4/utils/copyMessage.tsx create mode 100644 src/v4/utils/deleteMessage.ts create mode 100644 src/v4/utils/flagMessage.ts create mode 100644 src/v4/utils/generateShadeColors.ts create mode 100644 src/v4/utils/index.ts diff --git a/.github/workflows/dev.yaml b/.github/workflows/dev.yaml index 7f6c89ded..98672e9de 100644 --- a/.github/workflows/dev.yaml +++ b/.github/workflows/dev.yaml @@ -16,6 +16,10 @@ jobs: STORYBOOK_API_KEY: ${{ secrets.STORYBOOK_API_KEY }} STORYBOOK_USER1: ${{ secrets.STORYBOOK_USER1 }} STORYBOOK_USER2: ${{ secrets.STORYBOOK_USER2 }} + STORYBOOK_USER3: ${{ secrets.STORYBOOK_USER3 }} + STORYBOOK_USER4: ${{ secrets.STORYBOOK_USER4 }} + STORYBOOK_USER5: ${{ secrets.STORYBOOK_USER5 }} + STORYBOOK_USER6: ${{ secrets.STORYBOOK_USER6 }} steps: - name: git checkout diff --git a/.github/workflows/staging.yaml b/.github/workflows/staging.yaml index 3f2bacce6..8732d0050 100644 --- a/.github/workflows/staging.yaml +++ b/.github/workflows/staging.yaml @@ -18,6 +18,10 @@ jobs: STORYBOOK_API_KEY: ${{ secrets.STORYBOOK_API_KEY }} STORYBOOK_USER1: ${{ secrets.STORYBOOK_USER1 }} STORYBOOK_USER2: ${{ secrets.STORYBOOK_USER2 }} + STORYBOOK_USER3: ${{ secrets.STORYBOOK_USER3 }} + STORYBOOK_USER4: ${{ secrets.STORYBOOK_USER4 }} + STORYBOOK_USER5: ${{ secrets.STORYBOOK_USER5 }} + STORYBOOK_USER6: ${{ secrets.STORYBOOK_USER6 }} steps: - name: git checkout diff --git a/.storybook/decorators/UiKitDecorator.tsx b/.storybook/decorators/UiKitDecorator.tsx index 93e1f66c2..9f618a51e 100644 --- a/.storybook/decorators/UiKitDecorator.tsx +++ b/.storybook/decorators/UiKitDecorator.tsx @@ -1,10 +1,11 @@ -import React, { useCallback, useEffect, useRef } from 'react'; -import UiKitProvider from '../../src/core/providers/UiKitProvider'; - +import React, { useCallback } from 'react'; +import { AmityUIKitProvider } from '../../src/v4/core/providers'; import { Preview } from '@storybook/react'; +import amityConfig from '../../amity-uikit.config.json'; -const GLOBAL_NAME = 'user'; +export type AmityUIKitConfig = typeof amityConfig; +const GLOBAL_NAME = 'user'; const global = { [GLOBAL_NAME]: { name: 'User selector', @@ -13,10 +14,7 @@ const global = { toolbar: { icon: 'user', items: [ - { - value: 'Web-Test,Web-Test', - title: 'Web-Test', - }, + { value: 'Web-Test,Web-test', title: 'Web-Test' }, { value: import.meta.env.STORYBOOK_USER1, title: import.meta.env.STORYBOOK_USER1?.split(',')[1], @@ -25,6 +23,22 @@ const global = { value: import.meta.env.STORYBOOK_USER2, title: import.meta.env.STORYBOOK_USER2?.split(',')[1], }, + { + value: import.meta.env.STORYBOOK_USER3, + title: import.meta.env.STORYBOOK_USER3?.split(',')[1], + }, + { + value: import.meta.env.STORYBOOK_USER4, + title: import.meta.env.STORYBOOK_USER4?.split(',')[1], + }, + { + value: import.meta.env.STORYBOOK_USER5, + title: import.meta.env.STORYBOOK_USER5?.split(',')[1], + }, + { + value: import.meta.env.STORYBOOK_USER6, + title: import.meta.env.STORYBOOK_USER6?.split(',')[1], + }, ], }, }, @@ -32,20 +46,13 @@ const global = { const FALLBACK_USER = 'Web-Test,Web-Test'; -const apiKey = import.meta.env.STORYBOOK_API_KEY; -const apiRegion = import.meta.env.STORYBOOK_API_REGION; - -const decorator: NonNullable = ( +const decorator: NonNullable[number] = ( Story, - { globals: { [GLOBAL_NAME]: val } }, + { globals: { [GLOBAL_NAME]: val, theme } }, ) => { const user = val || FALLBACK_USER; const [userId, displayName] = user.split(','); - const ref = useRef<{ logout: () => void; login: () => Promise } | null>(null); - - console.log('-------------------', val); - const handleConnectionStatusChange = useCallback((...args) => { console.log(`[UiKitProvider.handleConnectionStatusChange]`, ...args); }, []); @@ -58,28 +65,21 @@ const decorator: NonNullable = ( console.log(`[UiKitProvider.handleDisconnected]`, ...args); }, []); - useEffect(() => { - ref.current?.logout(); - ref.current?.login(); - }, [userId]); - return ( - {Story()} - + ); }; -export default { - global, - decorator, -}; +export default { global, decorator }; diff --git a/.storybook/preview.ts b/.storybook/preview.ts index f42902933..c7dd0ca30 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -13,6 +13,8 @@ const preview: Preview = { ['Social', 'Chat'], 'Utilities', 'Assets', + 'V4', + ['Chat'], ], }, }, diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..25fa6215f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index e67db5c6b..05a80770c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## 4.0.0-beta.2 (2024-04-12) + ## 4.0.0-beta.1 (2024-04-05) ### Bug Fixes diff --git a/amity-uikit.config.json b/amity-uikit.config.json index 51580c96b..55ed64dc6 100644 --- a/amity-uikit.config.json +++ b/amity-uikit.config.json @@ -1,133 +1,174 @@ { - "global_theme": { - "light_theme": { + "preferred_theme": "default", + "theme": { + "light": { "primary_color": "#1054DE", - "secondary_color": "#292B32" + "secondary_color": "#292B32", + "base_color": "#292b32", + "base_shade1_color": "#636878", + "base_shade2_color": "#898e9e", + "base_shade3_color": "#a5a9b5", + "base_shade4_color": "#ebecef", + "alert_color": "#FA4D30", + "background_color": "#FFFFFF" + }, + "dark": { + "primary_color": "#1054DE", + "secondary_color": "#292B32", + "base_color": "#ebecef", + "base_shade1_color": "#a5a9b5", + "base_shade2_color": "#6e7487", + "base_shade3_color": "#40434e", + "base_shade4_color": "#292b32", + "alert_color": "#FA4D30", + "background_color": "#191919" } }, "excludes": [], "customizations": { "select_target_page/*/*": { - "page_theme": { - "light_theme": { - "primary_color": "#1054DE", - "secondary_color": "#292B32" - } - }, + "theme": {}, "title": "Share to" }, "select_target_page/*/back_button": { - "back_icon": "back" + "theme": {}, + "back_icon": "back.png" }, + "camera_page/*/*": { - "resolution": "720p", - "page_theme": { - "light_theme": { - "primary_color": "#1054DE", - "secondary_color": "#292B32" - } - } + "theme": {}, + "resolution": "720p" }, "camera_page/*/close_button": { - "close_icon": "close", - "background_color": "#80000000" + "theme": {}, + "close_icon": "close.png" + }, + "create_story_page/*/*": { + "theme": {} }, - "create_story_page/*/*": {}, "create_story_page/*/back_button": { - "back_icon": "back", - "background_color": "#80000000" + "theme": {}, + "back_icon": "back.png", + "background_color": "" }, "create_story_page/*/aspect_ratio_button": { - "aspect_ratio_icon": "aspect_ratio", - "background_color": "#80000000" + "theme": {}, + "aspect_ratio_icon": "aspect_ratio.png", + "background_color": "" }, "create_story_page/*/story_hyperlink_button": { - "hyperlink_button_icon": "hyperlink_button", + "theme": {}, + "hyperlink_button_icon": "hyperlink_button.png", "background_color": "" }, "create_story_page/*/hyper_link": { - "hyper_link_icon": "hyper_link", + "theme": {}, + "hyper_link_icon": "hyper_link.png", "background_color": "" }, "create_story_page/*/share_story_button": { - "share_icon": "share_story_button", - "background_color": "#FFFFFF", + "theme": {}, + "share_icon": "share_story_button.png", + "background_color": "", "hide_avatar": false }, - "story_page/*/*": {}, - "story_page/*/progress_bar": {}, + "story_page/*/*": { + "theme": {} + }, + "story_page/*/progress_bar": { + "theme": {}, + "progress_color": "", + "background_color": "" + }, "story_page/*/overflow_menu": { - "overflow_menu_icon": "threeDot" + "theme": {}, + "overflow_menu_icon": "threeDot.png" }, "story_page/*/close_button": { - "close_icon": "close" + "theme": {}, + "close_icon": "close.png" }, "story_page/*/story_impression_button": { - "impression_icon": "impressionIcon" + "theme": {}, + "impression_icon": "impressionIcon.png" }, "story_page/*/story_comment_button": { - "comment_icon": "comment" + "theme": {}, + "comment_icon": "comment.png", + "background_color": "" }, "story_page/*/story_reaction_button": { - "reaction_icon": "like", - "background_color": "#1243EE" + "theme": {}, + "reaction_icon": "like.png", + "background_color": "" }, "story_page/*/create_new_story_button": { - "create_new_story_icon": "plus", - "background_color": "#1243EE" + "theme": {}, + "create_new_story_icon": "plus.png", + "background_color": "#ffffff" }, "story_page/*/speaker_button": { - "mute_icon": "mute", - "unmute_icon": "unmute" + "theme": {}, + "mute_icon": "mute.png", + "unmute_icon": "unmute.png", + "background_color": "" }, + "*/edit_comment_component/*": { - "component_theme": { - "light_theme": { - "primary_color": "#1054DE", - "secondary_color": "#292B32" - } - } + "theme": {} }, "*/edit_comment_component/cancel_button": { + "theme": {}, "cancel_icon": "", - "cancel_button_text": "Cancel", - "background_color": "#FFFFFF" + "cancel_button_text": "cancel", + "background_color": "" }, "*/edit_comment_component/save_button": { + "theme": {}, "save_icon": "", "save_button_text": "Save", - "background_color": "#1054DE" + "background_color": "" }, + "*/hyper_link_config_component/*": { - "component_theme": { - "light_theme": { - "primary_color": "", - "secondary_color": "" - } - } + "theme": {} }, "*/hyper_link_config_component/done_button": { + "theme": {}, "done_icon": "", - "done_button_text": "", + "done_button_text": "Done", "background_color": "" }, "*/hyper_link_config_component/cancel_button": { + "theme": {}, "cancel_icon": "", - "cancel_button_text": "" + "cancel_button_text": "Cancel" + }, + "*/comment_tray_component/*": { + "theme": {} + }, + "*/story_tab_component/*": { + "theme": {} }, - "*/comment_tray_component/*": {}, - "*/story_tab_component/*": {}, "*/story_tab_component/story_ring": { + "theme": {}, "progress_color": ["#339AF9", "#78FA58"], - "fail_color": ["#FA4D30"], - "background_color": ["#EBECEF"] + "background_color": "" }, "*/story_tab_component/create_new_story_button": { - "create_new_story_icon": "plus", - "background_color": "#1243EE" + "theme": {}, + "create_new_story_icon": "plus.png", + "background_color": "" }, "*/*/close_button": { - "close_icon": "close" + "theme": {}, + "close_icon": "close.png" + }, + "live_chat/*/*": {}, + "live_chat/chat_header/*": {}, + "live_chat/message_list/*": {}, + "live_chat/message_composer/*": { + "placeholder_text": "Write a message" } } } diff --git a/package.json b/package.json index ade65675a..f97056f86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@amityco/ui-kit-open-source", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "engines": { "node": ">=16", "pnpm": ">=8" @@ -90,6 +90,7 @@ "ts-jest": "^29.1.1", "tsup": "^7.3.0", "typescript": "^4.9.5", + "typescript-plugin-css-modules": "^5.1.0", "vite": "^4.5.1", "vite-tsconfig-paths": "^4.2.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b5ab3320..915a0f9d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -244,6 +244,9 @@ devDependencies: typescript: specifier: ^4.9.5 version: 4.9.5 + typescript-plugin-css-modules: + specifier: ^5.1.0 + version: 5.1.0(typescript@4.9.5) vite: specifier: ^4.5.1 version: 4.5.2 @@ -4270,6 +4273,18 @@ packages: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true + /@types/postcss-modules-local-by-default@4.0.2: + resolution: {integrity: sha512-CtYCcD+L+trB3reJPny+bKWKMzPfxEyQpKIwit7kErnOexf5/faaGpkFy4I5AwbV4hp1sk7/aTg0tt0B67VkLQ==} + dependencies: + postcss: 8.4.38 + dev: true + + /@types/postcss-modules-scope@3.0.4: + resolution: {integrity: sha512-//ygSisVq9kVI0sqx3UPLzWIMCmtSVrzdljtuaAEJtGoGnpjBikZ2sXO5MpH9SnWX9HRfXxHifDAXcQjupWnIQ==} + dependencies: + postcss: 8.4.38 + dev: true + /@types/pretty-hrtime@1.0.3: resolution: {integrity: sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==} dev: true @@ -5933,6 +5948,12 @@ packages: engines: {node: '>= 0.6'} dev: true + /copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + dependencies: + is-what: 3.14.1 + dev: true + /copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} dependencies: @@ -6009,6 +6030,12 @@ packages: source-map: 0.6.1 dev: false + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} dev: true @@ -6250,6 +6277,11 @@ packages: engines: {node: '>=12'} dev: true + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: true + /dotgitignore@2.1.0: resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} engines: {node: '>=6'} @@ -6374,6 +6406,15 @@ packages: hasBin: true dev: true + /errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + requiresBuild: true + dependencies: + prr: 1.0.1 + dev: true + optional: true + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -7698,6 +7739,24 @@ packages: safer-buffer: 2.1.2 dev: true + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dependencies: + safer-buffer: 2.1.2 + dev: true + optional: true + + /icss-utils@5.1.0(postcss@8.4.38): + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.38 + dev: true + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: true @@ -7707,6 +7766,18 @@ packages: engines: {node: '>= 4'} dev: true + /image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /immutable@4.3.5: + resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==} + dev: true + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -8030,6 +8101,10 @@ packages: call-bind: 1.0.7 dev: true + /is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + dev: true + /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -8711,6 +8786,24 @@ packages: dotenv-expand: 10.0.0 dev: true + /less@4.2.0: + resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} + engines: {node: '>=6'} + hasBin: true + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.6.2 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + dev: true + /leven@2.1.0: resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} engines: {node: '>=0.10.0'} @@ -8860,6 +8953,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: true + /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true @@ -9289,6 +9386,17 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + requiresBuild: true + dependencies: + iconv-lite: 0.6.3 + sax: 1.3.0 + dev: true + optional: true + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -9627,6 +9735,11 @@ packages: lines-and-columns: 1.2.4 dev: true + /parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + dev: true + /parseqs@0.0.5: resolution: {integrity: sha512-B3Nrjw2aL7aI4TDujOzfA4NsEc4u1lVcIRE0xesutH8kjeWF70uk+W5cBlIQx04zUH9NTBvuN36Y9xLRPK6Jjw==} dependencies: @@ -9741,6 +9854,7 @@ packages: /pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + requiresBuild: true dev: true /pirates@4.0.6: @@ -9797,6 +9911,45 @@ packages: yaml: 2.4.0 dev: true + /postcss-modules-extract-imports@3.1.0(postcss@8.4.38): + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.38 + dev: true + + /postcss-modules-local-by-default@4.0.5(postcss@8.4.38): + resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + icss-utils: 5.1.0(postcss@8.4.38) + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-modules-scope@3.2.0(postcss@8.4.38): + resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: true + + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -9819,6 +9972,15 @@ packages: source-map-js: 1.0.2 dev: true + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: true + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -9891,6 +10053,12 @@ packages: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: true + /prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + requiresBuild: true + dev: true + optional: true + /pump@2.0.1: resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} dependencies: @@ -10487,6 +10655,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + /reserved-words@0.1.2: + resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==} + dev: true + /resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} dev: false @@ -10646,6 +10818,20 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /sass@1.74.1: + resolution: {integrity: sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.3.5 + source-map-js: 1.0.2 + dev: true + + /sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + dev: true + /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: @@ -10849,6 +11035,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -10872,6 +11063,11 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -11151,6 +11347,19 @@ packages: /stylis@4.3.1: resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==} + /stylus@0.62.0: + resolution: {integrity: sha512-v3YCf31atbwJQIMtPNX8hcQ+okD4NQaTuKGUWfII8eaqn+3otrbttGL1zSMZAAtiPsBztQnujVBugg/cXFUpyg==} + hasBin: true + dependencies: + '@adobe/css-tools': 4.3.3 + debug: 4.3.4 + glob: 7.2.3 + sax: 1.3.0 + source-map: 0.7.4 + transitivePeerDependencies: + - supports-color + dev: true + /substyle@9.4.1(react@18.2.0): resolution: {integrity: sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==} peerDependencies: @@ -11512,6 +11721,15 @@ packages: strip-bom: 3.0.0 dev: true + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true @@ -11679,6 +11897,33 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true + /typescript-plugin-css-modules@5.1.0(typescript@4.9.5): + resolution: {integrity: sha512-6h+sLBa4l+XYSTn/31vZHd/1c3SvAbLpobY6FxDiUOHJQG1eD9Gh3eCs12+Eqc+TCOAdxcO+zAPvUq0jBfdciw==} + peerDependencies: + typescript: '>=4.0.0' + dependencies: + '@types/postcss-modules-local-by-default': 4.0.2 + '@types/postcss-modules-scope': 3.0.4 + dotenv: 16.4.5 + icss-utils: 5.1.0(postcss@8.4.38) + less: 4.2.0 + lodash.camelcase: 4.3.0 + postcss: 8.4.38 + postcss-load-config: 3.1.4(postcss@8.4.38) + postcss-modules-extract-imports: 3.1.0(postcss@8.4.38) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.38) + postcss-modules-scope: 3.2.0(postcss@8.4.38) + reserved-words: 0.1.2 + sass: 1.74.1 + source-map-js: 1.0.2 + stylus: 0.62.0 + tsconfig-paths: 4.2.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} @@ -12216,6 +12461,11 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + /yaml@2.3.1: resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} engines: {node: '>= 14'} diff --git a/src/chat/hooks/collections/useChannelModeratorsCollection.ts b/src/chat/hooks/collections/useChannelModeratorsCollection.ts new file mode 100644 index 000000000..f85a3565a --- /dev/null +++ b/src/chat/hooks/collections/useChannelModeratorsCollection.ts @@ -0,0 +1,16 @@ +import { ChannelRepository } from '@amityco/ts-sdk'; +import useLiveCollection from '~/core/hooks/useLiveCollection'; +import { MemberRoles } from '~/social/constants'; + +export default function useChannelModeratorsCollection(channelId?: string) { + const { items, ...rest } = useLiveCollection({ + fetcher: ChannelRepository.Membership.getMembers, + params: { channelId: channelId as string, roles: [MemberRoles.CHANNEL_MODERATOR] }, + shouldCall: () => !!channelId, + }); + + return { + moderators: items, + ...rest, + }; +} diff --git a/src/chat/hooks/useMessage.ts b/src/chat/hooks/useMessage.ts new file mode 100644 index 000000000..78bf17739 --- /dev/null +++ b/src/chat/hooks/useMessage.ts @@ -0,0 +1,12 @@ +import { MessageRepository } from '@amityco/ts-sdk'; +import useLiveObject from '~/core/hooks/useLiveObject'; + +const useMessage = (messageId?: string) => { + return useLiveObject({ + fetcher: MessageRepository.getMessage, + params: messageId, + shouldCall: () => !!messageId, + }); +}; + +export default useMessage; diff --git a/src/core/components/InputText/InsideInputText.tsx b/src/core/components/InputText/InsideInputText.tsx index 78e22238a..4c81f5564 100644 --- a/src/core/components/InputText/InsideInputText.tsx +++ b/src/core/components/InputText/InsideInputText.tsx @@ -40,6 +40,7 @@ const styling = css` min-width: 0; margin: 0; padding: 0.563rem 0.563rem; + color: ${({ theme }) => theme.palette.neutral.main}; background: none; border: none; box-sizing: border-box; diff --git a/src/core/components/SideMenuSection/index.tsx b/src/core/components/SideMenuSection/index.tsx index 5c390a349..a61b86783 100644 --- a/src/core/components/SideMenuSection/index.tsx +++ b/src/core/components/SideMenuSection/index.tsx @@ -9,6 +9,7 @@ const SectionContainer = styled.div` const ListHeading = styled.h4` ${({ theme }) => theme.typography.title}; + color: black; padding: 0 8px; margin: 1em 0; `; diff --git a/src/core/components/SocialMentionItem/index.tsx b/src/core/components/SocialMentionItem/index.tsx index afdc2baf7..1e5fc934b 100644 --- a/src/core/components/SocialMentionItem/index.tsx +++ b/src/core/components/SocialMentionItem/index.tsx @@ -10,9 +10,9 @@ const Item = styled.div<{ focused?: boolean; isBanned?: boolean; maxWidth?: numb display: flex; align-items: center; padding: 5px 15px; - background-color: ${({ focused, theme }) => focused && theme.palette.base.shade4}; + font-weight: 600; - color: ${({ isBanned, theme }) => isBanned && theme.palette.base.shade2}; + pointer-events: ${({ isBanned }) => isBanned && 'none'} !important; cursor: ${({ isBanned }) => isBanned && 'no-allowed'} !important; max-width: ${({ maxWidth }) => maxWidth || 0}px; diff --git a/src/core/hooks/useLiveCollection.ts b/src/core/hooks/useLiveCollection.ts index 4b06e7903..499ba72f2 100644 --- a/src/core/hooks/useLiveCollection.ts +++ b/src/core/hooks/useLiveCollection.ts @@ -22,12 +22,14 @@ function useLiveCollection({ isLoading: boolean; hasMore: boolean; loadMore: () => void; + error: Error | null; loadMoreHasBeenCalled: boolean; } { const { subscribe } = useSDKLiveCollectionConnector(); const [loadMoreHasBeenCalled, setLoadMoreHasBeenCalled] = useState(false); const [isLoading, setIsLoading] = useState(shouldCall ? shouldCall() : true); const [items, setItems] = useState([]); + const [error, setError] = useState(null); const [hasMore, setHasMore] = useState(false); const loadMoreFnRef = useRef<(() => void) | null>(null); @@ -44,6 +46,7 @@ function useLiveCollection({ if (response.data) setItems(response.data); setIsLoading(response.loading); setHasMore(response.hasNextPage); + setError(response.error); loadMoreFnRef.current = response.onNextPage; callback(response); }, @@ -68,6 +71,7 @@ function useLiveCollection({ hasMore, isLoading, loadMore, + error, loadMoreHasBeenCalled, }; } diff --git a/src/core/providers/UiKitProvider/index.tsx b/src/core/providers/UiKitProvider/index.tsx index 71b3d43ab..d6bfc440c 100644 --- a/src/core/providers/UiKitProvider/index.tsx +++ b/src/core/providers/UiKitProvider/index.tsx @@ -18,10 +18,7 @@ import CustomComponentsProvider, { CustomComponentType } from '../CustomComponen import PostRendererProvider, { PostRendererConfigType, } from '~/social/providers/PostRendererProvider'; -import { Config, CustomizationProvider } from '~/social/v4/providers/CustomizationProvider'; -import amityConfig from '../../../../amity-uikit.config.json'; -import { PageBehaviorProvider } from '~/social/v4/providers/PageBehaviorProvider'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; interface UiKitProviderProps { @@ -48,10 +45,6 @@ interface UiKitProviderProps { onEditUser?: (userId: string) => void; onMessageUser?: (userId: string) => void; }; - pageBehavior?: { - closeAction?: () => void; - hyperLinkAction?: () => void; - }; socialCommunityCreationButtonVisible?: boolean; onConnectionStatusChange?: (state: Amity.SessionStates) => void; onConnected?: () => void; @@ -62,7 +55,6 @@ const UiKitProvider = ({ apiKey, apiRegion, apiEndpoint, - authToken, userId, displayName, customComponents = {}, @@ -71,7 +63,6 @@ const UiKitProvider = ({ children /* TODO localization */, socialCommunityCreationButtonVisible, actionHandlers, - pageBehavior, onConnectionStatusChange, onDisconnected, }: UiKitProviderProps) => { @@ -103,21 +94,6 @@ const UiKitProvider = ({ setClient(ascClient); } - await ASCClient.login( - { userId, displayName, authToken }, - { - sessionWillRenewAccessToken(renewal) { - // secure mode - if (authToken) { - renewal.renewWithAuthToken(authToken); - return; - } - - renewal.renew(); - }, - }, - ); - setIsConnected(true); if (stateChangeRef.current == null) { @@ -148,34 +124,28 @@ const UiKitProvider = ({ return ( - - - - - - - - - - - {children} - - - - - - - - - - - - + + + + + + + + {children} + + + + + + + + + ); diff --git a/src/core/providers/UiKitProvider/theme/index.ts b/src/core/providers/UiKitProvider/theme/index.ts index 75febb338..1cf64902a 100644 --- a/src/core/providers/UiKitProvider/theme/index.ts +++ b/src/core/providers/UiKitProvider/theme/index.ts @@ -2,7 +2,7 @@ import merge from 'lodash/merge'; import defaultTheme from './default-theme'; import { buildPaletteTheme } from './palette'; import { buildTypographyTheme } from './typography'; -import { defaultThemeV4 } from '~/social/v4/constants/default-theme-v4'; +import { defaultThemeV4 } from '~/v4/social/constants/default-theme-v4'; /** * Builds a global theme object combining default theme and an optional override theme. diff --git a/src/core/v4/components/Typography/index.ts b/src/core/v4/components/Typography/index.ts deleted file mode 100644 index 177f4b71e..000000000 --- a/src/core/v4/components/Typography/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -import styled from 'styled-components'; - -const TypographyBase = styled.p<{ - $type: 'heading' | 'title' | 'subTitle' | 'body' | 'bodyBold' | 'caption' | 'captionBold'; -}>` - font-weight: ${({ theme, $type }) => theme.v4.typography[$type]?.fontWeight || 400}; - font-size: ${({ theme, $type }) => theme.v4.typography[$type].fontSize}; - line-height: ${({ theme, $type }) => theme.v4.typography[$type].lineHeight}; -`; - -export const Typography = { - Heading: styled(TypographyBase).attrs({ as: 'h1', $type: 'heading' })``, - Title: styled(TypographyBase).attrs({ as: 'h2', $type: 'title' })``, - SubTitle: styled(TypographyBase).attrs({ as: 'h3', $type: 'subTitle' })``, - Body: styled(TypographyBase).attrs({ $type: 'body' })` - padding: 0; - margin: 0; - `, - BodyBold: styled(TypographyBase).attrs({ $type: 'bodyBold' })` - padding: 0; - margin: 0; - `, - Caption: styled(TypographyBase).attrs({ $type: 'caption' })``, - CaptionBold: styled(TypographyBase).attrs({ $type: 'captionBold' })``, -}; diff --git a/src/global.d.ts b/src/global.d.ts index 51e00d34d..c968121df 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -13,3 +13,8 @@ interface ImportMetaEnv { interface ImportMeta { readonly env: ImportMetaEnv; } + +declare module '*.module.css' { + const classes: { [key: string]: string }; + export default classes; +} diff --git a/src/i18n/en.json b/src/i18n/en.json index d53663475..a320b04d6 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -3,8 +3,9 @@ "general.action.accept": "Accept", "general.action.decline": "Decline", "general.action.discard": "Discard", + "general.action.ok": "Ok", "general.error.tryAgainLater": "Something went wrong. Please try again later.", - + "general.connection.waiting": "Waiting for connection...", "hello": "Hello World", "plural.like": "{amount, plural, one {like} other {likes}}", "comment": "Comment", @@ -182,6 +183,7 @@ "create": "Create", "cancel": "Cancel", "loading": "Loading...", + "loading.chat": "Loading chat...", "anonymous": "Anonymous", "comment.edited": "Edited", "comment.readmore": "...Read more", @@ -207,6 +209,7 @@ "CommentComposeBar.addComment": "Add comment", "CommentComposeBar.replayTo": "Reply to ", + "CommentComposeBar.replayToUser": "Replying to {displayName}", "CommentComposeBar.saySomething": "Say something nice", "CommentComposeBar.unableToPost": "Unable to post", @@ -273,6 +276,7 @@ "chat.members.return": "Return to controls", "chat.members.count": "{count} members", "chat.create.modalTitle": "Create new chat", + "chat.loading.error": "Couldn't load chat", "userSelector.placeholder": "Enter name or email addresses", "userSelector.emptyState.title": "It's empty here...", "userSelector.emptyState.description": "No contact found here", @@ -396,5 +400,20 @@ "editChatMembersModal.confirm.cancelText": "Continue editing", "editChatMembersModal.confirm.okText": "Leave", - "notification.error.blockedWord": "Amity SDK (400308): Text contain blocked word" + "notification.error.blockedWord": "Amity SDK (400308): Text contain blocked word", + + "livechat.deleted.message": "This message was deleted", + "livechat.messageBubble.reply.button": "Reply", + "livechat.messageBubble.copy.button": "Copy", + "livechat.messageBubble.delete.button": "Delete", + "livechat.messageBubble.mention.button": "Mention", + "livechat.messageBubble.report.button": "Report", + + "livechat.modal.delete.message.title": "Delete this message?", + "livechat.modal.delete.message.content": "This message will also be removed from your friend’s devices.", + "livechat.mention.all": "All", + "livechat.mention.all.description": "Notify everyone", + + "livechat.notification.copy.message": "Message copied", + "livechat.composebar.placeholder": "Write a message" } diff --git a/src/icons/Mention.tsx b/src/icons/Mention.tsx new file mode 100644 index 000000000..d7db88eb0 --- /dev/null +++ b/src/icons/Mention.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +const Svg = ({ fill = "#1054DE", ...props }: React.SVGProps) => ( + + + + +); + +export default Svg; diff --git a/src/icons/index.ts b/src/icons/index.ts index 6e0a13af5..d5692c650 100644 --- a/src/icons/index.ts +++ b/src/icons/index.ts @@ -52,6 +52,7 @@ export { default as FlagIcon } from './Flag'; export { default as Pencil2Icon } from './Pencil2'; export { default as FireIcon } from './Fire'; export { default as HeartIcon } from './Heart'; +export { default as MentionIcon } from './Mention'; // files export { default as AudioFile } from './files/Audio'; diff --git a/src/index.ts b/src/index.ts index 25fbb1455..0c66ebd1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export { default as AmityUiKitProvider } from '~/core/providers/UiKitProvider'; +export { default as AmityUiKitProvider } from '~/v4/core/providers/AmityUIKitProvider'; export { default as AmityUiKitFeed } from '~/social/components/Feed'; export { default as AmityUiKitSocial } from '~/social/pages/Application'; export { default as AmityUiKitChat } from '~/chat/pages/Application'; @@ -19,14 +19,26 @@ export { default as AmityExpandableText } from '~/social/components/Comment/Comm export { useSDK as useAmitySDK } from '~/core/hooks/useSDK'; // v4 -export { - DraftsPage as AmityCreateStoryPage, - StoryPage as AmityViewStoryPage, -} from '~/social/v4/pages'; +export { default as AmityUIKitManager } from '~/v4/core/AmityUIKitManager'; +export { AmityDraftStoryPage, AmityViewStoryPage } from '~/v4/social/pages'; export { CommentTray as AmityCommentTrayComponent, StoryTab as AmityStoryTabComponent, -} from '~/social/v4/components'; +} from '~/v4/social/components'; + +// Chat v4 +export { + AmityLiveChatHeader, + AmityLiveChatMessageList, + AmityLiveChatMessageReceiverView, + AmityLiveChatMessageSenderView, + AmityLiveChatMessageComposeBar, +} from '~/v4/chat/components'; + +import type { AmityMessageActionType } from '~/v4/chat/components'; +export type { AmityMessageActionType }; + +export { AmityLiveChatPage } from '~/v4/chat/pages'; // import AmityComment from './components/Comment'; // import AmityCommentComposeBar from './components/CommentComposeBar'; diff --git a/src/social/components/Comment/index.tsx b/src/social/components/Comment/index.tsx index 8a6ecb9e1..8b7d1847e 100644 --- a/src/social/components/Comment/index.tsx +++ b/src/social/components/Comment/index.tsx @@ -37,7 +37,7 @@ import { useCustomComponent } from '~/core/providers/CustomComponentsProvider'; import useCommentFlaggedByMe from '~/social/hooks/useCommentFlaggedByMe'; import useCommentPermission from '~/social/hooks/useCommentPermission'; import useCommentSubscription from '~/social/hooks/useCommentSubscription'; -import useStory from '~/social/hooks/useStory'; + import { ERROR_RESPONSE } from '~/social/constants'; const REPLIES_PER_PAGE = 5; diff --git a/src/social/components/CommentComposeBar/index.tsx b/src/social/components/CommentComposeBar/index.tsx index 6c99524b3..4ebd7e5d6 100644 --- a/src/social/components/CommentComposeBar/index.tsx +++ b/src/social/components/CommentComposeBar/index.tsx @@ -21,7 +21,6 @@ import { import { backgroundImage as UserImage } from '~/icons/User'; import { useCustomComponent } from '~/core/providers/CustomComponentsProvider'; import useImage from '~/core/hooks/useImage'; -import useStory from '~/social/hooks/useStory'; const TOTAL_MENTIONEES_LIMIT = 30; const COMMENT_LENGTH_LIMIT = 50000; diff --git a/src/social/components/CommunityInfo/UICommunityInfo.tsx b/src/social/components/CommunityInfo/UICommunityInfo.tsx index 68dc74926..eec09c233 100644 --- a/src/social/components/CommunityInfo/UICommunityInfo.tsx +++ b/src/social/components/CommunityInfo/UICommunityInfo.tsx @@ -26,7 +26,7 @@ import { import { useCustomComponent } from '~/core/providers/CustomComponentsProvider'; import millify from 'millify'; import { isNonNullable } from '~/helpers/utils'; -import { StoryTab } from '~/social/v4/components/StoryTab'; +import { StoryTab } from '~/v4/social/components'; interface UICommunityInfoProps { communityId: string; @@ -45,15 +45,8 @@ interface UICommunityInfoProps { onClickLeaveCommunity: (communityId: string) => void; canLeaveCommunity: boolean; canReviewPosts: boolean; - isStorySyncing: boolean; - haveStories: boolean; - haveStoryPermission: boolean; - isStoryErrored: boolean; - isSeen: boolean; name: string; postSetting: ValueOf; - setStoryFile: React.Dispatch>; - onClickStory: (communityId: string) => void; } const UICommunityInfo = ({ @@ -66,11 +59,6 @@ const UICommunityInfo = ({ isJoined, isOfficial, isPublic, - isStorySyncing, - isSeen, - isStoryErrored, - haveStories, - haveStoryPermission, avatarFileUrl, canEditCommunity, onEditCommunity, @@ -80,8 +68,6 @@ const UICommunityInfo = ({ canReviewPosts, name, postSetting, - setStoryFile, - onClickStory, }: UICommunityInfoProps) => { const { formatMessage } = useIntl(); @@ -156,16 +142,7 @@ const UICommunityInfo = ({ )} - onClickStory(communityId)} - onChange={setStoryFile} - /> + {isJoined && canEditCommunity && ( + ); +}; + +export default Button; diff --git a/src/v4/core/components/Button/index.ts b/src/v4/core/components/Button/index.ts new file mode 100644 index 000000000..eae9c8e3b --- /dev/null +++ b/src/v4/core/components/Button/index.ts @@ -0,0 +1 @@ +export { default as Button } from './Button'; diff --git a/src/v4/core/components/Button/ui.stories.tsx b/src/v4/core/components/Button/ui.stories.tsx new file mode 100644 index 000000000..9caf7a90a --- /dev/null +++ b/src/v4/core/components/Button/ui.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import Button from './Button'; +import { ArrowRight2Icon } from '~/icons'; + +export default { + title: 'Components/Button', + component: Button, + argTypes: { + variant: { + control: { + type: 'select', + options: ['primary', 'secondary'], + }, + }, + size: { + control: { + type: 'select', + options: ['small', 'medium', 'large'], + }, + }, + disabled: { + control: 'boolean', + }, + onClick: { + action: 'clicked', + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => + + +); + +export const Size: ComponentStory = (args) => ( + <> + + + + +); + +export const Disabled = Template.bind({}); +Disabled.args = { + disabled: true, + children: 'Disabled Button', +}; + +export const WithIcon = Template.bind({}); +WithIcon.args = { + children: , +}; diff --git a/src/v4/core/components/ConfirmModal/index.tsx b/src/v4/core/components/ConfirmModal/index.tsx new file mode 100644 index 000000000..f5d7d1e18 --- /dev/null +++ b/src/v4/core/components/ConfirmModal/index.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import Modal from '../Modal'; +import { Button } from '~/v4/core/components/Button'; +import clsx from 'clsx'; +import styles from './styles.module.css'; + +const Confirm = ({ + 'data-qa-anchor': dataQaAnchor = '', + className, + title, + content, + okText = 'Ok', + onOk, + cancelText = 'Cancel', + onCancel, + type = 'confirm', +}: any) => ( + + {type === 'confirm' && ( + + )} + + + } + onCancel={onCancel} + > +
{content}
+
+); + +let spawnNewConfirm: any; // for modfying ConfirmContainer state outside + +// rendered by provider, to allow spawning of confirm from confirm function below +export const ConfirmContainer = () => { + const [confirm, setConfirm] = useState(null); + spawnNewConfirm = (confirmData: any) => { + setConfirm(confirmData); + }; + + if (!confirm) return null; + + const closeConfirm = () => setConfirm(null); + + const attachCanceling = (fn: any) => () => { + closeConfirm(); + fn && fn(); + }; + + return ( + + ); +}; + +/* + Usage: + confirm({ + title: 'Delete post', + content: + 'This post will be permanently deleted. You’ll no longer to see and find this post. Continue?', + okText: 'Delete', + onOk: onDelete, + }); + + This interface rely on ConfirmContainer being rendered by UIKITProvider in the react tree +*/ +export const confirm = (confirmData: any) => spawnNewConfirm({ ...confirmData, type: 'confirm' }); + +export const info = (data: any) => spawnNewConfirm({ ...data, type: 'info' }); + +export default Confirm; diff --git a/src/v4/core/components/ConfirmModal/styles.module.css b/src/v4/core/components/ConfirmModal/styles.module.css new file mode 100644 index 000000000..901d3eeef --- /dev/null +++ b/src/v4/core/components/ConfirmModal/styles.module.css @@ -0,0 +1,29 @@ +.modal { + max-width: 22.5rem !important; +} + +.confirmModalContent { + padding: var(--asc-spacing-m1) var(--asc-spacing-m1) var(--asc-spacing-s2) var(--asc-spacing-m1); +} + +.footer { + display: flex; + justify-content: flex-end; +} + +.okButton { + color: var(--asc-color-secondary-default); + background: var(--asc-color-alert) !important; + border: none; +} + +.cancelButton { + margin-right: var(--asc-spacing-s1); + background-color: var(--asc-color-base-background) !important; + color: var(--asc-color-secondary-default); + border: 1px solid var(--asc-color-secondary-default) !important; +} + +.cancelButton:hover { + color: var(--asc-color-secondary-default); +} diff --git a/src/core/v4/components/Icon/Icon.tsx b/src/v4/core/components/Icon/Icon.tsx similarity index 78% rename from src/core/v4/components/Icon/Icon.tsx rename to src/v4/core/components/Icon/Icon.tsx index c5621ac13..232b8b1dc 100644 --- a/src/core/v4/components/Icon/Icon.tsx +++ b/src/v4/core/components/Icon/Icon.tsx @@ -6,11 +6,19 @@ type IconName = keyof typeof Icons; export interface IconProps { name: IconName | null; size?: number; + className?: string; style?: React.CSSProperties; onClick?: (e: React.MouseEvent) => void; } -export const Icon: React.FC = ({ name, size = 24, style, onClick, ...props }) => { +export const Icon: React.FC = ({ + name, + size = 24, + className, + style, + onClick, + ...props +}) => { const iconMap = { ...Icons, }; @@ -27,6 +35,7 @@ export const Icon: React.FC = ({ name, size = 24, style, onClick, ... data-qa-anchor={`${name}-icon`} width={size} height={size} + className={className} style={style} onClick={onClick} {...props} diff --git a/src/core/v4/components/Icon/index.ts b/src/v4/core/components/Icon/index.ts similarity index 100% rename from src/core/v4/components/Icon/index.ts rename to src/v4/core/components/Icon/index.ts diff --git a/src/v4/core/components/InputText/InsideInputText.tsx b/src/v4/core/components/InputText/InsideInputText.tsx new file mode 100644 index 000000000..533a70dd7 --- /dev/null +++ b/src/v4/core/components/InputText/InsideInputText.tsx @@ -0,0 +1,225 @@ +import React, { forwardRef, KeyboardEventHandler, MutableRefObject, RefObject, useRef, useState } from 'react'; +import { Mention, MentionsInput } from 'react-mentions'; +import clsx from 'clsx'; +import TextareaAutosize from 'react-textarea-autosize'; + +import SocialMentionItem from '~/v4/core/components/SocialMentionItem'; +import { QueryMentioneesFnType } from '~/v4/chat/hooks/useMention'; + +import styles from './styles.module.css'; +import typography from '~/v4/styles/typography.module.css'; + +interface InsideInputTextProps { + 'data-qa-anchor'?: string; + id?: string; + input?: unknown; + name?: string; + value?: string; + placeholder?: string; + multiline?: boolean; + disabled?: boolean; + invalid?: boolean; + rows?: number; + maxRows?: number; + prepend?: React.ReactNode; + append?: React.ReactNode; + className?: string; + mentionAllowed?: boolean; + isModerator?: boolean; + queryMentionees?: QueryMentioneesFnType; + loadMoreMentionees?: (query: string) => Promise; + onChange: (data: { + text: string; + plainText: string; + lastMentionText?: string; + mentions: { + plainTextIndex: number; + id: string; + display: string; + }[]; + }) => void; + onKeyPress?: (event: React.KeyboardEvent) => void; + onClear?: () => void; + onClick?: () => void; + suggestionRef?: RefObject; +} + +const InsideInputText = forwardRef( + ( + { + 'data-qa-anchor': dataQaAnchor = '', + id, + name = '', + value = '', + placeholder = '', + multiline = false, + disabled = false, + invalid = false, + rows = 1, + maxRows = 3, + prepend, + append, + onChange, + onClear, + onClick, + onKeyPress, + className, + mentionAllowed = false, + queryMentionees, + loadMoreMentionees, + isModerator, + suggestionRef, + }, + ref, + ) => { + const [items, setItems] = useState>>>([]); + const mentionRef = useRef(null); + const containerRef = useRef(null); + + const handleMentionInput: React.ComponentProps['onChange'] = ( + e, + [,], + newPlainVal, + mentions, + ) => { + // Get last item of mention and save it in upper parent component + // This way we can call loadMoreMentionees and append new values + // inside the existing array + const lastSegment = newPlainVal.split(' ').pop(); + const isMentionText = lastSegment?.[0]?.match(/^@/g) || false; + + onChange({ + text: e.target.value, + plainText: newPlainVal, + lastMentionText: isMentionText ? lastSegment : undefined, + mentions, + }); + }; + + const handleKeyDown: KeyboardEventHandler = (e) => { + if (e.key === 'Backspace' && value?.length === 0) onClear?.(); + }; + + const classNames = clsx(className, { disabled, invalid }); + + const props = { + id, + name, + value, + placeholder, + disabled, + className: classNames, + 'data-qa-anchor': dataQaAnchor, + }; + + return ( +
+ {prepend} +
+ {multiline && mentionAllowed && ( + } + rows={rows} + {...props} + className='live-chat-mention-input' + classNames={styles} + onKeyDown={(e) => handleKeyDown(e)} + onChange={handleMentionInput} + onClick={onClick} + suggestionsPortalHost={(suggestionRef?.current || mentionRef.current) as Element} + onKeyPress={(e) => onKeyPress?.(e)} + > + { + if (!queryMentionees) return callback([]); + queryMentionees(queryValue).then((result) => { + + if (!isModerator) { + callback(result); + return; + } + + const mentionItem = { + id: '@all', + display: 'All', + isLastItem: false, + }; + + const resultWithAllMention = mentionItem.display.toLowerCase().includes(queryValue.trim().toLowerCase()) ? [mentionItem] : []; + + callback(resultWithAllMention.concat(result)); + }); + }} + renderSuggestion={({ id }, search, highlightedDisplay, index, focused) => { + return ( + loadMoreMentionees?.(search)} + /> + ); + }} + displayTransform={(_id, display) => `@${display}`} + appendSpaceOnAdd + onAdd={() => {}} + /> + + )} + {multiline ? ( + !mentionAllowed && ( + } + minRows={rows} + maxRows={maxRows} + {...props} + className={clsx(styles.baseInputStyle, props.className)} + onChange={(e) => + onChange?.({ + text: e.target.value, + plainText: e.target.value, + lastMentionText: '', + mentions: [], + }) + } + onKeyDown={(e) => handleKeyDown(e)} + onClick={onClick} + /> + ) + ) : ( + } + {...props} + className={clsx(styles.baseInputStyle, props.className)} + onChange={(e) => + onChange?.({ + text: e.target.value, + plainText: e.target.value, + lastMentionText: '', + mentions: [], + }) + } + onKeyDown={(e) => handleKeyDown(e)} + onClick={onClick} + /> + )} + {append} +
+ ); + }, +); + +export default InsideInputText; diff --git a/src/v4/core/components/InputText/index.tsx b/src/v4/core/components/InputText/index.tsx new file mode 100644 index 000000000..a4dc5ee59 --- /dev/null +++ b/src/v4/core/components/InputText/index.tsx @@ -0,0 +1,47 @@ +import React, { forwardRef } from 'react'; + +import InsideInputText from './InsideInputText'; +import { QueryMentioneesFnType } from '~/social/hooks/useSocialMention'; + +export interface InputTextProps { + 'data-qa-anchor'?: string; + id?: string; + input?: unknown; + name?: string; + value?: string; + placeholder?: string; + multiline?: boolean; + disabled?: boolean; + invalid?: boolean; + rows?: number; + maxRows?: number; + prepend?: React.ReactNode; + append?: React.ReactNode; + className?: string; + mentionAllowed?: boolean; + isModerator?: boolean; + queryMentionees?: QueryMentioneesFnType; + loadMoreMentionees?: (query: string) => Promise; + onChange: (data: { + text: string; + plainText: string; + lastMentionText?: string; + mentions: { + plainTextIndex: number; + id: string; + display: string; + }[]; + }) => void; + onKeyPress?: (event: React.KeyboardEvent) => void; + onClear?: () => void; + onClick?: () => void; + suggestionRef?: React.RefObject; +} + +const InputText = forwardRef( + (props, ref) => { + return ; + }, +); + +export default InputText; diff --git a/src/v4/core/components/InputText/styles.module.css b/src/v4/core/components/InputText/styles.module.css new file mode 100644 index 000000000..089cc9497 --- /dev/null +++ b/src/v4/core/components/InputText/styles.module.css @@ -0,0 +1,108 @@ +.inputTextContainer { + position: relative; + display: flex; + flex-wrap: wrap; + min-width: 1em; + + background: var(--asc-color-base-shade4); + border: 1px solid var(--asc-color-base-shade4); + border-radius: var(--asc-border-radius-sm); + transition: background 0.2s, border-color 0.2s; + + &:focus-within { + border-color: var(--asc-color-primary); + } + + &.invalid { + border-color: var(--asc-color-alert); + } + + &.disabled { + background: var(--asc-color-base-shade4); + border-color: var(--asc-color-base-shade3); + } +} + +.baseInputStyle { + flex: 1 1 auto; + display: block; + width: 1%; + min-width: 0; + margin: 0; + padding: 0.563rem 0.563rem; + background: none; + border: none; + box-sizing: border-box; + outline: none; + font: inherit; + resize: vertical; + + &::placeholder { + font-weight: 400; + } + + &[disabled] { + background: none; + } +} + +.mentionContainer { + position: relative; + width: 100%; + + > div:first-child { + /* Put !important to override inline-css in target element */ + position: absolute !important; + width: 100%; + + /* Revert all position value */ + top: revert !important; + left: revert !important; + right: revert !important; + bottom: revert !important; + } +} + +.live-chat-mention-input { + padding: var(--asc-spacing-s1); + width: calc(100% - 1rem); + + textarea { + flex: 1 1 auto; + display: block; + width: 1%; + min-width: 0; + margin: 0; + padding: 0.563rem 0.563rem; + background: none; + border: none; + box-sizing: border-box; + outline: none; + font: inherit; + resize: vertical; + color: var(--asc-color-white); + + &::placeholder { + font-weight: 400; + } + + &[disabled] { + background: none; + } + } +} + +.live-chat-mention-input__suggestions__list { + z-index: 999; + width: 100%; + position: absolute; + transform: translateY(1.25rem); + background-color: var(--asc-color-base-shade4); + max-height: 8rem; + overflow: auto; + + /* Use !important to overide inline-css at target element */ + /* Keep a position of suggest panel strict with the top of composebar same as design */ + bottom: 2rem !important; + left: 0 !important; +} \ No newline at end of file diff --git a/src/v4/core/components/InputText/ui.stories.jsx b/src/v4/core/components/InputText/ui.stories.jsx new file mode 100644 index 000000000..bf7eb514e --- /dev/null +++ b/src/v4/core/components/InputText/ui.stories.jsx @@ -0,0 +1,65 @@ +import React, { useState } from 'react'; + +import StyledInputText from '.'; +import { useArgs } from '@storybook/client-api'; + +export default { + title: 'Ui Only/v4/Input Text', +}; + +export const UiInputText = { + render: () => { + const [{ onChange, ...rest }] = useArgs(); + const [value, setValue] = useState(''); + + const handleChange = (newVal) => { + onChange(newVal); + setValue(newVal); + }; + + return ; + }, + + name: 'Simple Input text', + + args: { + multiline: false, + invalid: false, + disabled: false, + }, + + argTypes: { + multiline: { control: { type: 'boolean' } }, + invalid: { control: { type: 'boolean' } }, + disabled: { control: { type: 'boolean' } }, + onClear: { action: 'onClear()' }, + onChange: { action: 'onChange()' }, + }, +}; + +export const UiPrependAppend = { + render: () => { + const [{ onChange, ...rest }] = useArgs(); + const [value, setValue] = useState(''); + + const handleChange = (newVal) => { + onChange(newVal); + setValue(newVal); + }; + + return ; + }, + + name: 'with Decorators', + + args: { + prepend: '', + append: '', + }, + + argTypes: { + prepend: { control: { type: 'text' } }, + append: { control: { type: 'text' } }, + onChange: { action: 'onChange()' }, + }, +}; diff --git a/src/v4/core/components/Modal/index.tsx b/src/v4/core/components/Modal/index.tsx new file mode 100644 index 000000000..5b9db4beb --- /dev/null +++ b/src/v4/core/components/Modal/index.tsx @@ -0,0 +1,55 @@ +import React, { ReactNode, useEffect, useRef } from 'react'; +import styles from './styles.module.css'; +import clsx from 'clsx'; +import Close from '~/v4/icons/Close'; + +export interface ModalProps { + 'data-qa-anchor'?: string; + size?: 'small' | ''; + className?: string; + onOverlayClick?: () => void; + onCancel?: () => void; + title?: ReactNode; + footer?: ReactNode; + children: ReactNode; + dataTheme?: string; +} + +const Modal = ({ + 'data-qa-anchor': dataQaAnchor = '', + size = '', + onOverlayClick = () => {}, + onCancel, + title, + footer, + children, +}: ModalProps) => { + const modalRef = useRef(null); + // auto focus to prevent scroll on background (when focus kept on trigger button) + useEffect(() => { + modalRef?.current?.focus(); + }, [modalRef?.current]); + + return ( +
+
+ {(title || onCancel) && ( +
+ {title} + {onCancel && } +
+ )} + +
{children}
+ {footer &&
{footer}
} +
+
+ ); +}; + +export default Modal; diff --git a/src/v4/core/components/Modal/styles.module.css b/src/v4/core/components/Modal/styles.module.css new file mode 100644 index 000000000..9a5704d40 --- /dev/null +++ b/src/v4/core/components/Modal/styles.module.css @@ -0,0 +1,72 @@ +.closeIcon { + width: 1.125rem; + height: 1.125rem; + + padding: 0 0.375rem; + cursor: pointer; + margin-left: auto; + + &.svg-inline--fa { + width: auto; + } +} + +@keyframes appear { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +.overlay { + z-index: 9999; + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + overflow-y: auto; + display: flex; + padding: var(--asc-spacing-m2) 0; + background: rgba(23, 24, 28, 0.8); + animation-duration: 0.3s; + animation-name: appear; + margin-top: 0 !important; +} +.modalWindow { + margin: auto; + background-color: var(--asc-color-base-background); + border-radius: var(--asc-border-radius-lg); + max-width: 32.5rem; + min-width: 20rem; +} + +.modalWindow:focus { + outline: none; +} + +.smallModalWindow { + width: 27.5rem !important; +} + +.header { + padding: var(--asc-spacing-m1) var(--asc-spacing-m1) var(--asc-spacing-s2) var(--asc-spacing-m1); + border-bottom: 1px solid var(--asc-color-base-shade4); + display: flex; + align-items: center; + color: var(--asc-color-white); + border-bottom: 1px solid var(--theme-palette-base-shade4); +} + +.content { + color: var(--asc-color-base-default); + padding: var(--asc-spacing-m2) var(--asc-spacing-m1); +} + +.footer { + padding: var(--asc-spacing-m2) var(--asc-spacing-s2); + padding-top: var(--asc-spacing-xxs2); +} diff --git a/src/v4/core/components/Notification/index.tsx b/src/v4/core/components/Notification/index.tsx new file mode 100644 index 000000000..4fb1a82e2 --- /dev/null +++ b/src/v4/core/components/Notification/index.tsx @@ -0,0 +1,80 @@ +import React, { ReactNode, useState } from 'react'; +import Check from '~/v4/icons/Check'; +import ExclamationCircle from '~/v4/icons/ExclamationCircle'; +import Remove from '~/v4/icons/Remove'; +import clsx from 'clsx'; +import styles from './styles.module.css'; + +const DEFAULT_NOTIFICATION_DURATION = 3000; + +interface NotificationProps { + className?: string; + content: ReactNode; + icon?: ReactNode; +} + +interface NotificationData { + id: number; + content: ReactNode; + icon?: ReactNode; +} + +type NotificationInput = Omit & { duration?: number }; + +const Notification = ({ className, content, icon }: NotificationProps) => ( +
+ {icon} {content} +
+); + +let spawnNewNotification: (notificationData: NotificationInput) => void; // for modifying NotificationContainer state outside + +// rendered by provider, to allow spawning of notification from notification function below +export const NotificationsContainer = () => { + const [notifications, setNotifications] = useState([]); + + const removeNotification = (id: number) => + setNotifications && + setNotifications((prevNotifications) => + prevNotifications.filter((notification) => notification.id !== id), + ); + + spawnNewNotification = ({ + duration = DEFAULT_NOTIFICATION_DURATION, + ...notificationData + }: NotificationInput) => { + const id = Date.now(); + + setNotifications([{ id, ...notificationData }, ...notifications]); + + setTimeout(() => removeNotification(id), duration); + }; + + return ( +
+ {notifications.map((notificationData) => { + return ; + })} +
+ ); +}; + +/* + Usage: + notification.success({ + content: 'Report Sent', + }); + + This interface rely on NotificationsContainer being rendered by UIKITProvider in the react tree +*/ +export const notification = { + success: (data: Omit) => + spawnNewNotification({ ...data, icon: }), + info: (data: Omit) => + spawnNewNotification({ ...data, icon: }), + error: (data: Omit) => + spawnNewNotification({ ...data, icon: }), + show: (data: Omit) => spawnNewNotification(data), +}; + +export default Notification; diff --git a/src/v4/core/components/Notification/styles.module.css b/src/v4/core/components/Notification/styles.module.css new file mode 100644 index 000000000..ab5a7aafa --- /dev/null +++ b/src/v4/core/components/Notification/styles.module.css @@ -0,0 +1,44 @@ +/* styles.module.css */ +.icon { + width: 1.125rem; + height: 1.125rem; + margin-right: 8px; +} + +.notifications { + position: fixed; + padding-top: 50px; + top: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + align-items: center; + z-index: 99999; + pointer-events: none; +} + +.notificationContainer { + width: 480px; + padding: 8px 30px; + display: flex; + justify-content: center; + align-items: center; + color: white; + border-radius: 4px; + margin-bottom: 10px; + animation-duration: 0.3s; + animation-name: appear; + pointer-events: auto; + background-color: var(--asc-color-base-shade4); +} + +@keyframes appear { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} diff --git a/src/v4/core/components/Popover/index.tsx b/src/v4/core/components/Popover/index.tsx new file mode 100644 index 000000000..0c6a26452 --- /dev/null +++ b/src/v4/core/components/Popover/index.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import styles from './styles.module.css'; +import clsx from 'clsx'; +import { Popover as ReactTinyPopover } from 'react-tiny-popover'; + +export type ReactPopoverProps = { + fixed?: boolean; + isOpen: boolean; + children: React.ReactNode; +} & React.ComponentProps; + +const Popover = ({ isOpen, content, fixed = false, children, ...rest }: ReactPopoverProps) => { + return ( + + {children} + + ); +}; + +export default Popover; diff --git a/src/v4/core/components/Popover/styles.module.css b/src/v4/core/components/Popover/styles.module.css new file mode 100644 index 000000000..f03cefd17 --- /dev/null +++ b/src/v4/core/components/Popover/styles.module.css @@ -0,0 +1,11 @@ +.popover { + width: 12.5rem; + background-color: var(--asc-color-secondary-shade4); + z-index: 10000; + padding: var(--asc-spacing-s2) 0; + border-radius: var(--asc-border-radius-lg); + + &.fixed { + position: fixed !important; + } +} diff --git a/src/v4/core/components/SocialMentionItem/index.tsx b/src/v4/core/components/SocialMentionItem/index.tsx new file mode 100644 index 000000000..b9933079e --- /dev/null +++ b/src/v4/core/components/SocialMentionItem/index.tsx @@ -0,0 +1,154 @@ +import React, { useCallback, useEffect, useRef } from 'react'; +import clsx from 'clsx'; +import UserAvatar from '~/v4/chat/components/UserAvatar'; +import { backgroundImage as userBackgroundImage } from '~/icons/User'; +import BanIcon from '~/icons/Ban'; +import useObserver from '~/core/hooks/useObserver'; +import useUser from '~/core/hooks/useUser'; +import useImage from '~/core/hooks/useImage'; +import styles from './styles.module.css'; +import { MentionIcon } from '~/icons'; +import { SIZE_ALIAS } from '~/core/hooks/useSize'; +import { FormattedMessage } from 'react-intl'; +import { Typography } from '../index'; + +interface SocialMentionItemProps { + id: string; + focused: boolean; + isLastItem: boolean; + loadMore?: () => void; + rootEl: React.MutableRefObject; + containerRef: React.MutableRefObject; +} + +interface UserMentionItemProps { + id: string; + entry: IntersectionObserverEntry | null; + onMouseEnter: (e: React.MouseEvent, isBanned: boolean | undefined) => void; + focused: boolean; + isLastItem: boolean; + loadMore?: () => void; + targetRef: React.RefObject; + containerRef: React.RefObject; +} + +const UserMentionItem = ({ + id, + entry, + onMouseEnter, + focused, + isLastItem, + loadMore, + targetRef, + containerRef, +}: UserMentionItemProps) => { + const user = useUser(id); + const avatarFileUrl = useImage({ fileId: user?.avatarFileId, imageSize: 'small' }); + + useEffect(() => { + if (targetRef && entry?.isIntersecting) { + loadMore?.(); + } + }, [targetRef, entry?.isIntersecting, loadMore]); + + return ( +
onMouseEnter(e, user?.isGlobalBanned)} + > + +
+ {user?.displayName} +
+
{user?.isGlobalBanned ? : null}
+
+ ); +}; + +const CustomMentionItem = ({ + id, + onMouseEnter, + focused, + isLastItem, + targetRef, + containerRef, +}: Omit) => { + return ( +
onMouseEnter(e, false)} + > +
+ +
+ + + +
+
+
+ + + +
+
+ ); +}; + +const SocialMentionItem = ({ + id, + focused, + isLastItem, + loadMore, + rootEl, + containerRef, +}: SocialMentionItemProps) => { + const targetRef = useRef(null); + const entry = useObserver(targetRef?.current, { + root: rootEl?.current?.childNodes[0] as Element, + }); + + // Slow performance, need more pristine approach + const onMouseEnter = useCallback((e, isBanned) => { + if (isBanned) { + e.target.parentNode.style.cursor = 'not-allowed'; + e.target.parentNode.style['pointer-events'] = 'none'; + } + }, []); + + if (id === '@all') { + return ( + + ); + } + + return ( + + ); +}; + +export default SocialMentionItem; diff --git a/src/v4/core/components/SocialMentionItem/styles.module.css b/src/v4/core/components/SocialMentionItem/styles.module.css new file mode 100644 index 000000000..1c78bda54 --- /dev/null +++ b/src/v4/core/components/SocialMentionItem/styles.module.css @@ -0,0 +1,38 @@ +.mentionItem { + display: flex; + align-items: center; + padding: 0.313rem 0.938rem; + font-weight: 600; + background-color: var(--asc-color-base-shade4); +} + +.mentionItem.isBanned { + pointer-events: none; + cursor: not-allowed; +} + +.mentionItem:hover { + background-color: var(--asc-color-base-background); +} + +.userDisplayName { + margin-left: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--asc-color-white); +} + +.mentionAllDescription { + display: flex; + align-items: center; + color: var(--asc-color-base-shade2); +} + +.mentionAll { + justify-content: space-between; + div { + display: flex; + align-items: center; + } +} \ No newline at end of file diff --git a/src/v4/core/components/Typography/Typography.tsx b/src/v4/core/components/Typography/Typography.tsx new file mode 100644 index 000000000..fbc295f91 --- /dev/null +++ b/src/v4/core/components/Typography/Typography.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import clsx from 'clsx'; +import typography from '~/v4/styles/typography.module.css'; + +interface TypographyProps { + children: React.ReactNode; + className?: string; +} + +const Typography: React.FC & { + Heading: React.FC; + Title: React.FC; + Subtitle: React.FC; + Body: React.FC; + BodyBold: React.FC; + Caption: React.FC; + CaptionBold: React.FC; +} = ({ children, className = '' }) => { + return
{children}
; +}; + +Typography.Heading = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +Typography.Title = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +Typography.Subtitle = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +Typography.Body = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +Typography.BodyBold = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +Typography.Caption = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +Typography.CaptionBold = ({ children, className = '' }) => { + return ( +

+ {children} +

+ ); +}; + +export default Typography; diff --git a/src/v4/core/components/Typography/index.ts b/src/v4/core/components/Typography/index.ts new file mode 100644 index 000000000..1b67c1953 --- /dev/null +++ b/src/v4/core/components/Typography/index.ts @@ -0,0 +1 @@ +export { default as Typography } from './Typography'; diff --git a/src/v4/core/components/Typography/ui.stories.tsx b/src/v4/core/components/Typography/ui.stories.tsx new file mode 100644 index 000000000..6d21fbb86 --- /dev/null +++ b/src/v4/core/components/Typography/ui.stories.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import Typography from './Typography'; + +export default { + title: 'v4/Core/Components/Typography', + component: Typography, +} as ComponentMeta; + +const TypographyOverview: React.FC = () => ( +
+ Heading + Title + Subtitle + Body text + Bold body text + Caption + Bold caption +
+); + +export const Overview: ComponentStory = () => ; + +export const Heading: ComponentStory = (args) => ( + +); +Heading.args = { + children: 'Heading', +}; + +export const Title: ComponentStory = (args) => ( + +); +Title.args = { + children: 'Title', +}; + +export const Subtitle: ComponentStory = (args) => ( + +); +Subtitle.args = { + children: 'Subtitle', +}; + +export const Body: ComponentStory = (args) => ; +Body.args = { + children: 'Body text', +}; + +export const BodyBold: ComponentStory = (args) => ( + +); +BodyBold.args = { + children: 'Bold body text', +}; + +export const Caption: ComponentStory = (args) => ( + +); +Caption.args = { + children: 'Caption', +}; + +export const CaptionBold: ComponentStory = (args) => ( + +); +CaptionBold.args = { + children: 'Bold caption', +}; diff --git a/src/core/v4/components/index.ts b/src/v4/core/components/index.ts similarity index 100% rename from src/core/v4/components/index.ts rename to src/v4/core/components/index.ts diff --git a/src/v4/core/providers/AmityUIKitProvider.tsx b/src/v4/core/providers/AmityUIKitProvider.tsx new file mode 100644 index 000000000..385417e84 --- /dev/null +++ b/src/v4/core/providers/AmityUIKitProvider.tsx @@ -0,0 +1,166 @@ +import '../../../core/providers/UiKitProvider/inter.css'; +import './index.css'; +import '../../styles/global.css'; +import amityUKitConfig from '../../../../amity-uikit.config.json'; + +import React, { useEffect, useMemo, useState } from 'react'; +import useUser from '~/core/hooks/useUser'; + +import SDKConnectorProvider from '~/core/providers/SDKConnectorProvider'; +import { SDKContext } from '~/core/providers/SDKProvider'; +import PostRendererProvider from '~/social/providers/PostRendererProvider'; +import NavigationProvider from '~/social/providers/NavigationProvider'; + +import ConfigProvider from '~/social/providers/ConfigProvider'; +import { ConfirmContainer } from '~/v4/core/components/ConfirmModal'; +import { NotificationsContainer } from '~/v4/core/components/Notification'; + +import Localization from '~/core/providers/UiKitProvider/Localization'; + +import { ThemeProvider as StyledThemeProvider } from 'styled-components'; +import buildGlobalTheme from '~/core/providers/UiKitProvider/theme'; +import { Config, CustomizationProvider } from './CustomizationProvider'; +import { ThemeProvider } from './ThemeProvider'; +import { PageBehaviorProvider } from './PageBehaviorProvider'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { UIStyles } from '~/core/providers/UiKitProvider/styles'; +import AmityUIKitManager from '../AmityUIKitManager'; + +export type AmityUIKitConfig = typeof amityUKitConfig; + +interface AmityUIKitProviderProps { + apiKey: string; + apiRegion: string; + apiEndpoint?: { + http?: string; + mqtt?: string; + }; + authToken?: string; + userId: string; + displayName: string; + postRendererConfig?: any; + theme?: Record; + children?: React.ReactNode; + socialCommunityCreationButtonVisible?: boolean; + actionHandlers?: { + onChangePage?: (data: { type: string; [x: string]: string | boolean }) => void; + onClickCategory?: (categoryId: string) => void; + onClickCommunity?: (communityId: string) => void; + onClickUser?: (userId: string) => void; + onCommunityCreated?: (communityId: string) => void; + onEditCommunity?: (communityId: string, options?: { tab?: string }) => void; + onEditUser?: (userId: string) => void; + onMessageUser?: (userId: string) => void; + }; + pageBehavior?: { + onCloseAction?: () => void; + onClickHyperLink?: () => void; + }; + onConnectionStatusChange?: (state: Amity.SessionStates) => void; + onConnected?: () => void; + onDisconnected?: () => void; + configs?: AmityUIKitConfig; +} + +const AmityUIKitProvider: React.FC = ({ + apiKey, + apiRegion, + apiEndpoint, + authToken, + userId, + displayName, + postRendererConfig, + theme = {}, + children /* TODO localization */, + socialCommunityCreationButtonVisible, + pageBehavior, + onConnectionStatusChange, + onDisconnected, + configs, +}) => { + const queryClient = new QueryClient(); + const [client, setClient] = useState(null); + const currentUser = useUser(userId); + + const sdkContextValue = useMemo( + () => ({ + client, + currentUserId: userId || undefined, + userRoles: currentUser?.roles || [], + }), + [client, userId, currentUser?.roles], + ); + + useEffect(() => { + const setup = async () => { + try { + // Set up the AmityUIKitManager + AmityUIKitManager.setup({ apiKey, apiRegion, apiEndpoint }); + + // Register the device and get the client instance + await AmityUIKitManager.registerDevice( + userId, + displayName || userId, + { + sessionWillRenewAccessToken: (renewal) => { + // Handle access token renewal + if (authToken) { + renewal.renewWithAuthToken(authToken); + } else { + renewal.renew(); + } + }, + }, + onConnectionStatusChange, + onDisconnected, + ); + + const newClient = AmityUIKitManager.getClient(); + setClient(newClient); + } catch (error) { + console.error('Error setting up AmityUIKitManager:', error); + } + }; + + setup(); + }, [userId, displayName, authToken, onConnectionStatusChange, onDisconnected]); + + if (!client) return null; + + return ( + + + + + + + + + + + + + {children} + + + + + + + + + + + + + + + ); +}; + +export default AmityUIKitProvider; diff --git a/src/v4/core/providers/CustomizationProvider.tsx b/src/v4/core/providers/CustomizationProvider.tsx new file mode 100644 index 000000000..a189143ba --- /dev/null +++ b/src/v4/core/providers/CustomizationProvider.tsx @@ -0,0 +1,314 @@ +import React, { createContext, useContext, useState, useEffect } from 'react'; + +interface CustomizationContextValue { + config: Config | null; + parseConfig: (config: Config) => void; + isExcluded: (path: string) => boolean; + getConfig: (path: string) => Record; +} + +type Theme = { + light: { + primary_color: string; + secondary_color: string; + base_color: string; + base_shade1_color: string; + base_shade2_color: string; + base_shade3_color: string; + base_shade4_color: string; + alert_color: string; + background_color: string; + }; + dark: { + primary_color: string; + secondary_color: string; + base_color: string; + base_shade1_color: string; + base_shade2_color: string; + base_shade3_color: string; + base_shade4_color: string; + alert_color: string; + background_color: string; + }; +}; + +export interface Config { + preferred_theme?: 'light' | 'dark' | 'default'; + theme?: { + light?: Theme['light']; + dark?: Theme['dark']; + }; + excludes?: string[]; + customizations?: { + 'select_target_page/*/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + }; + title?: string; + }; + 'select_target_page/*/back_button'?: { + back_icon?: string; + }; + 'camera_page/*/*'?: { + resolution?: string; + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + 'camera_page/*/close_button'?: { + close_icon?: string; + background_color?: string; + }; + 'create_story_page/*/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + 'create_story_page/*/back_button'?: { + back_icon?: string; + background_color?: string; + }; + 'create_story_page/*/aspect_ratio_button'?: { + aspect_ratio_icon?: string; + background_color?: string; + }; + 'create_story_page/*/story_hyperlink_button'?: { + hyperlink_button_icon?: string; + background_color?: string; + }; + 'create_story_page/*/hyper_link'?: { + hyper_link_icon?: string; + background_color?: string; + }; + 'create_story_page/*/share_story_button'?: { + share_icon?: string; + background_color?: string; + hide_avatar?: boolean; + }; + 'story_page/*/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + 'story_page/*/progress_bar'?: { + progress_color?: string; + background_color?: string; + }; + 'story_page/*/overflow_menu'?: { + overflow_menu_icon?: string; + }; + 'story_page/*/close_button'?: { + close_icon?: string; + }; + 'story_page/*/story_impression_button'?: { + impression_icon?: string; + }; + 'story_page/*/story_comment_button'?: { + comment_icon?: string; + background_color?: string; + }; + 'story_page/*/story_reaction_button'?: { + reaction_icon?: string; + background_color?: string; + }; + 'story_page/*/create_new_story_button'?: { + create_new_story_icon?: string; + background_color?: string; + }; + 'story_page/*/speaker_button'?: { + mute_icon?: string; + unmute_icon?: string; + background_color?: string; + }; + '*/edit_comment_component/*'?: { + theme?: { + light_theme?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + '*/edit_comment_component/cancel_button'?: { + cancel_icon?: string; + cancel_button_text?: string; + background_color?: string; + }; + '*/edit_comment_component/save_button'?: { + save_icon?: string; + save_button_text?: string; + background_color?: string; + }; + '*/hyper_link_config_component/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + '*/hyper_link_config_component/done_button'?: { + done_icon?: string; + done_button_text?: string; + background_color?: string; + }; + '*/hyper_link_config_component/cancel_button'?: { + cancel_icon?: string; + cancel_button_text?: string; + }; + '*/comment_tray_component/*'?: { + component_theme?: { + light_theme?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + '*/story_tab_component/*'?: { + component_theme?: { + light_theme?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + '*/story_tab_component/story_ring'?: { + progress_color?: string[]; + background_color?: string; + }; + '*/story_tab_component/create_new_story_button'?: { + create_new_story_icon?: string; + background_color?: string; + }; + '*/*/close_button'?: { + close_icon?: string; + }; + 'live_chat/*/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + dark?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + 'live_chat/chat_header/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + dark?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + 'live_chat/message_list/*'?: { + theme?: { + light?: { + primary_color?: string; + secondary_color?: string; + }; + dark?: { + primary_color?: string; + secondary_color?: string; + }; + }; + }; + 'live_chat/message_composer/*'?: { + placeholder_text?: 'Write a message'; + }; + }; +} + +const CustomizationContext = createContext({ + config: null, + parseConfig: () => {}, + isExcluded: () => false, + getConfig: () => ({}), +}); + +export const useCustomization = () => { + const context = useContext(CustomizationContext); + if (!context) { + throw new Error('useCustomization must be used within a CustomizationProvider'); + } + return context; +}; + +interface CustomizationProviderProps { + children: React.ReactNode; + initialConfig: Config; +} + +export const CustomizationProvider: React.FC = ({ + children, + initialConfig, +}) => { + const [config, setConfig] = useState(null); + + useEffect(() => { + if (validateConfig(initialConfig)) { + parseConfig(initialConfig); + } else { + console.error('Invalid configuration provided to CustomizationProvider'); + } + }, [initialConfig]); + + const validateConfig = (config: Config): boolean => { + // Check if mandatory fields are present + if ( + !config?.preferred_theme || + !config?.theme || + !config?.excludes || + !config?.customizations + ) { + return false; + } + + return true; + }; + + const parseConfig = (newConfig: Config) => { + setConfig(newConfig); + }; + + const isExcluded = (path: string) => { + if (!config) return false; + return !!config.excludes?.some((exclude) => { + const regex = new RegExp(`^${exclude.replace(/\*/g, '.*')}$`); + return regex.test(path); + }); + }; + + const getConfig = (path: string) => { + if (!config?.customizations) return {}; + return config?.customizations[path as keyof Config['customizations']] || {}; + }; + + const contextValue: CustomizationContextValue = { + config, + parseConfig, + isExcluded, + getConfig, + }; + + return ( + {children} + ); +}; diff --git a/src/social/v4/providers/PageBehaviorProvider.tsx b/src/v4/core/providers/PageBehaviorProvider.tsx similarity index 54% rename from src/social/v4/providers/PageBehaviorProvider.tsx rename to src/v4/core/providers/PageBehaviorProvider.tsx index 8103ea4d6..eeefde75f 100644 --- a/src/social/v4/providers/PageBehaviorProvider.tsx +++ b/src/v4/core/providers/PageBehaviorProvider.tsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { useMemo, useContext } from 'react'; import { useNavigation } from '~/social/providers/NavigationProvider'; interface NavigationBehavior { - closeAction(): void; + onCloseAction(): void; + onClickHyperLink(): void; } interface PageBehavior { @@ -13,35 +14,38 @@ const PageBehaviorContext = React.createContext(undefi interface PageBehaviorProviderProps { children: React.ReactNode; - customNavigationBehavior?: Partial; + pageBehavior?: Partial; } export const PageBehaviorProvider: React.FC = ({ children, - customNavigationBehavior = {}, + pageBehavior = {}, }) => { const { onBack } = useNavigation(); const defaultNavigationBehavior: NavigationBehavior = { - closeAction: () => { + onCloseAction: () => { onBack(); }, - }; - const mergedNavigationBehavior: NavigationBehavior = { - ...defaultNavigationBehavior, - ...customNavigationBehavior, + onClickHyperLink: () => {}, }; - const pageBehavior: PageBehavior = { - navigationBehavior: mergedNavigationBehavior, - }; + const pageBehaviorMemo = useMemo(() => { + const mergedNavigationBehavior: NavigationBehavior = { + ...defaultNavigationBehavior, + ...pageBehavior, + }; + return { + navigationBehavior: mergedNavigationBehavior, + }; + }, []); return ( - {children} + {children} ); }; export const usePageBehavior = () => { - const pageBehavior = React.useContext(PageBehaviorContext); + const pageBehavior = useContext(PageBehaviorContext); if (!pageBehavior) { throw new Error('usePageBehavior must be used within a PageBehaviorProvider'); } diff --git a/src/v4/core/providers/ThemeProvider.tsx b/src/v4/core/providers/ThemeProvider.tsx new file mode 100644 index 000000000..dc0b19a5e --- /dev/null +++ b/src/v4/core/providers/ThemeProvider.tsx @@ -0,0 +1,221 @@ +import React, { createContext, useEffect, useState } from 'react'; +import { lighten, parseToHsl, darken, hslToColorString } from 'polished'; + +const SHADE_PERCENTAGES = [0.25, 0.4, 0.5, 0.75]; + +const setCSSVariable = (variable: string, value?: string) => { + if (!value) return; + document.documentElement.style.setProperty(variable, value); +}; + +const generateShades = (hexColor: string, isDarkMode = false): string[] => { + const hslColor = parseToHsl(hexColor); + + const shades = SHADE_PERCENTAGES.map((percentage) => { + if (isDarkMode) { + return darken(percentage, hslToColorString(hslColor)); + } else { + return lighten(percentage, hslToColorString(hslColor)); + } + }); + + return shades; +}; + +export const ThemeContext = createContext<{ + currentTheme: 'light' | 'dark'; + toggleTheme: () => void; +}>({ + currentTheme: 'light', + toggleTheme: () => {}, +}); + +const defaultConfig = { + preferred_theme: 'default', + theme: { + light: { + primary_color: '#1054DE', + secondary_color: '#292B32', + base_color: '#292b32', + base_shade1_color: '#636878', + base_shade2_color: '#898e9e', + base_shade3_color: '#a5a9b5', + base_shade4_color: '#ebecef', + alert_color: '#FA4D30', + background_color: '#FFFFFF', + }, + dark: { + primary_color: '#1054DE', + secondary_color: '#292B32', + base_color: '#ebecef', + base_shade1_color: '#a5a9b5', + base_shade2_color: '#6e7487', + base_shade3_color: '#40434e', + base_shade4_color: '#292b32', + alert_color: '#FA4D30', + background_color: '#191919', + }, + }, + excludes: [], + customizations: { + 'select_target_page/*/*': { + theme: {}, + title: 'Share to', + }, + 'select_target_page/*/back_button': { + back_icon: 'back.png', + }, + 'camera_page/*/*': { + resolution: '720p', + }, + 'camera_page/*/close_button': { + close_icon: 'close.png', + }, + 'create_story_page/*/*': {}, + 'create_story_page/*/back_button': { + back_icon: 'back.png', + background_color: '#1234DB', + }, + 'create_story_page/*/aspect_ratio_button': { + aspect_ratio_icon: 'aspect_ratio.png', + background_color: '1234DB', + }, + 'create_story_page/*/story_hyperlink_button': { + hyperlink_button_icon: 'hyperlink_button.png', + background_color: '#1234DB', + }, + 'create_story_page/*/hyper_link': { + hyper_link_icon: 'hyper_link.png', + background_color: '#1234DB', + }, + 'create_story_page/*/share_story_button': { + share_icon: 'share_story_button.png', + background_color: '#1234DB', + hide_avatar: false, + }, + 'story_page/*/*': {}, + 'story_page/*/progress_bar': { + progress_color: '#UD1234', + background_color: '#AB1234', + }, + 'story_page/*/overflow_menu': { + overflow_menu_icon: 'threeDot.png', + }, + 'story_page/*/close_button': { + close_icon: 'close.png', + }, + 'story_page/*/story_impression_button': { + impression_icon: 'impressionIcon.png', + }, + 'story_page/*/story_comment_button': { + comment_icon: 'comment.png', + background_color: '#2b2b2b', + }, + 'story_page/*/story_reaction_button': { + reaction_icon: 'like.png', + background_color: '#2b2b2b', + }, + 'story_page/*/create_new_story_button': { + create_new_story_icon: 'plus.png', + background_color: '#ffffff', + }, + 'story_page/*/speaker_button': { + mute_icon: 'mute.png', + unmute_icon: 'unmute.png', + background_color: '#1243EE', + }, + '*/edit_comment_component/*': { + theme: {}, + }, + '*/edit_comment_component/cancel_button': { + cancel_icon: '', + cancel_button_text: 'cancel', + background_color: '#1243EE', + }, + '*/edit_comment_component/save_button': { + save_icon: '', + save_button_text: 'Save', + background_color: '#1243EE', + }, + '*/hyper_link_config_component/*': { + theme: {}, + }, + '*/hyper_link_config_component/done_button': { + done_icon: '', + done_button_text: 'Done', + background_color: '#1243EE', + }, + '*/hyper_link_config_component/cancel_button': { + cancel_icon: '', + cancel_button_text: 'Cancel', + }, + '*/comment_tray_component/*': { + theme: {}, + }, + '*/story_tab_component/*': {}, + '*/story_tab_component/story_ring': { + progress_color: ['#339AF9', '#78FA58'], + background_color: '#AB1234', + }, + '*/story_tab_component/create_new_story_button': { + create_new_story_icon: 'plus.png', + background_color: '#1243EE', + }, + '*/*/close_button': { + close_icon: 'close.png', + }, + }, +}; + +export const ThemeProvider: React.FC<{ initialConfig?: any }> = ({ children, initialConfig }) => { + const config = initialConfig || defaultConfig; + + const [currentTheme, setCurrentTheme] = useState<'light' | 'dark'>('light'); + + useEffect(() => { + const primaryColorShades = generateShades(config.light.primary_color); + const secondaryColorShades = generateShades(config.light.secondary_color); + + setCSSVariable('--asc-color-primary-default', config.light.primary_color); + setCSSVariable('--asc-color-primary-shade1', primaryColorShades[0]); + setCSSVariable('--asc-color-primary-shade2', primaryColorShades[1]); + setCSSVariable('--asc-color-primary-shade3', primaryColorShades[2]); + setCSSVariable('--asc-color-primary-shade4', primaryColorShades[3]); + + setCSSVariable('--asc-color-secondary-default', config.light.secondary_color); + setCSSVariable('--asc-color-secondary-shade1', secondaryColorShades[0]); + setCSSVariable('--asc-color-secondary-shade2', secondaryColorShades[1]); + setCSSVariable('--asc-color-secondary-shade3', secondaryColorShades[2]); + setCSSVariable('--asc-color-secondary-shade4', secondaryColorShades[3]); + + setCSSVariable('--asc-color-base-default', config.light?.base_color); + setCSSVariable('--asc-color-base-shade1', config.light?.base_shade1_color); + setCSSVariable('--asc-color-base-shade2', config.light?.base_shade2_color); + setCSSVariable('--asc-color-base-shade3', config.light?.base_shade3_color); + setCSSVariable('--asc-color-base-shade4', config.light?.base_shade4_color); + + setCSSVariable('--asc-color-alert', config.light?.alert_color); + setCSSVariable('--asc-color-background', config.light?.background_color); + + setCSSVariable('--asc-color-primary-dark', config.dark?.primary_color); + }, [currentTheme, config]); + + useEffect(() => { + if (config.preferred_theme === 'default') { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = (e: MediaQueryListEvent) => { + setCurrentTheme(e.matches ? 'dark' : 'light'); + }; + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + } + }, [config.preferred_theme]); + + const toggleTheme = () => { + setCurrentTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light')); + }; + + return ( + {children} + ); +}; diff --git a/src/v4/core/providers/UIStyles.module.css b/src/v4/core/providers/UIStyles.module.css new file mode 100644 index 000000000..423afd13c --- /dev/null +++ b/src/v4/core/providers/UIStyles.module.css @@ -0,0 +1,27 @@ +.uiStyles { + font-family: var(--typography-body-font-family); + font-size: var(--typography-body-font-size); + font-weight: var(--typography-body-font-weight); + line-height: var(--typography-body-line-height); + color: var(--palette-base-main); + width: 100%; + height: 100%; + overflow: hidden; + } + + .uiStyles input, + .uiStyles div { + box-sizing: border-box; + } + + .uiStyles * { + font-size: var(--typography-body-font-size); + line-height: 1.5; + } + + .uiStyles pre { + font-family: var(--typography-body-font-family); + font-size: var(--typography-body-font-size); + font-weight: var(--typography-body-font-weight); + line-height: var(--typography-body-line-height); + } \ No newline at end of file diff --git a/src/v4/core/providers/index.css b/src/v4/core/providers/index.css new file mode 100644 index 000000000..19b92c9fd --- /dev/null +++ b/src/v4/core/providers/index.css @@ -0,0 +1,56 @@ +@keyframes react-loading-skeleton { + 100% { + transform: translateX(100%); + } + } + + .react-loading-skeleton { + --base-color: #ebebeb; + --highlight-color: #f5f5f5; + --animation-duration: 1.5s; + --animation-direction: normal; + --pseudo-element-display: block; /* Enable animation */ + + background-color: var(--base-color); + + width: 100%; + border-radius: 0.25rem; + display: inline-flex; + line-height: 1; + + position: relative; + user-select: none; + overflow: hidden; + z-index: 1; /* Necessary for overflow: hidden to work correctly in Safari */ + } + + .react-loading-skeleton::after { + content: ' '; + display: var(--pseudo-element-display); + position: absolute; + top: 0; + left: 0; + right: 0; + height: 100%; + background-repeat: no-repeat; + background-image: linear-gradient( + 90deg, + var(--base-color), + var(--highlight-color), + var(--base-color) + ); + transform: translateX(-100%); + + animation-name: react-loading-skeleton; + animation-direction: var(--animation-direction); + animation-duration: var(--animation-duration); + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + } + + @media (prefers-reduced-motion) { + .react-loading-skeleton { + --pseudo-element-display: none; /* Disable animation */ + } + } + \ No newline at end of file diff --git a/src/v4/core/providers/index.ts b/src/v4/core/providers/index.ts new file mode 100644 index 000000000..74c545319 --- /dev/null +++ b/src/v4/core/providers/index.ts @@ -0,0 +1 @@ +export { default as AmityUIKitProvider } from './AmityUIKitProvider'; diff --git a/src/v4/icons/ArrowTop.tsx b/src/v4/icons/ArrowTop.tsx new file mode 100644 index 000000000..44bddd8af --- /dev/null +++ b/src/v4/icons/ArrowTop.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const ArrowTop = (props: React.SVGProps) => ( + + + +); + +export default ArrowTop; diff --git a/src/v4/icons/Badge.tsx b/src/v4/icons/Badge.tsx new file mode 100644 index 000000000..b8f5029fd --- /dev/null +++ b/src/v4/icons/Badge.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const Badge = (props: React.SVGProps) => ( + + + + +); + +export default Badge; diff --git a/src/v4/icons/Bin.tsx b/src/v4/icons/Bin.tsx new file mode 100644 index 000000000..5324ad518 --- /dev/null +++ b/src/v4/icons/Bin.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const Bin = (props: React.SVGProps) => ( + + + +); + +export default Bin; diff --git a/src/v4/icons/Check.tsx b/src/v4/icons/Check.tsx new file mode 100644 index 000000000..fc6dd8749 --- /dev/null +++ b/src/v4/icons/Check.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const Check = (props: React.SVGProps) => ( + + + +); + +export default Check; diff --git a/src/v4/icons/Close.tsx b/src/v4/icons/Close.tsx new file mode 100644 index 000000000..ae74d4095 --- /dev/null +++ b/src/v4/icons/Close.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +const Close = (props: React.SVGProps) => ( + + + +); + +export default Close; diff --git a/src/v4/icons/Community.tsx b/src/v4/icons/Community.tsx new file mode 100644 index 000000000..ced2fc1b7 --- /dev/null +++ b/src/v4/icons/Community.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +const Community = (props: React.SVGProps) => ( + + + + +); + +export default Community; + +export const backgroundImage = `url("data:image/svg+xml,%3Csvg width='100%25' height='100%25' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='40' height='40' rx='20' fill='%23D9E5FC'/%3E%3Cpath d='M19.8462 12C20.7625 12 21.6413 12.356 22.2893 12.9898C22.9373 13.6235 23.3013 14.4831 23.3013 15.3793C23.3013 16.2756 22.9373 17.1351 22.2893 17.7688C21.6413 18.4026 20.7625 18.7586 19.8462 18.7586C18.9298 18.7586 18.051 18.4026 17.403 17.7688C16.755 17.1351 16.391 16.2756 16.391 15.3793C16.391 14.4831 16.755 13.6235 17.403 12.9898C18.051 12.356 18.9298 12 19.8462 12ZM12.9359 14.4138C13.4887 14.4138 14.0021 14.5586 14.4463 14.8193C14.2982 16.2 14.7128 17.571 15.5618 18.6428C15.0682 19.5697 14.081 20.2069 12.9359 20.2069C12.1504 20.2069 11.3972 19.9017 10.8418 19.3585C10.2864 18.8153 9.97436 18.0786 9.97436 17.3103C9.97436 16.5421 10.2864 15.8054 10.8418 15.2622C11.3972 14.719 12.1504 14.4138 12.9359 14.4138ZM26.7564 14.4138C27.5419 14.4138 28.2951 14.719 28.8505 15.2622C29.4059 15.8054 29.7179 16.5421 29.7179 17.3103C29.7179 18.0786 29.4059 18.8153 28.8505 19.3585C28.2951 19.9017 27.5419 20.2069 26.7564 20.2069C25.6113 20.2069 24.6241 19.5697 24.1305 18.6428C24.9795 17.571 25.3941 16.2 25.246 14.8193C25.6903 14.5586 26.2036 14.4138 26.7564 14.4138ZM13.4295 24.3103C13.4295 22.3117 16.3022 20.6897 19.8462 20.6897C23.3901 20.6897 26.2628 22.3117 26.2628 24.3103V26H13.4295V24.3103ZM8 26V24.5517C8 23.2097 9.86577 22.08 12.3929 21.7517C11.8105 22.4083 11.4551 23.3159 11.4551 24.3103V26H8ZM31.6923 26H28.2372V24.3103C28.2372 23.3159 27.8818 22.4083 27.2994 21.7517C29.8265 22.08 31.6923 23.2097 31.6923 24.5517V26Z' fill='white'/%3E%3C/svg%3E%0A");`; diff --git a/src/v4/icons/ConnectionSpinner.tsx b/src/v4/icons/ConnectionSpinner.tsx new file mode 100644 index 000000000..448975a31 --- /dev/null +++ b/src/v4/icons/ConnectionSpinner.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import styled, { keyframes } from 'styled-components'; +import { v4 } from 'uuid'; + +const rotateAnimation = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +`; + +const StyledSpinner = styled.svg` + animation: ${rotateAnimation} 2s linear infinite; +`; + +const ConnectionSpinner = (props: React.SVGProps) => { + const uuid = v4(); + return ( + + + + + + + + + + ); +}; + +export default ConnectionSpinner; diff --git a/src/v4/icons/Copy.tsx b/src/v4/icons/Copy.tsx new file mode 100644 index 000000000..217d4958e --- /dev/null +++ b/src/v4/icons/Copy.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Copy = (props: React.SVGProps) => ( + + + +); + +export default Copy; diff --git a/src/v4/icons/ExclamationCircle.tsx b/src/v4/icons/ExclamationCircle.tsx new file mode 100644 index 000000000..cf1bffd02 --- /dev/null +++ b/src/v4/icons/ExclamationCircle.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +const ExclamationCircle = (props: React.SVGProps) => ( + + + +); + +export default ExclamationCircle; diff --git a/src/v4/icons/Flag.tsx b/src/v4/icons/Flag.tsx new file mode 100644 index 000000000..6d13ce63f --- /dev/null +++ b/src/v4/icons/Flag.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Flag = (props: React.SVGProps) => ( + + + +); + +export default Flag; diff --git a/src/v4/icons/HeartReaction.tsx b/src/v4/icons/HeartReaction.tsx new file mode 100644 index 000000000..b4914841e --- /dev/null +++ b/src/v4/icons/HeartReaction.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { v4 } from 'uuid'; + +const HeartReaction = (props: React.SVGProps) => { + const localId = v4(); + return ( + + + + + + + + + + + ); +}; +export default HeartReaction; diff --git a/src/v4/icons/Kebub.tsx b/src/v4/icons/Kebub.tsx new file mode 100644 index 000000000..beb0b627c --- /dev/null +++ b/src/v4/icons/Kebub.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Kebub = ({ fill = '#A5A9B5', ...props }: React.SVGProps) => ( + + + +); + +export default Kebub; diff --git a/src/v4/icons/Mention.tsx b/src/v4/icons/Mention.tsx new file mode 100644 index 000000000..3e8a12b50 --- /dev/null +++ b/src/v4/icons/Mention.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Mention = ({ fill = 'white', ...props }: React.SVGProps) => ( + + + +); + +export default Mention; diff --git a/src/v4/icons/Reaction.tsx b/src/v4/icons/Reaction.tsx new file mode 100644 index 000000000..4a4e8b77a --- /dev/null +++ b/src/v4/icons/Reaction.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +const Reaction = (props: React.SVGProps) => ( + + + + + +); + +export default Reaction; diff --git a/src/v4/icons/Redo.tsx b/src/v4/icons/Redo.tsx new file mode 100644 index 000000000..58b6518d0 --- /dev/null +++ b/src/v4/icons/Redo.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Svg = (props: React.SVGProps) => ( + + + +); + +export default Svg; diff --git a/src/v4/icons/Remove.tsx b/src/v4/icons/Remove.tsx new file mode 100644 index 000000000..5be7bab12 --- /dev/null +++ b/src/v4/icons/Remove.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +const Svg = (props: React.SVGProps) => ( + + + +); + +export default Svg; diff --git a/src/v4/icons/Reply.tsx b/src/v4/icons/Reply.tsx new file mode 100644 index 000000000..1ee3db677 --- /dev/null +++ b/src/v4/icons/Reply.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Reply = (props: React.SVGProps) => ( + + + +); + +export default Reply; diff --git a/src/v4/icons/UserRegular.tsx b/src/v4/icons/UserRegular.tsx new file mode 100644 index 000000000..91149d27e --- /dev/null +++ b/src/v4/icons/UserRegular.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +const Svg = (props: React.SVGProps) => ( + + + +); + +export default Svg; diff --git a/src/social/v4/components/CommentEdition/CommentEdition.tsx b/src/v4/social/components/CommentEdition/CommentEdition.tsx similarity index 91% rename from src/social/v4/components/CommentEdition/CommentEdition.tsx rename to src/v4/social/components/CommentEdition/CommentEdition.tsx index 63375cbd5..eab6b2734 100644 --- a/src/social/v4/components/CommentEdition/CommentEdition.tsx +++ b/src/v4/social/components/CommentEdition/CommentEdition.tsx @@ -1,10 +1,12 @@ import React from 'react'; -import { useCustomization } from '~/social/v4/providers/CustomizationProvider'; + import { ButtonContainer, CommentEditContainer, CommentEditTextarea } from './styles'; import { QueryMentioneesFnType } from '~/social/hooks/useSocialMention'; -import { CancelButton, SaveButton } from '~/social/v4/elements'; + import { useTheme } from 'styled-components'; +import { CancelButton, SaveButton } from '../../elements'; +import { useCustomization } from '~/v4/core/providers/CustomizationProvider'; interface CommentEditionProps { pageId?: '*'; diff --git a/src/social/v4/components/CommentEdition/index.ts b/src/v4/social/components/CommentEdition/index.ts similarity index 100% rename from src/social/v4/components/CommentEdition/index.ts rename to src/v4/social/components/CommentEdition/index.ts diff --git a/src/social/v4/components/CommentEdition/styles.tsx b/src/v4/social/components/CommentEdition/styles.tsx similarity index 99% rename from src/social/v4/components/CommentEdition/styles.tsx rename to src/v4/social/components/CommentEdition/styles.tsx index 3be7bc2ff..85ea22d2c 100644 --- a/src/social/v4/components/CommentEdition/styles.tsx +++ b/src/v4/social/components/CommentEdition/styles.tsx @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled, { DefaultTheme } from 'styled-components'; import React, { ReactNode } from 'react'; import UIOptionMenu from '~/core/components/OptionMenu'; diff --git a/src/v4/social/components/CommentTray/CommentTray.module.css b/src/v4/social/components/CommentTray/CommentTray.module.css new file mode 100644 index 000000000..dedaa992d --- /dev/null +++ b/src/v4/social/components/CommentTray/CommentTray.module.css @@ -0,0 +1,50 @@ +.container { + display: flex; + flex-direction: column; + height: 100%; + background-color: var(--asc-color-base-inverse); + border: 1px solid var(--asc-color-base-shade4); +} + +.header { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding: var(--asc-spacing-m1); + background-color: var(--asc-color-base-inverse); + color: var(--asc-color-base-default); + font-size: 18px; + font-weight: bold; + border-bottom: 1px solid var(--asc-color-base-shade4); +} + +.roundedHeader { + border-top-left-radius: 1rem; + border-top-right-radius: 1rem; +} + +.content { + flex: 1; + overflow-y: auto; + padding: var(--asc-spacing-m1); +} + +.scroller { + height: 100%; +} + +.composeBarContainer { + padding: var(--asc-spacing-m1); + background-color: var(--asc-color-base-inverse); +} + +.nestedBackdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: -1; +} diff --git a/src/v4/social/components/CommentTray/CommentTray.tsx b/src/v4/social/components/CommentTray/CommentTray.tsx new file mode 100644 index 000000000..fb3636e06 --- /dev/null +++ b/src/v4/social/components/CommentTray/CommentTray.tsx @@ -0,0 +1,62 @@ +import React, { useState } from 'react'; + +import { CommentList } from '../../internal-components/CommentList'; +import { MobileSheetComposeBarContainer } from '../../internal-components/StoryViewer/styles'; +import { StoryCommentComposeBar } from '../../internal-components/StoryCommentComposeBar'; + +const REPLIES_PER_PAGE = 5; + +interface CommentTrayProps { + referenceType: Amity.CommentReferenceType; + referenceId: string; + community: Amity.Community; + shouldAllowInteraction: boolean; + shouldAllowCreation: boolean; +} + +export const CommentTray = ({ + referenceType, + referenceId, + community = {} as Amity.Community, + shouldAllowInteraction = true, + shouldAllowCreation = true, +}: CommentTrayProps) => { + const [isReplying, setIsReplying] = useState(false); + const [replyTo, setReplyTo] = useState(null); + + const onClickReply = (comment: Amity.Comment) => { + setIsReplying(true); + setReplyTo(comment); + }; + + const onCancelReply = () => { + setIsReplying(false); + setReplyTo(null); + }; + + return ( +
+
+ +
+ + + +
+ ); +}; diff --git a/src/social/v4/components/CommentTray/index.ts b/src/v4/social/components/CommentTray/index.ts similarity index 100% rename from src/social/v4/components/CommentTray/index.ts rename to src/v4/social/components/CommentTray/index.ts diff --git a/src/v4/social/components/CommentTray/ui.stories.tsx b/src/v4/social/components/CommentTray/ui.stories.tsx new file mode 100644 index 000000000..9cd1e8b37 --- /dev/null +++ b/src/v4/social/components/CommentTray/ui.stories.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { ThemeProvider } from 'styled-components'; +import { CustomizationProvider } from '~/v4/core/providers/CustomizationProvider'; +import { CommentTray } from './CommentTray'; +import buildGlobalTheme from '~/core/providers/UiKitProvider/theme'; +import { theme } from '../../theme'; + +export default { + title: 'Components/CommentTray', + component: CommentTray, + decorators: [ + (Story) => ( + + +
+ +
+
+
+ ), + ], +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + pageId: '*', + storyId: 'story123', + commentId: 'comment123', + referenceType: 'story', + referenceId: 'story123', + replyTo: 'user123', + isReplying: false, + limit: 5, + isOpen: true, + isJoined: true, + allowCommentInStory: true, + onClose: () => {}, + onClickReply: () => {}, + onCancelReply: () => {}, +}; + +export const Replying = Template.bind({}); +Replying.args = { + ...Default.args, + isReplying: true, +}; + +export const NotJoined = Template.bind({}); +NotJoined.args = { + ...Default.args, + isJoined: false, +}; diff --git a/src/v4/social/components/HyperLinkConfig/HyperLinkConfig.module.css b/src/v4/social/components/HyperLinkConfig/HyperLinkConfig.module.css new file mode 100644 index 000000000..b31fe412c --- /dev/null +++ b/src/v4/social/components/HyperLinkConfig/HyperLinkConfig.module.css @@ -0,0 +1,100 @@ +.hyperlinkFormContainer { + padding: var(--asc-spacing-m1); + border-radius: var(--asc-border-radius-md); +} + +.form { + display: flex; + flex-direction: column; + gap: var(--asc-spacing-l1); +} + +.inputContainer { + display: flex; + flex-direction: column; + gap: var(--asc-spacing-xxs2); +} + +.input { + width: 100%; + padding: var(--asc-spacing-s1); + border: none; + border-bottom: 1px solid var(--asc-color-base-shade4); + outline: none; + color: inherit; +} + +.input.hasError { + border-bottom-color: var(--asc-color-alert-default); + color: var(--asc-color-alert-default); +} + +.label { + display: block; +} + +.label::after { + content: none; + color: var(--asc-color-alert-default); +} + +.label.required::after { + content: '*'; +} + +.description { + color: var(--asc-color-base-shade2); +} + +.errorText { + color: var(--asc-color-alert-default); +} + +.characterCount { + color: var(--asc-color-base-shade1); + text-align: right; + margin-top: 0.3rem; +} + +.headerContainer { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 var(--asc-spacing-m1); +} + +.labelContainer { + display: flex; + justify-content: space-between; + align-items: center; +} + +.headerTitle { + color: var(--asc-color-base-default); +} + +.styledSecondaryButton { + color: var(--asc-color-base-default); +} + +.removeIcon { + width: 1.5rem; + height: 1.5rem; + fill: var(--asc-color-alert-default); +} + +.removeLinkButton { + display: flex; + justify-content: flex-start; + align-items: center; + gap: var(--asc-spacing-s1); + color: var(--asc-color-alert-default); + border-radius: 0; +} + +.divider { + width: 100%; + height: 0.0625rem; + align-self: stretch; + background-color: var(--asc-color-base-shade4); +} diff --git a/src/social/v4/components/HyperLinkConfig/HyperLinkConfig.tsx b/src/v4/social/components/HyperLinkConfig/HyperLinkConfig.tsx similarity index 70% rename from src/social/v4/components/HyperLinkConfig/HyperLinkConfig.tsx rename to src/v4/social/components/HyperLinkConfig/HyperLinkConfig.tsx index d9e3d924e..7db0b8cdc 100644 --- a/src/social/v4/components/HyperLinkConfig/HyperLinkConfig.tsx +++ b/src/v4/social/components/HyperLinkConfig/HyperLinkConfig.tsx @@ -1,38 +1,21 @@ import React from 'react'; -import { BottomSheet } from '~/core/v4/components'; - -import { - MobileSheet, - MobileSheetContainer, - MobileSheetContent, - MobileSheetHeader, -} from '~/core/v4/components/BottomSheet/styles'; - import { useIntl } from 'react-intl'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; -import { - Description, - Form, - HyperlinkFormContainer, - Input, - Label, - ErrorText, - InputContainer, - CharacterCount, - LabelContainer, - HeaderContainer, - HeaderTitle, - StyledSecondaryButton, - RemoveLinkButton, - RemoveIcon, - Divider, -} from './styles'; import { SecondaryButton } from '~/core/components/Button'; import { confirm } from '~/core/components/Confirm'; import useSDK from '~/core/hooks/useSDK'; -import { useCustomization } from '../../providers/CustomizationProvider'; +import { BottomSheet } from '~/v4/core/components'; +import { + MobileSheet, + MobileSheetContainer, + MobileSheetContent, + MobileSheetHeader, +} from '~/v4/core/components/BottomSheet/styles'; +import { useCustomization } from '~/v4/core/providers/CustomizationProvider'; +import { Trash2Icon } from '~/icons'; +import styles from './HyperLinkConfig.module.css'; interface HyperLinkConfigProps { pageId: '*'; @@ -56,7 +39,7 @@ export const HyperLinkConfig = ({ const componentId = 'hyper_link_config_component'; const { getConfig } = useCustomization(); const componentConfig = getConfig(`${pageId}/${componentId}/*`); - const componentTheme = componentConfig?.component_theme.light_theme || {}; + const componentTheme = componentConfig?.theme.light || {}; const cancelButtonConfig = getConfig(`*/hyper_link_config_component/cancel_button`); const doneButtonConfig = getConfig(`*/hyper_link_config_component/done_button`); @@ -67,7 +50,6 @@ export const HyperLinkConfig = ({ const schema = z.object({ url: z.string().refine(async (value) => { if (!value) return true; - // since validateUrls() will throw an error if the url is not whitelisted so need to catch it and return false instead const hasWhitelistedUrls = await client?.validateUrls([value]).catch(() => false); return hasWhitelistedUrls; }, formatMessage({ id: 'storyCreation.hyperlink.validation.error.whitelisted' })), @@ -76,8 +58,6 @@ export const HyperLinkConfig = ({ .optional() .refine(async (value) => { if (!value) return true; - // since validateUrls() will throw an error if the url is not whitelisted so need to catch it and return false instead - // TO FIX: use schema.safeParseAsync() const hasBlockedWord = await client?.validateTexts([value]).catch(() => false); return hasBlockedWord; }, formatMessage({ id: 'storyCreation.hyperlink.validation.error.blocked' })), @@ -137,7 +117,7 @@ export const HyperLinkConfig = ({ color: componentTheme?.secondary_color, }} > - +
{cancelButtonConfig?.cancel_button_text || formatMessage({ id: 'storyCreation.hyperlink.bottomSheet.cancel' })} @@ -145,10 +125,10 @@ export const HyperLinkConfig = ({ )} - +
{formatMessage({ id: 'storyCreation.hyperlink.bottomSheet.title' })} - - + {doneButtonConfig?.done_button_text || formatMessage({ id: 'storyCreation.hyperlink.bottomSheet.submit' })} {doneButtonConfig?.done_icon && ( )} - - + +
- -
- -