Skip to content
This repository has been archived by the owner on Jan 15, 2025. It is now read-only.

Feature/chat reconnection #1539

Merged
merged 8 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"start:prod": "NODE_ENV=production node build/server.js"
},
"dependencies": {
"@ecency/ns-query": "^1.1.0",
"@ecency/ns-query": "^1.1.1",
"@ecency/render-helper": "^2.2.29",
"@ecency/render-helper-amp": "^1.1.0",
"@emoji-mart/data": "^1.1.2",
Expand Down
2 changes: 2 additions & 0 deletions src/common/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { useGetAccountFullQuery } from "./api/queries";
import { UIManager } from "@ui/core";
import defaults from "./constants/defaults.json";
import { getAccessToken } from "./helper/user-token";
import { ChatLocaitonListener } from "./features/chats/components/chat-locaiton-listener";

// Define lazy pages
const ProfileContainer = loadable(() => import("./pages/profile-functional"));
Expand Down Expand Up @@ -125,6 +126,7 @@ const App = (props: any) => {
activeUserData={activeUserAccount}
ecencyAccessToken={accessToken}
>
<ChatLocaitonListener />
<Switch>
<Route exact={true} path={routes.HOME} component={EntryIndexContainer} />
<Route
Expand Down
4 changes: 4 additions & 0 deletions src/common/components/floating-faq/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
.scroll-to-top.visible ~ .floating-faq-button button {
right: 4rem;
bottom: 1.25rem;

@include media-breakpoint-down(md) {
bottom: 4rem;
}
}

.floating-container {
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/floating-faq/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const FloatingFAQ = () => {
{display && !isSubmitPage && (
<Button
noPadding={innerWidth < 768}
className="fixed bottom-4 right-4 w-[40px] h-[40px] md:w-auto md:h-[40px]"
className="fixed bottom-[4rem] md:bottom-4 right-4 w-[40px] h-[40px] md:w-auto md:h-[40px]"
onClick={handleShow}
icon={helpIconSvg}
iconPlacement="left"
Expand Down
3 changes: 2 additions & 1 deletion src/common/components/notifications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,8 @@ class NotificationsDialog extends Component<NotificationProps> {
<Modal
show={true}
onHide={this.hide}
className={"notifications-modal drawer " + this.props.className}
className={"notifications-modal drawer !top-[70px] " + this.props.className}
overlayClassName="!top-[70px]"
>
<ModalBody>
<DialogContent {...this.props} />
Expand Down
19 changes: 11 additions & 8 deletions src/common/features/chats/components/chat-channel-messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }:
const { publicKey } = useKeysQuery();
const { fetchNextPage, refetch } = usePublicMessagesQuery(
currentChannel,
joinedCommunityTeamKeys
joinedCommunityTeamKeys.map(({ pubkey }) => pubkey)
);

const { mutateAsync: muteUserInChannel, isLoading: isUserMutingLoading } =
Expand Down Expand Up @@ -92,6 +92,7 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }:
<>
<Dropdown
key={message.id}
closeOnClickOutside={true}
show={currentInteractingMessageId === message.id}
setShow={(v) =>
setCurrentInteractingMessageId(v ? currentInteractingMessageId : undefined)
Expand Down Expand Up @@ -131,13 +132,15 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }:
label={_t("chat.hide-message")}
onClick={() => hideMessage({ messageId: message.id, status: 0 })}
/>
<DropdownItemWithIcon
icon={
isUserMutingLoading ? <Spinner className="w-3.5 h-3.5" /> : removeUserSvg
}
label={_t("chat.block-author")}
onClick={() => muteUserInChannel({ pubkey: message.creator, status: 0 })}
/>
{message.creator !== publicKey && (
<DropdownItemWithIcon
icon={
isUserMutingLoading ? <Spinner className="w-3.5 h-3.5" /> : removeUserSvg
}
label={_t("chat.block-author")}
onClick={() => muteUserInChannel({ pubkey: message.creator, status: 0 })}
/>
)}
</DropdownMenu>
</Dropdown>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function ChatFloatingDate({
return currentFormattedDate ? (
<div
className={classNameObject({
"sticky z-10 flex justify-center text-xs text-center": true,
"sticky z-[9] flex justify-center text-xs text-center": true,
"top-0": !isPage,
"top-[180px] md:top-[57px]": isPage
})}
Expand Down
14 changes: 14 additions & 0 deletions src/common/features/chats/components/chat-locaiton-listener.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import useDebounce from "react-use/lib/useDebounce";
import { useLocation } from "react-router";
import { useContext } from "react";
import { NostrContext } from "@ecency/ns-query";
import React from "react";

export function ChatLocaitonListener() {
const location = useLocation();
const { setSleepMode } = useContext(NostrContext);

useDebounce(() => setSleepMode(!location.pathname.startsWith("/chats")), 3000, [location]);

return <></>;
}
4 changes: 4 additions & 0 deletions src/common/features/chats/components/chat-popup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "@ecency/ns-query";
import { ChatInvitation } from "../chat-invitation";
import { ChatChannelNotJoined } from "../chat-channel-not-joined";
import { ChatsUserNotJoinedSection } from "../../screens/chats-user-not-joined-section";

export const ChatPopUp = () => {
const { activeUser, global } = useMappedStore();
Expand Down Expand Up @@ -163,6 +164,9 @@ export const ChatPopUp = () => {
) : (
<ChatsWelcome />
)}
{communityName && !currentChannel && (
<ChatsUserNotJoinedSection username={communityName} />
)}
</div>
<div className="pl-2">
{((currentContact && isContactJoined) || (currentChannel && isJoinedToChannel)) && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function BlockedUsersModal({ channel, setShow, show }: Props) {
<thead>
<Tr>
<Th>{_t("g.username")}</Th>
<Th>{_t("g.status")}</Th>
<Th>{_t("g.actions")}</Th>
</Tr>
</thead>
<tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function HiddenMessagesModal({ channel, setShow, show }: Props) {
<thead>
<Tr>
<Th>{_t("g.message")}</Th>
<Th>{_t("g.status")}</Th>
<Th>{_t("g.actions")}</Th>
</Tr>
</thead>
<tbody>
Expand Down Expand Up @@ -72,7 +72,7 @@ export function HiddenMessagesModal({ channel, setShow, show }: Props) {
outline={true}
onClick={() => hideInChannel({ messageId: message.id, status: 1 })}
>
{_t("chat.unblock")}
{_t("g.restore")}
</Button>
</Td>
</Tr>
Expand All @@ -83,7 +83,7 @@ export function HiddenMessagesModal({ channel, setShow, show }: Props) {
)}
{(!hiddenMessages || hiddenMessages?.length === 0) && (
<div className="text-center text-gray-400 dark:text-gray-600">
{_t("chat.no-locked-user")}
{_t("chat.no-hidden-messages")}
</div>
)}
</div>
Expand Down
63 changes: 8 additions & 55 deletions src/common/features/chats/components/join-community-chat-btn.tsx
Original file line number Diff line number Diff line change
@@ -1,90 +1,43 @@
import React, { useContext, useMemo } from "react";
import React, { useMemo } from "react";
import { History } from "history";
import { Community } from "../../../store/communities";
import { _t } from "../../../i18n";
import { Spinner } from "@ui/spinner";
import { Button } from "@ui/button";
import {
ChatContext,
useAddCommunityChannel,
useChannelsQuery,
useCommunityChannelQuery,
useCreateCommunityChat,
useLeaveCommunityChannel,
useLeftCommunityChannelsQuery
} from "@ecency/ns-query";
import { useCommunityChannelQuery, useCreateCommunityChat } from "@ecency/ns-query";
import { useMappedStore } from "../../../store/use-mapped-store";
import { messageSendSvg } from "../../../img/svg";

interface Props {
history: History;
community: Community;
}

export default function JoinCommunityChatBtn(props: Props) {
const { hasUserJoinedChat } = useContext(ChatContext);
const { data: currentChannel, isLoading: isCurrentChannelLoading } = useCommunityChannelQuery(
props.community
);
const { activeUser } = useMappedStore();
const { data: channels } = useChannelsQuery();
const { data: leftChannelsIds, isLoading: isChannelsIdsLoading } =
useLeftCommunityChannelsQuery();

const { mutateAsync: addCommunityChannel, isLoading: isAddCommunityChannelLoading } =
useAddCommunityChannel(currentChannel);
const { mutateAsync: createCommunityChat, isLoading: isCreateCommunityChatLoading } =
useCreateCommunityChat(props.community);
const { mutateAsync: leaveCommunityChannel, isLoading: isLeavingCommunityChannelLoading } =
useLeaveCommunityChannel(currentChannel);

const isGlobalLoading = useMemo(
() => isChannelsIdsLoading || isCurrentChannelLoading,
[isChannelsIdsLoading, isCurrentChannelLoading]
);
const isCommunityChannelCreated = useMemo(() => !!currentChannel, [currentChannel]);
const isCommunityChannelJoined = useMemo(
() =>
channels?.some(
(item) =>
item.communityName === props.community.name &&
!leftChannelsIds?.includes(currentChannel?.id!)
),
[channels, leftChannelsIds]
);

// For now only Ecency official account able to start community channels
const isAbleToCreateChannel = useMemo(() => activeUser?.username === "ecency", [activeUser]);

const join = async () => {
if (!hasUserJoinedChat) {
return;
}
await addCommunityChannel();
};

return isGlobalLoading ? (
return isCurrentChannelLoading ? (
<></>
) : (
<>
{isCommunityChannelJoined && (
<Button
size="sm"
appearance="secondary"
disabled={isLeavingCommunityChannelLoading}
icon={isLeavingCommunityChannelLoading && <Spinner className="w-3.5 h-3.5" />}
onClick={() => leaveCommunityChannel()}
>
{_t("chat.chat-joined")}
</Button>
)}
{!isCommunityChannelJoined && isCommunityChannelCreated && (
{isCommunityChannelCreated && (
<Button
size="sm"
disabled={isAddCommunityChannelLoading}
to={`/chats/${props.community.name}/channel`}
iconPlacement="left"
icon={messageSendSvg}
>
{_t("chat.join-community-chat")}
{_t("chat.view-community-channel")}
</Button>
)}
{!isCommunityChannelCreated && isAbleToCreateChannel && (
Expand All @@ -95,7 +48,7 @@ export default function JoinCommunityChatBtn(props: Props) {
icon={isCreateCommunityChatLoading && <Spinner />}
iconPlacement="left"
>
{_t("chat.start-community-chat")}
{_t("chat.start-community-channel")}
</Button>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function useSearchCommunitiesQuery(query: string) {
return useQuery(
["chats/search-communities", query],
async () => {
const response = await getCommunities("", 10, query);
const response = await getCommunities("", 5, query.toLowerCase());
return response ?? [];
},
{
Expand Down
5 changes: 2 additions & 3 deletions src/common/features/chats/queries/search-users-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ export function useSearchUsersQuery(query: string) {
if (!query) {
return [];
}
const response = await getAccountReputations(query, 10);
response.sort((a, b) => (a.reputation > b.reputation ? -1 : 1));
return response;
const response = await getAccountReputations(query.toLowerCase(), 5);
return response.sort((a, b) => (a.reputation > b.reputation ? -1 : 1));
},
{
initialData: []
Expand Down
2 changes: 1 addition & 1 deletion src/common/features/chats/screens/chats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const Chats = ({ match, history }: Props) => {
!isShowChatRoom &&
isReady &&
(!!directContact?.pubkey.startsWith("not_joined_") || (community && !communityChannel)),
[isShowChatRoom, isReady, directContact]
[isShowChatRoom, isReady, directContact, communityChannel, community]
);

const isShowDefaultScreen = useMemo(
Expand Down
2 changes: 1 addition & 1 deletion src/common/features/ui/dropdown/dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function DropdownMenu(props: HTMLProps<HTMLDivElement> & Props) {
<div
{...nativeProps}
className={classNameObject({
"z-[1000] absolute flex flex-col items-start border border-[--border-color] min-w-[200px] py-2 gap-2 pr-3 rounded-xl bg-white":
"z-[1000] ecency-dropdown-menu absolute flex flex-col items-start border border-[--border-color] min-w-[200px] py-2 gap-2 pr-3 rounded-xl bg-white":
true,
"right-0": props.align === "right",
"top-[100%]": (props.align ?? "bottom") === "bottom",
Expand Down
15 changes: 12 additions & 3 deletions src/common/features/ui/dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,29 @@ export function Dropdown(props: HTMLProps<HTMLDivElement> & Props) {
const ref = useRef<HTMLDivElement | null>(null);
const { openPopovers } = useContext(UIContext);

useClickAway(ref, () => {
useClickAway(ref, (e) => {
if (openPopovers.size === 0) {
setShow(false);
} else {
return;
}

if (props.closeOnClickOutside ?? true) {
if (!isInsideDropdown(e) && (props.closeOnClickOutside ?? true)) {
setShow(false);
// props.setShow?.(false);
props.setShow?.(false);
}
});
const nativeProps = useFilteredProps(props, ["show", "setShow", "closeOnClickOutside"]);

const isInsideDropdown = (e: Event) => {
let target = e.target as HTMLElement | null;
while (!target?.parentElement?.classList.contains("ecency-dropdown-menu") && target) {
target = target?.parentElement ?? null;
}

return !!target;
};

return (
<DropdownContext.Provider
value={{
Expand Down
8 changes: 7 additions & 1 deletion src/common/features/ui/modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface Props {
animation?: boolean;
size?: "md" | "lg";
dialogClassName?: string;
overlayClassName?: string;
}

export const ModalContext = createContext<{
Expand Down Expand Up @@ -59,7 +60,12 @@ export function Modal(props: Omit<HTMLProps<HTMLDivElement>, "size"> & Props) {
<ModalContext.Provider value={{ show, setShow }}>
{show &&
createPortal(
<div className="bg-black opacity-[50%] z-[1040] fixed top-0 left-0 right-0 bottom-0" />,
<div
className={classNameObject({
"bg-black opacity-[50%] z-[1040] fixed top-0 left-0 right-0 bottom-0": true,
[props.overlayClassName ?? ""]: !!props.overlayClassName
})}
/>,
document.querySelector("#modal-overlay-container")!!
)}
{show &&
Expand Down
Loading