diff --git a/backend/channel_plugin/apps/centri/apps.py b/backend/channel_plugin/apps/centri/apps.py index e29e090be..ab7f34580 100644 --- a/backend/channel_plugin/apps/centri/apps.py +++ b/backend/channel_plugin/apps/centri/apps.py @@ -6,12 +6,7 @@ class CentriConfig(AppConfig): def ready(self) -> None: try: - from apps.centri.signals import( - channel_signals, - thread_signals, - message_signals, - central_signals - ) + from apps.centri.signals import channel_signals, thread_signals, message_signals except ImportError: pass diff --git a/backend/channel_plugin/apps/centri/signals/channel_signals.py b/backend/channel_plugin/apps/centri/signals/channel_signals.py index ff6fd4349..0689fb51a 100644 --- a/backend/channel_plugin/apps/centri/signals/channel_signals.py +++ b/backend/channel_plugin/apps/centri/signals/channel_signals.py @@ -1,49 +1,62 @@ -from apps.centri.centwrapper import CentClient -from apps.centri.helperfuncs import build_room_name -from apps.channelmessages.serializers import ChannelMessageSerializer -from apps.channels.views import ChannelMemberViewset -from cent import CentException -from django.conf import settings from django.core.signals import request_finished from django.dispatch import receiver +from django.utils import timezone +from django.conf import settings + +from cent import CentException + +from apps.centri.centwrapper import CentClient +from apps.channels.views import ChannelMemberViewset +from apps.channelmessages.serializers import ChannelMessageSerializer +from apps.centri.helperfuncs import build_room_name + CLIENT = CentClient( - address=settings.CENTRIFUGO_URL, - api_key=settings.CENTRIFUGO_API_KEY, - timeout=3, - verify=True, + address = settings.CENTRIFUGO_URL, + api_key = settings.CENTRIFUGO_API_KEY, + timeout = 3, + verify = True ) @receiver(request_finished, sender=ChannelMemberViewset) def JoinedChannelSignal(sender, **kwargs): - + serializer = ChannelMessageSerializer( + data=data, context={"channel_id": channel_id, "org_id": org_id} + uid = kwargs.get("dispatch_uid") - + if uid == "JoinedChannelSignal": org_id = kwargs.get("org_id") channel_id = kwargs.get("channel_id") user = kwargs.get("user") room_name = build_room_name(org_id, channel_id) + + data = { + "user_id": user.get("_id"), + "content": "event", + "files": [] + } + + event = { + "action": "join:channel", + "recipients": kwargs.get("added", [user]) + } - if user: - data = {"user_id": user.get("_id"), "content": "event", "files": []} - else: - if not kwargs.get("added"): - return None - else: - try: - data = { - "user_id": kwargs.get("added")[0].get("_id"), - "content": "event", - "files": [], - } - except: # noqa - return None - - event = {"action": "join:channel", "recipients": kwargs.get("added", [user])} + serializer = ChannelMessageSerializer( + data=data, + context={"channel_id": channel_id, "org_id": org_id} + ) + serializer.is_valid(raise_exception=True) + channelmessage = serializer.data.get("channelmessage") + + # required + channelmessage.type = "event" + channelmessage.event = event + channelmessage.can_reply = False +======= try: serializer = ChannelMessageSerializer( data=data, context={"channel_id": channel_id, "org_id": org_id} @@ -54,45 +67,51 @@ def JoinedChannelSignal(sender, **kwargs): channelmessage.type = "event" channelmessage.event = event channelmessage.can_reply = False +>>>>>>> 83443824799f880c1d34fec26578e5cd8da4f730 # required result = channelmessage.create(org_id) - print("\n") - print(result) - print("\n") CLIENT.publish(room_name, result) - except: # noqa + except: pass - @receiver(request_finished, sender=ChannelMemberViewset) def LeftChannelSignal(sender, **kwargs): uid = kwargs.get("dispatch_uid") - + if uid == "LeftChannelSignal": - + + org_id = kwargs.get("org_id") channel_id = kwargs.get("channel_id") user = kwargs.get("user") - + room_name = build_room_name(org_id, channel_id) try: CLIENT.unsubscribe(user.get("_id"), room_name) except CentException: print("client removal failed because channel is not active") - - data = {"user_id": user.get("_id"), "content": "event", "files": []} - - event = {"action": "leave:channel", "recipients": kwargs.get("removed", [user])} + + data = { + "user_id": user.get("_id"), + "content": "event", + "files": [] + } + + event = { + "action": "leave:channel", + "recipients": kwargs.get("removed", [user]) + } serializer = ChannelMessageSerializer( - data=data, context={"channel_id": channel_id, "org_id": org_id} + data=data, + context={"channel_id": channel_id, "org_id": org_id} ) serializer.is_valid(raise_exception=True) channelmessage = serializer.data.get("channelmessage") - + # required channelmessage.type = "event" channelmessage.event = event @@ -100,9 +119,7 @@ def LeftChannelSignal(sender, **kwargs): try: result = channelmessage.create(org_id) - print("\n") - print(result) - print("\n") CLIENT.publish(room_name, result) - except: # noqa + except: pass + diff --git a/backend/channel_plugin/apps/centri/signals/message_signals.py b/backend/channel_plugin/apps/centri/signals/message_signals.py index 016f8582e..d15176b90 100644 --- a/backend/channel_plugin/apps/centri/signals/message_signals.py +++ b/backend/channel_plugin/apps/centri/signals/message_signals.py @@ -36,9 +36,6 @@ def CreateMessageSignal(sender, **kwargs): } try: - print("\n") - print(payload) - print("\n") CLIENT.publish(room_name, payload) except CentException: pass @@ -61,10 +58,6 @@ def EditMessageSignal(sender, **kwargs): } try: - print("\n") - print(payload) - print("\n") - CLIENT.publish(room_name, payload) except CentException: pass @@ -87,11 +80,7 @@ def DeleteMessageSignal(sender, **kwargs): "action": "delete:message" } - try: - print("\n") - print(payload) - print("\n") - + try: CLIENT.publish(room_name, payload) except CentException: pass diff --git a/backend/channel_plugin/apps/centri/signals/signals.py b/backend/channel_plugin/apps/centri/signals/signals.py new file mode 100644 index 000000000..722450823 --- /dev/null +++ b/backend/channel_plugin/apps/centri/signals/signals.py @@ -0,0 +1,181 @@ +# from django.core.signals import request_finished +# from django.dispatch import receiver +# from django.utils import timezone +# from django.conf import settings + +# from cent import CentException + +# from .centwrapper import CentClient +# from apps.channels.views import ChannelMemberViewset +# from apps.channelmessages.views import ChannelMessageViewset +# from apps.channelmessages.serializers import ChannelMessageSerializer +# from .helperfuncs import build_room_name + + + +# CLIENT = CentClient( +# address = settings.CENTRIFUGO_URL, +# api_key = settings.CENTRIFUGO_API_KEY, +# timeout = 3, +# verify = True +# ) + + +# # >>>>>>>>>>> channel member signals <<<<<<<<<<<<<<<<<< +# @receiver(request_finished, sender=ChannelMemberViewset) +# def JoinedChannelSignal(sender, **kwargs): + +# uid = kwargs.get("dispatch_uid") + +# if uid == "JoinedChannelSignal": +# org_id = kwargs.get("org_id") +# channel_id = kwargs.get("channel_id") +# user = kwargs.get("user") + +# room_name = build_room_name(org_id, channel_id) + +# data = { +# "user_id": user.get("_id"), +# "content": "event", +# "files": [] +# } + +# event = { +# "action": "join:channel", +# "recipients": kwargs.get("added", [user]) +# } + +# serializer = ChannelMessageSerializer( +# data=data, +# context={"channel_id": channel_id, "org_id": org_id} +# ) + +# serializer.is_valid(raise_exception=True) +# channelmessage = serializer.data.get("channelmessage") + +# # required +# channelmessage.type = "event" +# channelmessage.event = event +# channelmessage.can_reply = False + +# try: +# result = channelmessage.create(org_id) +# CLIENT.publish(room_name, result) +# except: +# pass + +# @receiver(request_finished, sender=ChannelMemberViewset) +# def LeftChannelSignal(sender, **kwargs): +# uid = kwargs.get("dispatch_uid") + +# if uid == "LeftChannelSignal": + + +# org_id = kwargs.get("org_id") +# channel_id = kwargs.get("channel_id") +# user = kwargs.get("user") + +# room_name = build_room_name(org_id, channel_id) + +# try: +# CLIENT.unsubscribe(user.get("_id"), room_name) +# except CentException: +# print("client removal failed because channel is not active") + +# data = { +# "user_id": user.get("_id"), +# "content": "event", +# "files": [] +# } + +# event = { +# "action": "leave:channel", +# "recipients": kwargs.get("removed", [user]) +# } + +# serializer = ChannelMessageSerializer( +# data=data, +# context={"channel_id": channel_id, "org_id": org_id} +# ) + +# serializer.is_valid(raise_exception=True) +# channelmessage = serializer.data.get("channelmessage") + +# # required +# channelmessage.type = "event" +# channelmessage.event = event +# channelmessage.can_reply = False + +# try: +# result = channelmessage.create(org_id) +# CLIENT.publish(room_name, result) +# except: +# pass + +# # >>>>>>>>>>> channelmessages signals <<<<<<<<<<<<<<<<<< +# @receiver(request_finished, sender=ChannelMessageViewset) +# def CreateMessageSignal(sender, **kwargs): +# uid = kwargs.get("dispatch_uid") + +# if uid == "CreateMessageSignal": +# org_id = kwargs.get("org_id") +# channel_id = kwargs.get("channel_id") + +# room_name = build_room_name(org_id, channel_id) + +# # send notification to channel that has created a new message +# payload = kwargs.get("data", {}) + +# payload["event"] = { +# "action": "create:message" +# } + +# try: +# CLIENT.publish(room_name, payload) +# except CentException: +# pass + +# @receiver(request_finished, sender=ChannelMessageViewset) +# def EditMessageSignal(sender, **kwargs): +# uid = kwargs.get("dispatch_uid") + +# if uid == "EditMessageSignal": +# org_id = kwargs.get("org_id") +# channel_id = kwargs.get("channel_id") + +# room_name = build_room_name(org_id, channel_id) + +# # send message to channel that user has edited a message +# payload = kwargs.get("data", {}) + +# payload["event"] = { +# "action": "update:message" +# } + +# try: +# CLIENT.publish(room_name, payload) +# except CentException: +# pass + +# @receiver(request_finished, sender=ChannelMessageViewset) +# def DeleteMessageSignal(sender, **kwargs): +# uid = kwargs.get("dispatch_uid") + +# if uid == "DeleteMessageSignal": +# org_id = kwargs.get("org_id") +# channel_id = kwargs.get("channel_id") + +# room_name = build_room_name(org_id, channel_id) + +# # send notification to channel that user has joined +# payload = kwargs.get("data", {}) +# payload["can_reply"] = False + +# payload["event"] = { +# "action": "delete:message" +# } + +# try: +# CLIENT.publish(room_name, payload) +# except CentException: +# pass diff --git a/backend/channel_plugin/apps/centri/signals/thread_signals.py b/backend/channel_plugin/apps/centri/signals/thread_signals.py index fb54bd899..15c330144 100644 --- a/backend/channel_plugin/apps/centri/signals/thread_signals.py +++ b/backend/channel_plugin/apps/centri/signals/thread_signals.py @@ -36,11 +36,7 @@ def CreateThreadSignal(sender, **kwargs): "action": "create:thread" } - try: - print("\n") - print(payload) - print("\n") - + try: CLIENT.publish(room_name, payload) except CentException: pass @@ -63,10 +59,6 @@ def EditThreadSignal(sender, **kwargs): } try: - print("\n") - print(payload) - print("\n") - CLIENT.publish(room_name, payload) except CentException: pass @@ -89,11 +81,7 @@ def DeleteThreadSignal(sender, **kwargs): "action": "delete:thread" } - try: - print("\n") - print(payload) - print("\n") - + try: CLIENT.publish(room_name, payload) except CentException: pass diff --git a/backend/channel_plugin/apps/channelmessages/views.py b/backend/channel_plugin/apps/channelmessages/views.py index e6aff448c..d50f30cdf 100644 --- a/backend/channel_plugin/apps/channelmessages/views.py +++ b/backend/channel_plugin/apps/channelmessages/views.py @@ -579,13 +579,13 @@ def paginate_messages(request, org_id, channel_id): data = {"page": page, "last_timestamp": last_timestamp, "page_size":page_size} response = get_messages_from_page(org_id, "channelmessage", channel_id, page, page_size) - if response['data']: - payload = { - "user_id": user_id, - "last_timestamp": response['data'][-1]['timestamp'] - } + # if response['data']: + # payload = { + # "user_id": user_id, + # "last_timestamp": response['data'][-1]['timestamp'] + # } - save_response = save_last_message_user(org_id, "userscroll", payload) + # save_response = save_last_message_user(org_id, "userscroll", payload) return Response(response, status=status.HTTP_200_OK) diff --git a/backend/channel_plugin/apps/channels/serializers.py b/backend/channel_plugin/apps/channels/serializers.py index 2695d4bd7..54df39c80 100644 --- a/backend/channel_plugin/apps/channels/serializers.py +++ b/backend/channel_plugin/apps/channels/serializers.py @@ -47,28 +47,6 @@ def to_representation(self, instance): return data -class RoomSerializer(serializers.Serializer): - - room_name = serializers.CharField( - max_length=100, required=True, help_text="Channel name" - ) - - room_members_ids = serializers.ListField() - ord_id = serializers.CharField(max_length=200, required=True) - private = serializers.BooleanField(default=False) - - def convert_to_channel_serializer(self) -> serializers.Serializer : - self.is_valid(raise_exception=True) - - data = { - 'name' : self.data.get("room_name"), - 'owner': self.data.get("room_members_ids", ["1"])[0], - "private": self.data.get("private") - } - - return ChannelSerializer(data=data, context={"org_id": self.data.get("org_id")}) - - class UserSerializer(serializers.Serializer): _id = serializers.CharField(max_length=30, required=True, help_text="User ID") @@ -232,22 +210,3 @@ class ThreadFilesSerializer(serializers.ListField): class ChannelAllFilesSerializer(serializers.Serializer): channelmessage = MessageFilesSerializer() thread = ThreadFilesSerializer() - - -class ChannelPermissions(serializers.Serializer): - archive_channel = serializers.BooleanField(required=False) - mention_channel = serializers.BooleanField(required=False) - add_members = serializers.BooleanField(required=False) - remove_members = serializers.BooleanField(required=False) - send_images = serializers.BooleanField(required=False) - send_videos = serializers.BooleanField(required=False) - add_bots = serializers.BooleanField(required=False) - send_messages = serializers.BooleanField(required=False) - delete_messages = serializers.BooleanField(required=False) - send_stickers = serializers.BooleanField(required=False) - create_threads = serializers.BooleanField(required=False) - send_files = serializers.BooleanField(required=False) - pin_messages = serializers.BooleanField(required=False) - - - roles = serializers.ListField(child=serializers.CharField(max_length=30), required=False) diff --git a/backend/channel_plugin/apps/channels/urls.py b/backend/channel_plugin/apps/channels/urls.py index 962239bc8..f672a949b 100644 --- a/backend/channel_plugin/apps/channels/urls.py +++ b/backend/channel_plugin/apps/channels/urls.py @@ -8,15 +8,12 @@ channel_socket_view, user_channel_list, notification_views, - channel_list_zc_main_views, - handle_channel_permissions, ) from django.urls import path app_name = "channels" urlpatterns = [ - path("channels/", channel_list_zc_main_views), path("/channels/", channel_list_create_view), path("/channels//files/", channel_media_all_view), path( @@ -33,13 +30,10 @@ channel_members_can_input_view, name="channel-members-can-input", ), - path("/channels//members//notifications/", - notification_views), path( "/channels//members//", channel_members_update_retrieve_views, ), path("/channels/users//", user_channel_list), - path("/channels//manage_permissions/", handle_channel_permissions, name='change_channel_permissions') - + path("/channels//members//notifications/", notification_views), ] diff --git a/backend/channel_plugin/apps/channels/views.py b/backend/channel_plugin/apps/channels/views.py index 056b14125..a07e2168f 100644 --- a/backend/channel_plugin/apps/channels/views.py +++ b/backend/channel_plugin/apps/channels/views.py @@ -6,29 +6,23 @@ from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import status, throttling -from rest_framework.decorators import action, api_view, throttle_classes +from rest_framework.decorators import action, throttle_classes from rest_framework.response import Response from rest_framework.viewsets import ViewSet from channel_plugin.utils.customexceptions import ThrottledViewSet -from channel_plugin.utils.customrequest import ( - Request, - find_match_in_db, - manage_channel_permissions, -) +from channel_plugin.utils.customrequest import Request from channel_plugin.utils.wrappers import OrderMixin from .serializers import ( # SearchMessageQuerySerializer, ChannelAllFilesSerializer, ChannelGetSerializer, - ChannelPermissions, ChannelSerializer, ChannelUpdateSerializer, NotificationsSettingSerializer, - RoomSerializer, SocketSerializer, UserChannelGetSerializer, - UserSerializer, + UserSerializer ) # from rest_framework.filters @@ -76,12 +70,6 @@ def channels(self, request, org_id): status_code = status.HTTP_404_NOT_FOUND if result.__contains__("_id"): result.update({"members": len(result["users"].keys())}) - request_finished.send( - sender=None, - dispatch_uid="UpdateSidebarSignal", - org_id=org_id, - user_id=result.get("owner"), - ) status_code = status.HTTP_201_CREATED return Response(result, status=status_code) @@ -121,41 +109,6 @@ def create_room(self, request): return Response(result, status=status_code) - @swagger_auto_schema( - operation_id="create-room", - request_body=RoomSerializer, - responses={ - 201: openapi.Response("Response", RoomSerializer), - 404: openapi.Response("Error Response", ErrorSerializer), - }, - ) - @throttle_classes([throttling.AnonRateThrottle]) - @action( - methods=["POST"], - detail=False, - ) - def create_room(self, request): - serializer = RoomSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - channel_serializer = serializer.convert_to_channel_serializer() - channel_serializer.is_valid() - print(channel_serializer) - channel = channel_serializer.data.get("channel") - result = channel.create(serializer.data.get("ord_id")) - status_code = status.HTTP_404_NOT_FOUND - - if result.__contains__("_id"): - request_finished.send( - sender=None, - dispatch_uid="UpdateSidebarSignal", - org_id=channel_serializer.data.get("ord_id"), - user_id=result.get("owner"), - ) - status_code = status.HTTP_201_CREATED - return Response(serializer.data, status=status_code) - else: - return Response(result, status=status_code) - @swagger_auto_schema( operation_id="retrieve-channels", responses={ @@ -328,14 +281,6 @@ def channel_update(self, request, org_id, channel_id): ): if result.__contains__("_id"): result.update({"members": len(result["users"].keys())}) - # TODO: make this block asynchronus - # for user_id in result["users"].keys(): - # request_finished.send( - # sender=None, - # dispatch_uid="UpdateSidebarSignal", - # org_id=org_id, - # user_id=user_id, - # ) status_code = status.HTTP_200_OK return Response(result, status=status_code) @@ -345,15 +290,6 @@ def channel_update(self, request, org_id, channel_id): 204: openapi.Response("Channel deleted successfully"), 404: openapi.Response("Not found"), }, - manual_parameters=[ - openapi.Parameter( - "user_id", - openapi.IN_QUERY, - description="User ID (owner of message)", - required=True, - type=openapi.TYPE_STRING, - ) - ], ) @action( methods=["DELETE"], @@ -372,13 +308,6 @@ def channel_delete(self, request, org_id, channel_id): # delete relationships if result.get("status") == 200: - request_finished.send( - sender=None, - dispatch_uid="UpdateSidebarSignal", - org_id=org_id, - user_id=request.query_params.get("user_id", None), - ) - if result.get("data", {}).get("deleted_count") > 0: Request.delete( org_id, "channelmessage", data_filter={"channel_id": channel_id} @@ -467,6 +396,8 @@ def get_channel_socket_name(self, request, org_id, channel_id): ) + + channel_list_create_view = ChannelViewset.as_view( { "get": "channel_all", @@ -513,126 +444,51 @@ def retrieve_channel(request, org_id, channel_id): return result return {} - @swagger_auto_schema( - responses={ - 200: openapi.Response("Response", NotificationsSettingSerializer), - 404: openapi.Response("Not Found"), - }, - operation_id="retrieve-user-notifications", - ) - @action( - methods=["GET"], - detail=False, - ) - def retrieve_notification(self, request, org_id, channel_id, member_id): - """Retrieve user notification preferences for channel - - bash - curl -X GET "{{baseUrl}}/v1/{{org_id}}/channels/{{channel_id}}/members/{{member_id}}/notifications/" -H "accept: application/json" - - """ # noqa - channel = self.retrieve_channel(request, org_id, channel_id) - if channel.__contains__("_id"): - user_data = channel["users"].get(member_id) - if user_data: - serializer = UserSerializer(data=user_data) - serializer.is_valid(raise_exception=True) - - # an empty field will be returned for users that have not - # changed their settings. - FACTORY_SETTINGS = { - "web": "nothing", - "mobile": "mentions", - "same_for_mobile": False, - "mute": False, - } - - settings = serializer.data.get("notifications", FACTORY_SETTINGS) - return Response(settings, status=status.HTTP_200_OK) - - return Response( - {"error": "member not found"}, status=status.HTTP_404_NOT_FOUND - ) - return Response( - {"error": "channel not found"}, status=status.HTTP_404_NOT_FOUND - ) - - @swagger_auto_schema( - request_body=NotificationsSettingSerializer, - responses={ - 200: openapi.Response( - "Response", - NotificationsSettingSerializer, - ) - }, - operation_id="update-user-notifications", - ) - @action( - methods=["PUT"], - detail=False, - ) - def update_notification(self, request, org_id, channel_id, member_id): - """Update user notification preferences for a channel - - bash - curl -X PUT "{{baseUrl}}v1/{{org_id}}/channels/{{channel_id}}/members/{{member_id}}/notifications/" - -H "accept: application/json" - -H "Content-Type: application/json" - -d "{ - \"web\": \"all\", - \"mobile\": \"all\", - \"same_for_mobile\": true, - \"mute\": true - }" - - """ - - channel = self.retrieve_channel(request, org_id, channel_id) - if channel.__contains__("_id"): - user_data = channel["users"].get(member_id) - - if user_data: - serializer = NotificationsSettingSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - - # by default, users do not have a settings field - # whether or not this user has a settings field, - # make an update with the new settings - notification_settings = dict(serializer.data) - user_data.setdefault("notifications", {}).update(notification_settings) - - # push the updated user details to the channel object - channel["users"].update({f"{member_id}": user_data}) - - # remove channel id to avoid changing it - channel.pop("_id", None) - - payload = {"users": channel["users"]} - result = Request.put( - org_id, "channel", payload=payload, object_id=channel_id - ) - - if result: - if isinstance(result, dict): - data = ( - notification_settings if not result.get("error") else result - ) - status_code = ( - status.HTTP_201_CREATED - if not result.get("error") - else status.HTTP_400_BAD_REQUEST - ) - - return Response(data, status=status_code) - else: - return Response(result, status=result.status_code) - - return Response( - {"error": "member not found"}, status=status.HTTP_404_NOT_FOUND - ) - return Response( - {"error": "channel not found"}, status=status.HTTP_404_NOT_FOUND - ) + # def prepare_params(self): + # param_checkers = { + # "__starts": "$", + # "__ends": "#", + # "__contains": "*", + # "__gt": ">", + # "__lt": "<", + # } + + # params = dict(self.request.query_params) + + # """ + # Note if your planing to use the filterwrapper class + # you have to convert the values of your query_parameter + # to a python value by using json.loads + # """ + + # for key in self.request.query_params.keys(): + # try: + # params[key] = json.loads(params.get(key)[0]) + # except: # noqa + # params[key] = params.get(key)[0] + + # for chk in param_checkers: + # if key.endswith(chk): + # p = param_checkers[chk] + key.replace(chk, "") + + # try: + # params[p] = json.loads(params.get(key)) + # except: # noqa + # params[p] = params.get(key) + # params.pop(key) + # return params + + # def filter_objects(self, data: list, serializer: serializers.Serializer): + # # method applies filteration to user list + # output = [] + + # params = self.prepare_params() + # params = FilterWrapper.filter_params( + # allowed=list(serializer().get_fields().keys()), params=params + # ) + + # output = FilterWrapper.filter_objects(data, params) + # return output @swagger_auto_schema( request_body=UserSerializer, @@ -780,17 +636,6 @@ def add_member(self, request, org_id, channel_id): channel_id=channel_id, user=output, ) - - try: - request_finished.send( - sender=None, - dispatch_uid="UpdateSidebarSignal", - org_id=org_id, - user_id=user.get("_id"), - ) - except: # noqa - pass - else: # when output is a list multiple users where added request_finished.send( @@ -1076,16 +921,6 @@ def remove_member(self, request, org_id, channel_id, member_id): channel_id=channel_id, user=user_data.copy(), ) - try: - request_finished.send( - sender=None, - dispatch_uid="UpdateSidebarSignal", - org_id=org_id, - user_id=user_data.get("_id"), - ) - except: # noqa - pass - status_code = ( status.HTTP_204_NO_CONTENT if not result.get("error") @@ -1100,6 +935,127 @@ def remove_member(self, request, org_id, channel_id, member_id): {"error": "Channel not found"}, status=status.HTTP_404_NOT_FOUND ) + @swagger_auto_schema( + responses={ + 200: openapi.Response("Response", NotificationsSettingSerializer), + 404: openapi.Response("Not Found"), + }, + operation_id="retrieve-user-notifications", + ) + @action( + methods=["GET"], + detail=False, + ) + def notification_retrieve(self, request, org_id, channel_id, member_id): + """Retrieve user notification preferences for channel + + ```bash + curl -X GET "{{baseUrl}}/v1/{{org_id}}/channels/{{channel_id}}/members/{{member_id}}/notifications/" -H "accept: application/json" + ``` + """ # noqa + channel = self.retrieve_channel(request, org_id, channel_id) + if channel.__contains__("_id"): + user_data = channel["users"].get(member_id) + if user_data: + serializer = UserSerializer(data=user_data) + serializer.is_valid(raise_exception=True) + + # an empty field will be returned for users that have not + # changed their settings. + # DEFAULT_SETTINGS = { + # "web": "nothing", + # "mobile": "mentions", + # "same_for_mobile": False, + # "mute": False + # } + + settings = serializer.data.get("notifications", {}) + return Response(settings, status=status.HTTP_200_OK) + + return Response( + {"error": "member not found"}, status=status.HTTP_404_NOT_FOUND + ) + return Response( + {"error": "channel not found"}, status=status.HTTP_404_NOT_FOUND + ) + + @swagger_auto_schema( + request_body=NotificationsSettingSerializer, + responses={ + 200: openapi.Response( + "Response", + NotificationsSettingSerializer, + ) + }, + operation_id="update-user-notifications", + ) + @action( + methods=["PUT"], + detail=False, + ) + def notification_update(self, request, org_id, channel_id, member_id): + """Update user notification preferences for a channel + + ```bash + curl -X PUT "{{baseUrl}}v1/{{org_id}}/channels/{{channel_id}}/members/{{member_id}}/notifications/" + -H "accept: application/json" + -H "Content-Type: application/json" + -d "{ + \"web\": \"all\", + \"mobile\": \"all\", + \"same_for_mobile\": true, + \"mute\": true + }" + ``` + """ + + channel = self.retrieve_channel(request, org_id, channel_id) + if channel.__contains__("_id"): + user_data = channel["users"].get(member_id) + + if user_data: + serializer = NotificationsSettingSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + # by default, users do not have a settings field + # whether or not this user has a settings field, + # make an update with the new settings + notification_settings = dict(serializer.data) + user_data.setdefault("notifications", {}).update(notification_settings) + + # push the updated user details to the channel object + channel["users"].update({f"{member_id}": user_data}) + + # remove channel id to avoid changing it + channel.pop("_id", None) + + payload = {"users": channel["users"]} + result = Request.put( + org_id, "channel", payload=payload, object_id=channel_id + ) + + if result: + if isinstance(result, dict): + data = ( + notification_settings if not result.get("error") else result + ) + status_code = ( + status.HTTP_201_CREATED + if not result.get("error") + else status.HTTP_400_BAD_REQUEST + ) + + return Response(data, status=status_code) + else: + return Response(result, status=result.status_code) + + return Response( + {"error": "member not found"}, status=status.HTTP_404_NOT_FOUND + ) + return Response( + {"error": "channel not found"}, status=status.HTTP_404_NOT_FOUND + ) + channel_members_can_input_view = ChannelMemberViewset.as_view( { @@ -1107,9 +1063,6 @@ def remove_member(self, request, org_id, channel_id, member_id): } ) -notification_views = ChannelMemberViewset.as_view( - {"get": "retrieve_notification", "put": "update_notification"} -) channel_members_list_create_views = ChannelMemberViewset.as_view( { @@ -1122,20 +1075,6 @@ def remove_member(self, request, org_id, channel_id, member_id): {"get": "get_member", "put": "update_member", "delete": "remove_member"} ) - -@api_view(["POST", "GET"]) -# @permission_classes(["IsAdmin"]) -def handle_channel_permissions(request, org_id, channel_id): - if request.method == "GET": - data = find_match_in_db( - org_id, "channelpermissions", "channel_id", channel_id, return_data=True - ) - return Response(data, status=status.HTTP_200_OK) - serializer = ChannelPermissions(data=request.data) - if serializer.is_valid(): - payload = dict(serializer.validated_data) - payload.update({"channel_id": channel_id}) - data = manage_channel_permissions(org_id, channel_id, payload) - - return Response(data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +notification_views = ChannelMemberViewset.as_view( + {"get": "notification_retrieve", "put": "notification_update"} +) \ No newline at end of file diff --git a/backend/channel_plugin/apps/threads/views.py b/backend/channel_plugin/apps/threads/views.py index 5a218c5f3..ffd504157 100644 --- a/backend/channel_plugin/apps/threads/views.py +++ b/backend/channel_plugin/apps/threads/views.py @@ -7,7 +7,7 @@ from rest_framework.throttling import AnonRateThrottle from channel_plugin.utils.customexceptions import ThrottledViewSet -from channel_plugin.utils.customrequest import Request , get_thread_from_message +from channel_plugin.utils.customrequest import Request from channel_plugin.utils.wrappers import OrderMixin from django.utils.timezone import datetime @@ -17,7 +17,6 @@ from .permissions import CanReply, IsMember, IsOwner from .serializers import ThreadSerializer, ThreadUpdateSerializer, ReactionSerializer -from rest_framework.decorators import api_view class ThreadViewset(ThrottledViewSet, OrderMixin): @@ -388,19 +387,3 @@ def update_thread_reaction(self, request, org_id, thread_id): "delete": "thread_message_delete", } ) - - -@api_view(["GET","POST"]) -def paginate_threads_messages(request, org_id, channel_id): - DEFAULT_PAGE_SIZE = 10 - page = int(request.GET.get("page", 1)) - last_timestamp = request.GET.get("last_timestamp", None) - page_size = int(request.GET.get("page_size", DEFAULT_PAGE_SIZE)) - user_id = request.GET.get("user_id", "error") - - - data = {"page": page, "last_timestamp": last_timestamp, "page_size":page_size} - response = get_thread_from_message(org_id, "thread", channel_id, page, page_size) - - - return Response(response, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/backend/channel_plugin/channel_plugin/utils/customrequest.py b/backend/channel_plugin/channel_plugin/utils/customrequest.py index 23f536492..78c3a2779 100644 --- a/backend/channel_plugin/channel_plugin/utils/customrequest.py +++ b/backend/channel_plugin/channel_plugin/utils/customrequest.py @@ -98,7 +98,8 @@ def put(org_id, collection_name, payload, data_filter=None, object_id=None): else: if object_id is None: return {"error": "Object ID must be set for multiple payload"} - data.update({"filter": {"_id": object_id}}) + data.update({"object_id": object_id}) + response = requests.put(write, data=json.dumps(data)) if response.status_code >= 200 and response.status_code < 300: if not bulk_write: @@ -159,9 +160,7 @@ def search_db(org_id, channel_id, collection_name, **params): return {"error": response.json()} -def get_messages_from_page( - org_id, collection_name, channel_id, page, page_size, site_host=None -): +def get_messages_from_page(org_id, collection_name, channel_id, page, page_size, site_host=None): data = { "plugin_id": settings.PLUGIN_ID, "organization_id": org_id, @@ -171,39 +170,40 @@ def get_messages_from_page( {"channel_id": {"$eq": channel_id}}, ] }, - "options": {}, - } - skips = page_size * (page - 1) + "options" : { - data["options"].update( - { - "skip": skips, - "limit": page_size, } - ) + } + skips = page_size * (page - 1) + + data["options"].update({ + "skip" : skips, + "limit" : page_size, + }) + response = requests.post(read, data=json.dumps(data)) + data = response.json() pg_links = gen_page_links(org_id, "userscroll", channel_id, page, page_size) - + for i in pg_links: - if pg_links[i] is not None: + if pg_links[i] != None: try: pg_links[i] = site_host + pg_links[i] - except: # noqa + except: pass - data["links"] = pg_links - return data + data['links'] = pg_links + + return data def gen_page_links(org_id, collection_name, channel_id, cur_page, page_size): - new_url = reverse( - "paginate_messages", kwargs={"org_id": org_id, "channel_id": channel_id} - ) + new_url = reverse("paginate_messages",kwargs= {"org_id":org_id, "channel_id":channel_id}) data = { "plugin_id": settings.PLUGIN_ID, "organization_id": org_id, @@ -213,67 +213,57 @@ def gen_page_links(org_id, collection_name, channel_id, cur_page, page_size): {"channel_id": {"$eq": channel_id}}, ] }, - "options": {}, - } - - skips = page_size * (cur_page - 1) + 1 - - data["options"].update( - { - "skip": skips, - "limit": page_size, - } - ) - - response = requests.post(read, data=json.dumps(data)) - data = response.json() - if cur_page > 1: prev_link = new_url + f"?page={cur_page - 1}&?page_size={page_size}" pass else: prev_link = None try: - if not data["data"]: - next_link = None + if data['data']: + # next_link = pass else: next_link = new_url + f"?page={cur_page + 1}&page_size={page_size}" - except: # noqa + except: print("Error RetrIEVEING DATA") pass - data_links = {"prev": prev_link, "next": next_link} + data_links = {"prev":prev_link, "next":next_link} return data_links + def save_last_message_user(org_id, collection_name, payload): - data = { - "plugin_id": settings.PLUGIN_ID, - "organization_id": org_id, - "collection_name": collection_name, - "bulk_write": False, - "payload": payload, - } - if find_match_in_db(org_id, collection_name, "user_id", payload["user_id"]): - data["bulk_write"] = True - data.update({"filter": {"user_id": {"$eq": payload["user_id"]}}}) - requests.put(write, data=json.dumps(data)) + data = { + "plugin_id": settings.PLUGIN_ID, + "organization_id": org_id, + "collection_name": collection_name, + "bulk_write": False, + "payload": payload, + } + if find_match_in_db(org_id, collection_name, "user_id", payload['user_id']): + data["bulk_write"]= True + data.update({"filter": {"user_id": + {"$eq": payload["user_id"]} + } + }) + r = requests.put(write, data= json.dumps(data)) print("UPDATED") - + else: - requests.post(write, data=json.dumps(data)) - print("Created new") - match = find_match_in_db(org_id, collection_name, "user_id", payload["user_id"]) - if match is None: - requests.post(write, data=json.dumps(data)) + r = requests.post(write, data = json.dumps(data)) + print("Created new") + +def find_match_in_db(org_id, collection_name, param, value, return_data=False): + match = find_match_in_db(org_id, collection_name, "user_id", payload['user_id']) + if match == None: + r = requests.post(write, data = json.dumps(data)) print("Created new") else: - data.update({"object_id": payload["user_id"]}) - requests.put(read, data=json.dumps(data)) + data.update({"object_id":payload['user_id']}) + r = requests.put(read, data= json.dumps(data)) print("Updated") - -def find_match_in_db(org_id, collection_name, param, value, return_data=False): +def find_match_in_db(org_id, collection_name, param, value): data = { "plugin_id": settings.PLUGIN_ID, "organization_id": org_id, @@ -289,77 +279,24 @@ def find_match_in_db(org_id, collection_name, param, value, return_data=False): response_data = response.json() print(response_data) try: - if response_data: - return response_data["data"] - if response_data["data"] is not None: + if return_data: + return response_data['data'] + if response_data['data'] != None: print("We made a match") return True - except: # noqa + except: print("No match") return None + response = requests.get(read, data=json.dumps(data)) + response_data = response + print(response_data) + if response.ok: + print("We made a match") + return True -def manage_channel_permissions(org_id, channel_id, payload): - - - collection_name = "channelpermissions" - data = { - "plugin_id": settings.PLUGIN_ID, - "organization_id": org_id, - "collection_name": collection_name, - "filter": { - "$and": [ - {"channel_id": {"$eq": channel_id}}, - ] - }, - "bulk_write": False, - "payload": payload - } - - if find_match_in_db(org_id, "channelpermissions", "channel_id", channel_id): - data['bulk_write'] = True - response = requests.put(write, data= json.dumps(data)) - return response.json() - response = requests.post(write, data= json.dumps(data)) - return response.json() - -def get_channel_permissions(org_id, channel_id): - pass - -def get_thread_from_message(org_id, collection_name, channelmessage_id, page, page_size): - data = { - "plugin_id": settings.PLUGIN_ID, - "organization_id": org_id, - "collection_name": collection_name, - "filter": { - "$and": [ - {"channel_id": {"$eq": channel_id}}, - ] - }, - "options": {}, - } - - skips = page_size * (page - 1) - - data["options"].update( - { - "skip": skips, - "limit": page_size, - } - ) - - response = requests.post(read, data=json.dumps(data)) - - data = response.json() - pg_links = gen_page_links(org_id, "userscroll", channel_id, page, page_size) - - for i in pg_links: - if pg_links[i] is not None: - try: - pg_links[i] = site_host + pg_links[i] - except: # noqa - pass + else: + print("No match") + return None - data["links"] = pg_links - return data \ No newline at end of file diff --git a/backend/channel_plugin/channel_plugin/utils/middleware.py b/backend/channel_plugin/channel_plugin/utils/middleware.py index fc4664d00..ee9483cc7 100644 --- a/backend/channel_plugin/channel_plugin/utils/middleware.py +++ b/backend/channel_plugin/channel_plugin/utils/middleware.py @@ -1,8 +1,6 @@ # from django import http # from django.conf import settings -from django.http import JsonResponse -from rest_framework import status -from rest_framework.response import Response +# from django.http import JsonResponse import logging @@ -11,9 +9,6 @@ def __init__(self, get_response): self.get_response = get_response def __call__(self, request): - if "Authorization" not in request.headers: - print({"message": "This user is not authenticated"}) - response = self.get_response(request) return response diff --git a/backend/channel_plugin/config/settings/base.py b/backend/channel_plugin/config/settings/base.py index de00379aa..be5f2f72b 100644 --- a/backend/channel_plugin/config/settings/base.py +++ b/backend/channel_plugin/config/settings/base.py @@ -132,7 +132,7 @@ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", - "channel_plugin.utils.middleware.AuthenticationMiddleware", + # "channel_plugin.utils.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.common.BrokenLinkEmailsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", diff --git a/backend/channel_plugin/config/urls.py b/backend/channel_plugin/config/urls.py index 47b8a11c0..4ba6b7fb4 100644 --- a/backend/channel_plugin/config/urls.py +++ b/backend/channel_plugin/config/urls.py @@ -63,8 +63,7 @@ def render_react(request): path("api/v1/", include("apps.channelmessages.urls")), path("api/v1/", include("apps.roles.urls")), path("api/v1/", include("apps.threads.urls")), - path("api/v1/", include("apps.googlemeet.urls")), - path("api/v1/", include("apps.centri.urls")), + path("api/v1/", include("apps.centri.urls")) # DRF auth token # path("auth-token/", obtain_auth_token), ] diff --git a/backend/channel_plugin/schema.json b/backend/channel_plugin/schema.json index e27e592cf..d8af5eba9 100644 --- a/backend/channel_plugin/schema.json +++ b/backend/channel_plugin/schema.json @@ -11,14 +11,12 @@ "200": { "id": "200", "role_id": "198", - "is_admin": false, - "starred": true + "is_admin": false }, "202": { "id": "202", "role_id": "199", - "is_admin": true, - "starred": true + "is_admin": true } }, "created_on": "12-08-2021 12:00:00" diff --git a/frontend/main/src/components/ChannelBrowser/index.jsx b/frontend/main/src/components/ChannelBrowser/index.jsx index 8657f5031..e50339772 100644 --- a/frontend/main/src/components/ChannelBrowser/index.jsx +++ b/frontend/main/src/components/ChannelBrowser/index.jsx @@ -1,3 +1,4 @@ +import { AddIcon } from "@chakra-ui/icons"; import { Box } from "@chakra-ui/layout"; import React, { useEffect, useState } from "react"; import ChannelBrowserHeader from "./ChannelBrowserHeader"; @@ -7,7 +8,6 @@ import { useDispatch, useSelector } from "react-redux"; import SearchMenu from "./SearchMenu"; import ChannelList from "./ChannelList"; import PluginHeaderC from "../createChannel/homeHeader"; -import FloatingButton from "./FloatingButton"; const ChannelBrowser = () => { const { users } = useSelector((state) => state.appReducer); @@ -36,8 +36,6 @@ const ChannelBrowser = () => { {/* */} - - { {/* Mobile View to Add Channel */} - - + ); }; diff --git a/frontend/main/src/components/EditDescriptionModal/index.jsx b/frontend/main/src/components/EditDescriptionModal/index.jsx index 21e4f25ee..74856992b 100644 --- a/frontend/main/src/components/EditDescriptionModal/index.jsx +++ b/frontend/main/src/components/EditDescriptionModal/index.jsx @@ -1,4 +1,5 @@ -import cancelIcon from '../../assets/cancel_icon.png'; +import cancelIcon from '../../assets/cancel_icon.png' + import { Modal, ModalOverlay, @@ -15,7 +16,7 @@ import { Center, Box, } from "@chakra-ui/react" -import React, { useState } from 'react'; +import React, { useState } from 'react' /* @@ -27,21 +28,24 @@ the modal requires saveDescription prop which should be implemented as a function that receives 'description' as parameter. The description parameter is the content of the text area after the user has clicked save. A sample of such function is shown below + +function saveDescription(description){ + console.log(description) + } */ export default function EditDescriptionModal(props) { //picking up the prop - const {saveDescription, openEditModal} = props; + const {saveDescription} = props //Chakra specific hook for handling modal opening and closing const { isOpen, onOpen, onClose } = useDisclosure() const initialRef = React.useRef() - const [description, setDescription] = useState(''); - + const [description, setDescription] = useState('') //handles text area change event - function onTextAreaChange(e){ - setDescription(e.target.value); + function onTextAreaChange(event){ + setDescription(event.target.value) } @@ -49,9 +53,9 @@ has clicked save. A sample of such function is shown below <> {/* The button is what get's rendered when this component is mounted */} - {/* */} + - diff --git a/frontend/main/src/components/MessageBoard/MessageBoardIndex.jsx b/frontend/main/src/components/MessageBoard/MessageBoardIndex.jsx index 753636a9f..51a579621 100644 --- a/frontend/main/src/components/MessageBoard/MessageBoardIndex.jsx +++ b/frontend/main/src/components/MessageBoard/MessageBoardIndex.jsx @@ -21,10 +21,13 @@ import Centrifuge from 'centrifuge'; import { SubscribeToChannel } from '@zuri/control' +//notifications +import notificationsManager from "./subs/Centrifugo/NotificationsManager"; -const MessageBoardIndex = ({allUsers}) => { + +const MessageBoardIndex = () => { const { channelId } = useParams(); const dispatch = useDispatch() @@ -32,9 +35,15 @@ const MessageBoardIndex = ({allUsers}) => { const { channelDetails } = useSelector((state) => state.channelsReducer); const { channelMessages, sockets, renderedMessages, users } = useSelector((state) => state.appReducer) - const { _getChannelMessages, _getSocket } = bindActionCreators(appActions, dispatch) + const { _getChannelMessages, _getSocket, _getNotifications } = bindActionCreators(appActions, dispatch) const canInput = channelDetails.allow_members_inpu + const [ orgId, setOrgId ] = useState() + + + + + // We will attempt to connect only when we are certain that the state has been updated // so we first check that sockets.socket_name is not undefined @@ -50,6 +59,7 @@ const MessageBoardIndex = ({allUsers}) => { case 'join:channel' || 'leave:channel' || 'create:message' :{ dispatch({ type: GET_CHANNELMESSAGES, payload: [...channelMessages, messageCtx.data] }) + notificationsManager(messageCtx.data.content) break; } @@ -62,7 +72,7 @@ const MessageBoardIndex = ({allUsers}) => { return true; // stop searching } }); - + dispatch({ type: GET_CHANNELMESSAGES, payload: channelMessagesCopy }) break; } @@ -123,6 +133,22 @@ const MessageBoardIndex = ({allUsers}) => { }, [channelId]); + useEffect(() => { + if(users){ + setOrgId(users[0]) + } + }, []) + + const retrieveNotificationSettings = () =>{ + _getNotifications(orgId?.org_id, channelId, orgId?._id) + } + + useEffect(() =>{ + if(orgId){ + retrieveNotificationSettings() + } + }) + return ( @@ -132,7 +158,7 @@ const MessageBoardIndex = ({allUsers}) => { m="5px" bg="white" overflowY="scroll" - height={["83vh", "85vh", "70vh", "68vh"]} + height={["83vh", "85vh", "65vh", "58vh"]} css={{ "&::-webkit-scrollbar": { width: "0", @@ -143,7 +169,7 @@ const MessageBoardIndex = ({allUsers}) => { }} > - + {channelDetails.allow_members_input ? : } diff --git a/frontend/main/src/components/MessageBoard/subs/Centrifugo/NotificationsManager.js b/frontend/main/src/components/MessageBoard/subs/Centrifugo/NotificationsManager.js new file mode 100644 index 000000000..a0c5873a8 --- /dev/null +++ b/frontend/main/src/components/MessageBoard/subs/Centrifugo/NotificationsManager.js @@ -0,0 +1,17 @@ +import { useSelector } from "react-redux" + +const notificationsManager = (data) => { + + const { channelDetails, userNotificationSettings } = useSelector(state => state.channelReducer) + + + if(userNotificationSettings.web === "all" || userNotificationSettings.web === "mentions"){ + const notification = new Notification(`Notifications from ${channelDetails.name}`, { + body: data + }) + } + +} + + +export default notificationsManager; \ No newline at end of file diff --git a/frontend/main/src/components/MessageBoard/subs/MessageCardContainer/MessageCardContainer.jsx b/frontend/main/src/components/MessageBoard/subs/MessageCardContainer/MessageCardContainer.jsx index 61f2b68b2..ab797cc62 100644 --- a/frontend/main/src/components/MessageBoard/subs/MessageCardContainer/MessageCardContainer.jsx +++ b/frontend/main/src/components/MessageBoard/subs/MessageCardContainer/MessageCardContainer.jsx @@ -1,89 +1,88 @@ - - -import React, { useEffect, useState, useRef } from 'react' -import { Box, Text, Flex } from '@chakra-ui/layout' -import { Button } from '@chakra-ui/button' -import { FaCaretDown } from "react-icons/fa"; -import { useParams } from 'react-router'; -import { useHistory } from 'react-router'; -import { Image } from "@chakra-ui/react" -import Spinner from '../../../../../src/assets/spinner.gif' - -import APIService from "../../../../utils/api"; - -//redux -import { useDispatch, useSelector } from "react-redux"; -import { bindActionCreators } from "redux"; -import appActions from '../../../../redux/actions/app'; - -// import MessageCard from "../MessageCard/MessageCard"; -import MessageCard from '../../../shared/MessageCard'; -import EmptyStateComponent from '../../../createChannel/EmptyStateComponent'; - -//centrifuge -import Centrifuge from 'centrifuge' -import { GET_RENDEREDMESSAGES } from '../../../../redux/actions/types'; - - -const MessageCardContainer = ({ channelId,allUsers }) => { - - const dispatch = useDispatch() - const history = useHistory() - const { _getChannelMessages, _getSocket } = bindActionCreators(appActions, dispatch) - const { channelMessages, sockets, renderedMessages, users } = useSelector((state) => state.appReducer) - - const [allChannelMessage, setAllChannelMessage] = useState() - const [moreMessages, setMoreMessages] = useState(false) - const noOfMessages = 20; - - const messageRef = useRef(); - - useEffect(() => { - if (messageRef.current) { - messageRef.current.scrollIntoView( - - { - behavior: 'smooth', - block: 'end', - inline: 'nearest' - }) - } - }) - - - useEffect(() => { - console.log("\n\n\nUseEffect works\n\n\n"); - const loadData = async () => { - _getChannelMessages(users.currentWorkspace, channelId) - } - loadData() - }, [channelId]); - - - return ( - <> - - - {channelMessages && channelMessages.length > 0 && - - {channelMessages && channelMessages.length > 0 && - channelMessages.map((message) => { - return ( - message === [] ? Loading... : - - ) - }) - } - - } - - - - ) -} - -export default MessageCardContainer; - - + + +import React, { useEffect, useState, useRef } from 'react' +import { Box, Text, Flex } from '@chakra-ui/layout' +import { Button } from '@chakra-ui/button' +import { FaCaretDown } from "react-icons/fa"; +import { useParams } from 'react-router'; +import { useHistory } from 'react-router'; +import { Image } from "@chakra-ui/react" +import Spinner from '../../../../../src/assets/spinner.gif' + +import APIService from "../../../../utils/api"; + +//redux +import { useDispatch, useSelector } from "react-redux"; +import { bindActionCreators } from "redux"; +import appActions from '../../../../redux/actions/app'; + +// import MessageCard from "../MessageCard/MessageCard"; +import MessageCard from '../../../shared/MessageCard'; +import EmptyStateComponent from '../../../createChannel/EmptyStateComponent'; + +//centrifuge +import Centrifuge from 'centrifuge' +import { GET_RENDEREDMESSAGES } from '../../../../redux/actions/types'; + + +const MessageCardContainer = ({ channelId,allUsers, org_id }) => { + + const dispatch = useDispatch() + const history = useHistory() + const { _getChannelMessages, _getSocket } = bindActionCreators(appActions, dispatch) + const { channelMessages, sockets, renderedMessages, users } = useSelector((state) => state.appReducer) + + const [allChannelMessage, setAllChannelMessage] = useState() + const [moreMessages, setMoreMessages] = useState(false) + const noOfMessages = 20; + + const messageRef = useRef(); + + useEffect(() => { + if (messageRef.current) { + messageRef.current.scrollIntoView( + + { + behavior: 'smooth', + block: 'end', + inline: 'nearest' + }) + } + }) + + + useEffect(() => { + console.log("\n\n\nUseEffect works\n\n\n"); + const loadData = async () => { + _getChannelMessages(users.currentWorkspace, channelId) + } + loadData() + }, [channelId]); + + + return ( + <> + + + {channelMessages && channelMessages.length > 0 && + + {channelMessages && channelMessages.length > 0 && + channelMessages.map((message) => { + return ( + message === [] ? Loading... : + + ) + }) + } + + } + + + + ) +} + +export default MessageCardContainer; + diff --git a/frontend/main/src/components/UserProfile/OnclickuserProfile.jsx b/frontend/main/src/components/UserProfile/OnclickuserProfile.jsx index 03211a3a3..7d21a7c02 100644 --- a/frontend/main/src/components/UserProfile/OnclickuserProfile.jsx +++ b/frontend/main/src/components/UserProfile/OnclickuserProfile.jsx @@ -1,6 +1,4 @@ import React, { useState } from "react"; -// import Picker from "emoji-picker-react"; - import { Box, IconButton, @@ -12,18 +10,18 @@ import { } from "@chakra-ui/react"; import { FaTimes } from "react-icons/fa"; import { BiMessageRoundedDetail } from "react-icons/bi"; -// import { IoNotificationsOffCircleOutline } from "react-icons/io"; -import { MdNotificationsOff } from "react-icons/md"; +import { FiPhoneCall } from "react-icons/fi"; import { CgMore } from "react-icons/cg"; import smileEmoji from '../images/emoji-smile.png'; import Picker from "emoji-picker-react"; +// import UserProfileOnHover from "./UserProfileOnHover"; const OnClickUserProfile = ({showProfile ,setShowProfile }) => { const data = { imageUrl: - "https://s3-alpha-sig.figma.com/img/4d94/53d2/8f82ff6751f10eb228f58c9994598a9b?Expires=1633910400&Signature=Xhnnkxdar7-DRe3Z6MjbAro3Mf7I8~6gSN3X548sBShE2sCOd7FXWWqpU1vM~jPglJZcoefRPTgF0kP8U-8XRLBZ~gG2FYein3KHf7tPs02oYfc~lmSBHA7uKrP1-BaPpMIgiLNV1AaJU08qp~VQCljgWr7hWtzPM-dYk-4fKGzxpxPz3duO1nT8tkBsg0iviEUbtAV87kj7Kd7iZE0cN863zKca71gfyxAloV4co~MUeWsB4tTYlQkkLeeYAYBZM5mF~6m63UpTjKMYUWcP1h8SwmoBEsitlFqq4g5-zisKGTiwggJIU0sEsKhZCuCnjTykENLac7vgpvvoTlm7RQ__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA", + "https://s3-alpha-sig.figma.com/img/d4a3/e257/f13a3cdb05ca74c9abf2933974335778?Expires=1631491200&Signature=JtDbDf-1BE7WkG0QO5i2-h-UhB8HF69Fr~QoJ0-wxPGvakH45P3R4xWFvrKRkpdqzcZWLl~aoWehGnocI-VTbns~3GT2rGw69rNnbWEOEdOlPf2RHkgceFJwzC6jma00vO1ROq3MMThgHdL0oVCLLmQV7XVgcq7RDUULJxrlrSqBffyTBjk-nuic0ONndBtT~nitN0WBUH8lKAoljTdErKZw0ucFGKC4xyfVdWGmT8w0~NRHvR6zy-3e48uxcvJ-8jIMFNJVlQ4jjnY1rXlnSWIataD0t6bJwmCGgGK7t-nADuJJtv9Vk9v31athvVG7z95o~naOyVOJabkpZoGIZw__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA", imageAlt: "profile-image", name: "Deryin Cutting", details: "View full profile", @@ -34,7 +32,6 @@ const OnClickUserProfile = ({showProfile ,setShowProfile }) => { const [toggle, setToggle] = useState(false) - const [showPicker, setShowPicker] = useState(false); const [chosenEmoji, setChosenEmoji] = useState(); @@ -47,18 +44,16 @@ const OnClickUserProfile = ({showProfile ,setShowProfile }) => { return ( <> - {showProfile && - - - + Profile setShowProfile(false)} _hover='none' icon={ } /> - - - + + + { pt='20px' ml="auto" mr="auto" - mt="30px" - /> + mt="30px" + /> - + {/* Users' Name */} - {data.name} {/* Active indicator */} - - {/* Users' Status detail */} - {data.statusDetail} - + {/* Users' Image */} - {chosenEmoji ? chosenEmoji.emoji : {imageAlt} } - {showPicker && - } - - - + {chosenEmoji ? chosenEmoji.emoji :} + {showPicker && } + + + { Message - + - - { } + icon={} background="#F0FDF9" width="40px" height="40px" @@ -138,10 +126,22 @@ const OnClickUserProfile = ({showProfile ,setShowProfile }) => { /> - Mute - - - + Call + + + + } + background="#F0FDF9" + width="40px" + height="40px" + borderRadius="12px" + ml="24px" + _hover='none' + /> + Call + + } @@ -153,9 +153,8 @@ const OnClickUserProfile = ({showProfile ,setShowProfile }) => { onClick={() => setToggle(!toggle)} /> More - - - + + Display Name {data.name} @@ -164,23 +163,15 @@ const OnClickUserProfile = ({showProfile ,setShowProfile }) => { Email {data.email} - - - - - - - {toggle && MoreBtnOnHover()} - - -} - - - + + + + + {toggle && MoreBtnOnHover()} + } ); } - export default OnClickUserProfile; function MoreBtnOnHover() { diff --git a/frontend/main/src/components/UserProfile/UserProfileOnHover.jsx b/frontend/main/src/components/UserProfile/UserProfileOnHover.jsx index 3749a43ad..a34e23c8a 100644 --- a/frontend/main/src/components/UserProfile/UserProfileOnHover.jsx +++ b/frontend/main/src/components/UserProfile/UserProfileOnHover.jsx @@ -1,4 +1,4 @@ -import React, {useState} from "react"; +import React from "react"; import { Box } from "@chakra-ui/layout"; import { Text, @@ -6,7 +6,6 @@ import { Image, Link, Button } from "@chakra-ui/react"; import smileEmoji from '../images/emoji-smile.png' -// import UserStatusSettings from './UserStatusSettings' @@ -24,16 +23,6 @@ import smileEmoji from '../images/emoji-smile.png' time: "10:00AM", }; - const [showPicker, setShowPicker] = useState(false); - const [chosenEmoji, setChosenEmoji] = useState(); - - const handleStatus = () => { - setShowPicker(!showPicker) - }; - const onEmojiClick = (event, emojiObject) => { - setChosenEmoji(emojiObject); - }; - return ( @@ -67,12 +56,7 @@ import smileEmoji from '../images/emoji-smile.png' Status - {/* */} - - {chosenEmoji ? chosenEmoji.emoji : {imageAlt} } - {showPicker && - } - + Local Time {data.time} diff --git a/frontend/main/src/components/channelDetailsAndSetting/about.jsx b/frontend/main/src/components/channelDetailsAndSetting/about.jsx index 7b73314c7..6fb688c87 100644 --- a/frontend/main/src/components/channelDetailsAndSetting/about.jsx +++ b/frontend/main/src/components/channelDetailsAndSetting/about.jsx @@ -1,16 +1,14 @@ import React, { useState, useEffect } from "react"; -import { FiEdit } from 'react-icons/fi'; -import { Stack, StackDivider, Spacer, Flex, HStack, List, ListItem, Heading, Box, Text } from '@chakra-ui/react'; +import { Stack, StackDivider, Spacer, Flex, HStack, List, ListItem, Heading, Box, Text, Link } from '@chakra-ui/react'; import axios from 'axios'; -//import instance from './../../utils/api'; -import moment from 'moment'; -import EditDescriptionModal from "../EditDescriptionModal"; -// import { _editChannel } from "../../redux/actions/app"; +import instance from './../../utils/api'; +import moment from 'moment' +import Edit from "../channelDetailsAndSetting/Edit" const About = (index) => { const [topic, setTopic] = useState("Add a topic"); - // const [description, setDescription] = useState("Add description"); + const [description, setDescription] = useState("Add description"); const [owner, setOwner] = useState("owner"); const [created_on, setCreatedOn] = useState("August 28, 2021"); @@ -95,40 +93,31 @@ const About = (index) => { setModalValue(name, description) console.log(name, description) } - - function saveDescription(description){ - //console.log(description); - // _editChannel(org_id, channel_id, data); - setDescription(""); - setOpenEditModal(false); - } return ( - <> - - + - } /> - } /> - - - - - + > + + } /> + } /> + + + ); } -const Info = ({ title, placeholder, index }) => { +const Info = ({ title, placeholder, edit, index }) => { return ( <> @@ -172,29 +161,4 @@ const Info = ({ title, placeholder, index }) => { ) } -const Edit = ({ clickHandler }) => { - return ( - clickHandler( - //data params - )} - > - - - - Edit - - - ) - -} - export default About; \ No newline at end of file diff --git a/frontend/main/src/components/channelDetailsAndSetting/channelDetailsAndSettings.jsx b/frontend/main/src/components/channelDetailsAndSetting/channelDetailsAndSettings.jsx index 95b2f3bd4..2fb00789f 100644 --- a/frontend/main/src/components/channelDetailsAndSetting/channelDetailsAndSettings.jsx +++ b/frontend/main/src/components/channelDetailsAndSetting/channelDetailsAndSettings.jsx @@ -24,9 +24,11 @@ import { FaRegBell } from "react-icons/fa"; import { FaChevronDown } from "react-icons/fa"; import { FaRegStar } from "react-icons/fa"; import { FaHashtag } from "react-icons/fa"; + import { useDispatch, useSelector } from "react-redux"; import appActions from "../../redux/actions/app"; import { bindActionCreators } from "redux"; + import OrganisationMembersList from "./organisationMembersList"; import About from "./about"; import FileList from "./fileList"; diff --git a/frontend/main/src/components/createChannel/EmptyStateComponent.jsx b/frontend/main/src/components/createChannel/EmptyStateComponent.jsx index e00f5c6dd..e03631500 100644 --- a/frontend/main/src/components/createChannel/EmptyStateComponent.jsx +++ b/frontend/main/src/components/createChannel/EmptyStateComponent.jsx @@ -13,7 +13,7 @@ import { useSelector } from "react-redux"; import UtilityService from "../../utils/utils"; import { Center } from "@chakra-ui/react" -const EmptyStateComponent = () => { +const EmptyStateComponent = ({org_id}) => { const { isOpen, onOpen, onClose } = useDisclosure(); const { @@ -29,7 +29,7 @@ const EmptyStateComponent = () => { return ( - +
diff --git a/frontend/main/src/components/createChannel/addPeopleModal.jsx b/frontend/main/src/components/createChannel/addPeopleModal.jsx index aa05ba1a9..5dd7c8903 100644 --- a/frontend/main/src/components/createChannel/addPeopleModal.jsx +++ b/frontend/main/src/components/createChannel/addPeopleModal.jsx @@ -31,7 +31,7 @@ const SelectUser = ({user}) => ( ) -const AddPeopleModal = ({ isOpen, onClose }) => { +const AddPeopleModal = ({ isOpen, onClose, org_id }) => { const [users, setUsers] = useState([]) const [value, setValue] = useState('') @@ -56,7 +56,7 @@ const AddPeopleModal = ({ isOpen, onClose }) => { } const { channelId } = useParams()//dynamic channel id - const org_id = '614679ee1a5607b13c00bcb7';//Test value for org id + // const org_id = '614679ee1a5607b13c00bcb7';//Test value for org id const channel_id = channelId; //assigning dynamic channel id to channel_id const dispatch = useDispatch(); // channel members diff --git a/frontend/main/src/components/shared/ChannelHeader.jsx b/frontend/main/src/components/shared/ChannelHeader.jsx index a0c1f75a9..4e9d2df5f 100644 --- a/frontend/main/src/components/shared/ChannelHeader.jsx +++ b/frontend/main/src/components/shared/ChannelHeader.jsx @@ -3,6 +3,7 @@ import { Box, HStack } from "@chakra-ui/layout"; import { Flex, Spacer, Avatar, AvatarGroup, Button, IconButton, Image, useDisclosure } from "@chakra-ui/react"; import { BiChevronDown, BiChevronLeft, BiLockAlt } from "react-icons/bi"; import { AiOutlineStar } from 'react-icons/ai'; +import { ImNotification } from "react-icons/im"; import { Icon } from "@chakra-ui/icon"; import { FiHash } from "react-icons/fi"; import { Link } from "react-router-dom"; @@ -17,6 +18,7 @@ import ChannelDetails from "../channelDetailsAndSetting/channelDetailsAndSetting import NewChannelHeader from "./pluginHeader"; import { useParams } from "react-router"; +import MoreNotificationModal from "./MoreNotificationModal"; //avatar details(Just a placeholder) const avatars = [ @@ -28,12 +30,11 @@ const avatars = [ const ChannelHeader = ({channelId, org_id}) => { // const { channelId } = useParams()//dynamic channel id - // const org_id = '614679ee1a5607b13c00bcb7';//Test value for org id const channel_id = channelId; //assigning dynamic channel id to channel_id console.log(channel_id); const dispatch = useDispatch(); - const { _getPinnedMessages } = bindActionCreators(appActions, dispatch); //extract redux function + const { _getPinnedMessages } = bindActionCreators(appActions, dispatch);//extract redux function //.......getting pinned messages...........// const { pinnedMessages } = useSelector((state) => state.channelsReducer); const { users } = useSelector((state) => state.appReducer); @@ -52,12 +53,56 @@ const ChannelHeader = ({channelId, org_id}) => { {pinnedMessages > 0 && ( - + )} }> + }> + + + {/* + <> + + + + + + + */} + {/* */} + + {/*Mobile responsive version */} + {/* + + + + {isPrivate === true ? : } + + {channelDetails.name} + + + {channelDetails.members} members + + + + + } aria-label="search"> + } aria-label='channel-details' color='#ffffff' /> + + */} ); }; @@ -65,7 +110,7 @@ const ChannelHeader = ({channelId, org_id}) => { const pinnedAndBookmarkButtonStyle = { bg: "#BCF9E6", height:'25px', - ml:'5px', + ml:'10px', borderRadius: "4px", size: "sm", fontWeight: "normal", diff --git a/frontend/main/src/components/shared/DeleteMessage.jsx b/frontend/main/src/components/shared/DeleteMessage.jsx index 9d91fe969..bd92d695b 100644 --- a/frontend/main/src/components/shared/DeleteMessage.jsx +++ b/frontend/main/src/components/shared/DeleteMessage.jsx @@ -13,7 +13,6 @@ import { Box, Image } from "@chakra-ui/react" - import {_deleteMessage} from "../../redux/actions/app"; function DeleteMessage() { const { isOpen, onOpen, onClose } = useDisclosure() @@ -106,7 +105,7 @@ import { > Cancel - - - - - } - - setData(e.target.value)} - onInput={()=>setOnInput(true)} - fontWeight={active} - fontStyle={italic} - onMouseDown={formatSelection} - onBlur={()=>setOnInput(false)} - /> - - - - - - - - - - - - - setEmoji(!emoji)}/> - { - (input || data!== "") ? : - } - - - - - - - { - click ? - - + + return ( + + + + + + + + { + emoji && + } + { _focus={{ border: "none" }} value={data} height="58px" - changeText={(e)=>setData(e.target.value)} - onInput={()=>setOnInput(true)} + changeText={(e) => setData(e.target.value)} + onInput={() => setOnInput(true)} fontWeight={active} fontStyle={italic} - onBlur={()=>setOnInput(false)} + onMouseDown={formatSelection} + onBlur={() => setOnInput(false)} /> - {(input || data!== "") ? ( - - ) : ( - - )} - - - - setEmoji(!emoji)} /> - - - - - - - + + + + + + + + + + + + + setEmoji(!emoji)} /> + { + (input || data !== "") ? : + } + + + + - : - setOnClick(true)}> - setData(e.target.value)} - onInput={()=>setOnInput(true)} - /> - - - - - + + { + click ? + + + setData(e.target.value)} + onInput={() => setOnInput(true)} + fontWeight={active} + fontStyle={italic} + onBlur={() => setOnInput(false)} + /> + {(input || data !== "") ? ( + + ) : ( + + )} + + + + setEmoji(!emoji)} /> + + + + + + + + + : + setOnClick(true)}> + setData(e.target.value)} + onInput={() => setOnInput(true)} + /> + + + + + + + } - } - ); - }; - - const MAX_HEIGHT = 200; - const MIN_HEIGHT = 58; - - const ResizableInput = ({ +}; + +const MAX_HEIGHT = 200; +const MIN_HEIGHT = 58; + +const ResizableInput = ({ textareaRef, changeText, onKeyUp = null, onBlur = null, onFocus = null, ...rest - }) => { +}) => { const fitToContent = (maxHeight) => { - const text = textareaRef?.current; - if (!text) return; - - var adjustedHeight = text.clientHeight; - if (!maxHeight || maxHeight > adjustedHeight) { - adjustedHeight = Math.max(text.scrollHeight, adjustedHeight); - if (maxHeight) adjustedHeight = Math.min(maxHeight, adjustedHeight); - if (adjustedHeight === maxHeight) - textareaRef.current.style.overflowY = "auto"; - if (adjustedHeight > text.clientHeight) - text.style.height = adjustedHeight + "px"; - } + const text = textareaRef?.current; + if (!text) return; + + var adjustedHeight = text.clientHeight; + if (!maxHeight || maxHeight > adjustedHeight) { + adjustedHeight = Math.max(text.scrollHeight, adjustedHeight); + if (maxHeight) adjustedHeight = Math.min(maxHeight, adjustedHeight); + if (adjustedHeight === maxHeight) + textareaRef.current.style.overflowY = "auto"; + if (adjustedHeight > text.clientHeight) + text.style.height = adjustedHeight + "px"; + } }; const keyUpEventHandler = () => { - if (onKeyUp) onKeyUp(); - fitToContent(MAX_HEIGHT); + if (onKeyUp) onKeyUp(); + fitToContent(MAX_HEIGHT); }; const blurEventHandler = () => { - if (onBlur) onBlur(); - textareaRef.current.style.height = MIN_HEIGHT + "px"; - textareaRef.current.scrollTo(0, 0); - textareaRef.current.style.overflowY = "hidden"; + if (onBlur) onBlur(); + textareaRef.current.style.height = MIN_HEIGHT + "px"; + textareaRef.current.scrollTo(0, 0); + textareaRef.current.style.overflowY = "hidden"; }; const focusEventHandler = () => { - if (onFocus) onFocus(); - fitToContent(MAX_HEIGHT); + if (onFocus) onFocus(); + fitToContent(MAX_HEIGHT); }; return ( -