diff --git a/zubhub_backend/zubhub/creators/utils.py b/zubhub_backend/zubhub/creators/utils.py index 0118138cb..64cafda31 100644 --- a/zubhub_backend/zubhub/creators/utils.py +++ b/zubhub_backend/zubhub/creators/utils.py @@ -1,27 +1,33 @@ from datetime import timedelta +from typing import Dict, List, Set + +from activitylog.models import Activitylog +from activitylog.utils import push_activity +from creators.models import Badge, Creator, Setting +from creators.tasks import ( + send_mass_email, + send_mass_text, + send_whatsapp, + upload_file_task, + upload_file_task_group, +) +from django.contrib.auth import get_user_model from django.contrib.postgres.search import SearchQuery, SearchRank -from django.db.models import F -from typing import List, Set, Dict, cast from django.core.exceptions import FieldDoesNotExist from django.db import transaction +from django.db.models import F, Sum + +# from django.template.loader import render_to_string from django.utils import timezone -from django.contrib.auth import get_user_model -from projects.tasks import delete_file_task -from creators.tasks import upload_file_task, send_mass_email, send_mass_text, send_whatsapp, upload_file_task_group -from creators.models import Setting from notifications.models import Notification -from activitylog.models import Activitylog -from activitylog.utils import push_activity -from notifications.utils import push_notification, get_notification_template_name -from creators.models import Creator, Badge -from projects.models import Project, Comment -from django.template.loader import render_to_string -from django.db.models import Sum +from notifications.utils import get_notification_template_name, push_notification +from projects.models import Comment, Project +from projects.tasks import delete_file_task try: from allauth.account.adapter import get_adapter - from allauth.account.utils import send_email_confirmation from allauth.account.models import EmailAddress + from allauth.account.utils import send_email_confirmation except ImportError: raise ImportError("allauth needs to be added to INSTALLED_APPS.") @@ -121,10 +127,7 @@ def setup_user_phone(user): phone = user_phone(user) phone_number = None if phone: - phone_number = PhoneNumber(user=user, - phone=phone, - primary=True, - verified=False) + phone_number = PhoneNumber(user=user, phone=phone, primary=True, verified=False) phone_number.save() if phone_number: @@ -148,11 +151,9 @@ def send_phone_confirmation(request, user, signup=False, phone=None): if not phone_number.verified: phone_number.send_confirmation(request, signup=signup) except PhoneNumber.DoesNotExist: - phone_number = PhoneNumber.objects.add_phone(request, - user, - phone, - signup=signup, - confirm=True) + phone_number = PhoneNumber.objects.add_phone( + request, user, phone, signup=signup, confirm=True + ) assert phone_number @@ -163,11 +164,10 @@ def send_group_invite_notification(creatorgroup, new_members): def process_avatar(oldInstance, newInstance): - """ Upload Creator avatar to media server and do cleanups where neccessary """ + """Upload Creator avatar to media server and do cleanups where neccessary""" if oldInstance and oldInstance.username != newInstance.username: - newInstance.avatar = 'https://robohash.org/{0}'.format( - newInstance.username) + newInstance.avatar = "https://robohash.org/{0}".format(newInstance.username) newInstance.save() if oldInstance.avatar.find("robohash.org") == -1: @@ -176,16 +176,14 @@ def process_avatar(oldInstance, newInstance): upload_file_task.delay(newInstance.id, newInstance.username) elif not oldInstance: - upload_file_task.delay(newInstance.id, newInstance.username) def process_group_avatar(oldInstance, newInstance): - """ Upload CreatorGroup avatar to media server and do cleanups where neccessary """ + """Upload CreatorGroup avatar to media server and do cleanups where neccessary""" if oldInstance and oldInstance.groupname != newInstance.groupname: - newInstance.avatar = 'https://robohash.org/{0}'.format( - newInstance.groupname) + newInstance.avatar = "https://robohash.org/{0}".format(newInstance.groupname) newInstance.save() if oldInstance.avatar.find("robohash.org") == -1: @@ -194,13 +192,11 @@ def process_group_avatar(oldInstance, newInstance): upload_file_task_group.delay(newInstance.id, newInstance.groupname) elif not oldInstance: - upload_file_task_group.delay(newInstance.id, newInstance.groupname) def activity_notification(activities, **kwargs): - - from projects.models import Project, Comment + from projects.models import Comment, Project today = timezone.now().replace(hour=0, minute=0, second=0) yesterday = today - timedelta(days=1) @@ -219,38 +215,41 @@ def activity_notification(activities, **kwargs): ctx[activity] = None new_creators = get_user_model().objects.filter( - date_joined__gte=yesterday, date_joined__lt=today) - new_projects = Project.objects.filter(created_on__gte=yesterday, - created_on__lt=today) - new_comments = Comment.objects.filter(created_on__gte=yesterday, - created_on__lt=today) + date_joined__gte=yesterday, date_joined__lt=today + ) + new_projects = Project.objects.filter( + created_on__gte=yesterday, created_on__lt=today + ) + new_comments = Comment.objects.filter( + created_on__gte=yesterday, created_on__lt=today + ) if new_creators: new_creators = list( - map(lambda creator: [str(creator.pk), creator.username], - new_creators)) + map(lambda creator: [str(creator.pk), creator.username], new_creators) + ) ctx["new_creators"] = new_creators if new_projects: new_projects = list( - map(lambda project: [str(project.pk), project.title], - new_projects)) + map(lambda project: [str(project.pk), project.title], new_projects) + ) ctx["new_projects"] = new_projects if new_comments: new_comments = list( map( - lambda comment: - [str(comment.pk), comment.creator.username], new_comments)) + lambda comment: [str(comment.pk), comment.creator.username], + new_comments, + ) + ) ctx["new_comments"] = new_comments for creator in staffs: if creator.email: - email_contexts.append({ - "user": creator.username, - "email": creator.email, - **ctx - }) + email_contexts.append( + {"user": creator.username, "email": creator.email, **ctx} + ) if creator.phone: phone_contexts.append({"phone": creator.phone, **ctx}) @@ -263,54 +262,67 @@ def activity_notification(activities, **kwargs): if len(phone_contexts) > 0 and ctx_values: send_mass_text.delay(template_name=template_name, ctxs=phone_contexts) + def perform_creator_search(user, query_string): from creators.models import CreatorTag + """ Perform search for creators matching query. - performs search across creators and creatortags, + performs search across creators and creatortags, and aggregate all creators that match the result. """ query = SearchQuery(query_string) - rank = SearchRank(F('search_vector'), query) + rank = SearchRank(F("search_vector"), query) result_creators = None - # fetch all creators whose creatortag(s) matches the search query - result_tags = CreatorTag.objects.filter( - search_vector=query).prefetch_related("creators") + result_tags = CreatorTag.objects.filter(search_vector=query).prefetch_related( + "creators" + ) for tag in result_tags: - result_creators = tag.creators.filter(is_active=True).annotate(rank=rank).order_by('-rank') + result_creators = ( + tag.creators.filter(is_active=True).annotate(rank=rank).order_by("-rank") + ) ############################################################ # fetch all creators that matches the search term if result_creators: - result_creators.union(get_user_model().objects.annotate(rank=rank).filter( - search_vector=query, is_active=True ).order_by('-rank')) + result_creators.union( + get_user_model() + .objects.annotate(rank=rank) + .filter(search_vector=query, is_active=True) + .order_by("-rank") + ) else: - result_creators = get_user_model().objects.annotate(rank=rank).filter( - search_vector=query,is_active=True ).order_by('-rank') + result_creators = ( + get_user_model() + .objects.annotate(rank=rank) + .filter(search_vector=query, is_active=True) + .order_by("-rank") + ) ############################################################## + if not user.is_authenticated: + result_creators = result_creators[:4] return result_creators - def custom_set_creatortags_queryset(creator, creatortags): """ Handle the process of assigning a queryset of creatortags to creator.tags - - Given a queryset of creatortags,'enforce_creator__creator_tags_constraints' needs + + Given a queryset of creatortags,'enforce_creator__creator_tags_constraints' needs to be called for each core creatortags (staff, moderator, group, creator) and - there needs to be some order to how it is called to ensure adherence to an order + there needs to be some order to how it is called to ensure adherence to an order of relative importance. - E.g. if you attempt giving a creator instance a queryset of 'staff' and 'creator', - 'staff' tag should be set and 'creator' tag should be dropped. This is because 'staff' - tag is expected to precede 'creator' tag. + E.g. if you attempt giving a creator instance a queryset of 'staff' and 'creator', + 'staff' tag should be set and 'creator' tag should be dropped. + This is because 'staff' tag is expected to precede 'creator' tag. - Note: do not change the order of the if statements, they are arranged in order of + Note: do not change the order of the if statements, they are arranged in order of relative importance to ensure certain tags takes precidence over others. """ with transaction.atomic(): @@ -343,56 +355,84 @@ def enforce_creator__creator_tags_constraints(creator, tag): """ Enforce certain constraints on what tags a creator instance should have together. - Should only be called when trying to set base tags like 'creator', 'staff', 'moderator' or 'group'. + Should only be called when trying to set base tags + like 'creator', 'staff', 'moderator' or 'group'. - Creator tags is a list, but certain tags shouldn't be in the same list at the same time. - e.g. "creator" tag is the default tag set when a user account is created, but should be removed - from a creator's tags list when other tags like "staff", "moderator" or "group" is added to the tags list. - This function contains the logic to decide which creator tags already in a creator's tags list should be set with - the incoming tag and which should be removed. + Creator tags is a list, but certain tags shouldn't be in the same + list at the same time. + e.g. "creator" tag is the default tag set when a user account is created, but should + be removed from a creator's tags list when other tags like "staff", "moderator" + or "group" is added to the tags list. + This function contains the logic to decide which creator tags already in a creator's + tags list should be set with the incoming tag and which should be removed. """ if tag.creators.filter(id=creator.id).exists(): - from creators.models import CreatorTag if tag.name == "creator": - """ If tag is 'creator', ensure that 'staff', 'moderator' and 'group' tags are not in the user's tags list """ - diff = CreatorTag.objects.filter(name__in=["creator", "staff", "group", "moderator"]) + """ + If tag is 'creator', ensure that 'staff', 'moderator' and + 'group' tags are not in the user's tags list + """ + diff = CreatorTag.objects.filter( + name__in=["creator", "staff", "group", "moderator"] + ) tags_to_set = creator.tags.difference(diff) return tags_to_set.union(diff.filter(name=tag.name)) - + elif tag.name == "staff": - """ If tag is 'staff', ensure that 'creator' and 'group' tags are not in the user's tags list """ + """ + If tag is 'staff', ensure that 'creator' and 'group' tags are not in + the user's tags list + """ diff = CreatorTag.objects.filter(name__in=["creator", "staff", "group"]) tags_to_set = creator.tags.difference(diff) return tags_to_set.union(diff.filter(name=tag.name)) elif tag.name == "moderator": - """ If tag is 'moderator', ensure that 'creator' and 'group' tags are not in the user's tags list """ + """ + If tag is 'moderator', ensure that 'creator' and 'group' tags are + not in the user's tags list + """ diff = CreatorTag.objects.filter(name__in=["creator", "group", "moderator"]) tags_to_set = creator.tags.difference(diff) return tags_to_set.union(diff.filter(name=tag.name)) elif tag.name == "group": - """ If tag is 'group', ensure that 'staff', 'creator' and 'moderator' tags are not in the user's tags list """ - diff = CreatorTag.objects.filter(name__in=["creator", "staff", "group", "moderator"]) + """If tag is 'group', ensure that 'staff', 'creator'and + 'moderator' tags are not in the user's tags list""" + diff = CreatorTag.objects.filter( + name__in=["creator", "staff", "group", "moderator"] + ) tags_to_set = creator.tags.difference(diff) return tags_to_set.union(diff.filter(name=tag.name)) else: - """ If no matching tag is found, raise an Exception to avoid mistakenly resetting creator.tags """ - raise Exception(""" - tag didn't match any of the conditions. - pass a tag that matches any of 'creator', 'staff', 'moderator', or 'group' - """) + """ + If no matching tag is found, raise an Exception to avoid + mistakenly resetting creator.tags + """ + raise Exception( + """ + tag didn't match any of the conditions. + pass a tag that matches any of 'creator', 'staff', 'moderator', or 'group' + """ + ) else: return creator.tags.all() -def activity_log(target: List[Creator], source: Creator, contexts, activity_type: Activitylog.Type, link: str): + +def activity_log( + target: List[Creator], + source: Creator, + contexts, + activity_type: Activitylog.Type, + link: str, +): for user, context in zip(target, contexts): - context.update({'source': source.username}) - context.update({'target': user.username}) + context.update({"source": source.username}) + context.update({"target": user.username}) push_activity(user, source, context, activity_type, link) @@ -400,64 +440,98 @@ def activity_log(target: List[Creator], source: Creator, contexts, activity_type enabled_notification_settings: Dict[Notification.Type, Set[int]] = { Notification.Type.BOOKMARK: {Setting.WEB}, Notification.Type.CLAP: {Setting.WEB}, - Notification.Type.COMMENT: - {Setting.WHATSAPP, Setting.EMAIL, Setting.SMS, Setting.WEB}, - Notification.Type.FOLLOW: - {Setting.WHATSAPP, Setting.EMAIL, Setting.SMS, Setting.WEB}, - Notification.Type.FOLLOWING_PROJECT: - {Setting.WHATSAPP, Setting.EMAIL, Setting.SMS, Setting.WEB}, + Notification.Type.COMMENT: { + Setting.WHATSAPP, + Setting.EMAIL, + Setting.SMS, + Setting.WEB, + }, + Notification.Type.FOLLOW: { + Setting.WHATSAPP, + Setting.EMAIL, + Setting.SMS, + Setting.WEB, + }, + Notification.Type.FOLLOWING_PROJECT: { + Setting.WHATSAPP, + Setting.EMAIL, + Setting.SMS, + Setting.WEB, + }, } -def is_valid_setting(setting: int, - notification_type: Notification.Type) -> bool: +def is_valid_setting(setting: int, notification_type: Notification.Type) -> bool: return setting in enabled_notification_settings[notification_type] -def send_notification(users: List[Creator], source: Creator, contexts, - notification_type: Notification.Type, link: str) -> None: +def send_notification( + users: List[Creator], + source: Creator, + contexts, + notification_type: Notification.Type, + link: str, +) -> None: email_contexts = [] sms_contexts = [] for user, context in zip(users, contexts): user_setting = Setting.objects.get(creator=user) - context.update({'source': source.username}) + context.update({"source": source.username}) - if user.email and user_setting.contact == Setting.EMAIL and is_valid_setting( - Setting.EMAIL, notification_type): + if ( + user.email + and user_setting.contact == Setting.EMAIL + and is_valid_setting(Setting.EMAIL, notification_type) + ): context.update({"email": user.email}) email_contexts.append(context) - if user.phone and user_setting.contact == Setting.SMS and is_valid_setting( - Setting.SMS, notification_type): + if ( + user.phone + and user_setting.contact == Setting.SMS + and is_valid_setting(Setting.SMS, notification_type) + ): context.update({"phone": user.phone}) sms_contexts.append(context) if is_valid_setting(Setting.WEB, notification_type): - successfully_pushed = push_notification(user, source, - notification_type, link, - context) + successfully_pushed = push_notification( + user, source, notification_type, link, context + ) if not successfully_pushed: return - if user.phone and user_setting.contact == Setting.WHATSAPP and is_valid_setting( - Setting.WHATSAPP, notification_type): + if ( + user.phone + and user_setting.contact == Setting.WHATSAPP + and is_valid_setting(Setting.WHATSAPP, notification_type) + ): context.update({"phone": user.phone}) - send_whatsapp.delay(phone=user.phone, - template_name=get_notification_template_name( - Setting.WHATSAPP, notification_type), - ctx=context) + send_whatsapp.delay( + phone=user.phone, + template_name=get_notification_template_name( + Setting.WHATSAPP, notification_type + ), + ctx=context, + ) if len(email_contexts) > 0: - send_mass_email.delay(template_name=get_notification_template_name( - Setting.EMAIL, notification_type), - ctxs=email_contexts, - full_template=True) + send_mass_email.delay( + template_name=get_notification_template_name( + Setting.EMAIL, notification_type + ), + ctxs=email_contexts, + full_template=True, + ) if len(sms_contexts) > 0: - send_mass_text.delay(template_name=get_notification_template_name( - Setting.SMS, notification_type), - ctxs=sms_contexts, - full_template=True) + send_mass_text.delay( + template_name=get_notification_template_name( + Setting.SMS, notification_type + ), + ctxs=sms_contexts, + full_template=True, + ) # def sync_user_email_addresses(user): @@ -488,51 +562,58 @@ def send_notification(users: List[Creator], source: Creator, contexts, def remove_initial_titles(creator, ids): for id in ids: - creator.badges.remove(Badge.objects.get(id= id)) + creator.badges.remove(Badge.objects.get(id=id)) + def add_titles(creator, count, ids): for id in ids: - value = Badge.objects.get(id= id).threshold_value + value = Badge.objects.get(id=id).threshold_value if count > value: - creator.badges.add(Badge.objects.get(id = id)) + creator.badges.add(Badge.objects.get(id=id)) break + def set_badge_like_category(creator): - likes_count = (Project.objects.filter(creator= creator) - .aggregate(Sum(('likes_count')))["likes_count__sum"]) - + likes_count = Project.objects.filter(creator=creator).aggregate( + Sum(("likes_count")) + )["likes_count__sum"] + badge_ids = [14, 13, 12] remove_initial_titles(creator, badge_ids) add_titles(creator, likes_count, badge_ids) - + + def set_badge_view_category(creator): - views_count = (Project.objects.filter(creator= creator).aggregate( - Sum(('views_count')))["views_count__sum"]) + views_count = Project.objects.filter(creator=creator).aggregate( + Sum(("views_count")) + )["views_count__sum"] badge_ids = [11, 10, 9, 8] remove_initial_titles(creator, badge_ids) add_titles(creator, views_count, badge_ids) + def set_badge_comment_category(creator): creator_id = creator.id - comments_count = Comment.objects.filter(creator__id= creator_id).count() + comments_count = Comment.objects.filter(creator__id=creator_id).count() badge_ids = [7, 6, 5] remove_initial_titles(creator, badge_ids) add_titles(creator, comments_count, badge_ids) + def set_badge_project_category(creator, projects_count): project_count_before_del = creator.projects_count - if(projects_count < project_count_before_del): - queryset = Project.objects.filter(creator= creator) - if( queryset.exists()): - set_badge_view_category(creator) - set_badge_like_category(creator) - set_badge_comment_category(creator) + if projects_count < project_count_before_del: + queryset = Project.objects.filter(creator=creator) + if queryset.exists(): + set_badge_view_category(creator) + set_badge_like_category(creator) + set_badge_comment_category(creator) badge_ids = [4, 3, 2, 1] diff --git a/zubhub_backend/zubhub/projects/utils.py b/zubhub_backend/zubhub/projects/utils.py index f34f54f5f..9a1de9f5d 100644 --- a/zubhub_backend/zubhub/projects/utils.py +++ b/zubhub_backend/zubhub/projects/utils.py @@ -478,6 +478,9 @@ def perform_project_search( if can_view(user, project): result.append(project) + if not user.is_authenticated: + result = result[:4] + return result diff --git a/zubhub_backend/zubhub/projects/views.py b/zubhub_backend/zubhub/projects/views.py index a20ebc89b..5d222f127 100644 --- a/zubhub_backend/zubhub/projects/views.py +++ b/zubhub_backend/zubhub/projects/views.py @@ -218,6 +218,8 @@ def get_queryset(self): .filter(search_vector=query) .order_by("-rank") ) + if not self.request.user.is_authenticated: + tags = tags[:4] return tags diff --git a/zubhub_frontend/zubhub/public/locales/en/translation.json b/zubhub_frontend/zubhub/public/locales/en/translation.json index 51afb1356..983199552 100644 --- a/zubhub_frontend/zubhub/public/locales/en/translation.json +++ b/zubhub_frontend/zubhub/public/locales/en/translation.json @@ -30,7 +30,12 @@ "createActivity": "CREATE ACTIVITY", "createActivityMenu": "Create Activity", "unpublishedActivities": "Unpublished Activities", - "myActivities": "My Activities" + "myActivities": "My Activities", + "searchType": { + "projects": "Projects", + "creators": "Creators", + "tags": "Tags" + } }, "sidebar":{ "logout": "Logout", @@ -258,7 +263,7 @@ }, "projects": { - "1":"Explore, ", + "1":"Explore,", "2": "Create, Share!", "shareProject": "Share your work", "exploreIdeas": "Explore Ideas", @@ -480,7 +485,16 @@ }, "errors": { "unexpected": "Uh oh! Seems like we hit a snag :( Maybe try again later?", - "noResult": "We could not find anything for your search term! Maybe try to search something else?" + "noResult": "We could not find anything for your search term! Maybe try to search something else?", + "noResult2": "Sorry, but we couldn't find any results for", + "noResultDescription": "Try searching for something else or browse our suggestions!" + }, + "loginCard": { + "title": { + "projects": "Log in or sign up to search for projects", + "creators": "Log in or sign up to search for users", + "tags": "Log in or sign up to search for projects with specific tags" + } } }, @@ -567,7 +581,7 @@ }, "deleteProjectToastSuccess": "Your project was deleted successfully!", "socialShare": { - "fbwa": "Check out this project on ZubHub! ", + "fbwa": "Check out this project on ZubHub!", "copySuccess": "Link successfully copied to clipboard!", "copyFail": "Hmmm... something went wrong trying to copy that link :(" }, @@ -649,7 +663,7 @@ }, "activityLog": { "activity": "Activity Log", - "addActivityLog": "Seems like there isn't any activity on your account yet! Get involved to see activity logs!" + "addActivityLog": "Seems like there isn't any activity on your account yet! Get involved to see activity logs!" }, "team": "Team", "createteam": "Create Team", @@ -1021,7 +1035,7 @@ }, "errors": { "max": "your creation step shouldn't be more than 1000 characters", - "required": "describe creation process in at least one step ", + "required": "describe creation process in at least one step", "emptyStep": "step can't be empty provide either a description or an image" } }, @@ -1191,4 +1205,4 @@ "buttonLabel": "Get Started" } } -} \ No newline at end of file +} diff --git a/zubhub_frontend/zubhub/public/locales/hi/translation.json b/zubhub_frontend/zubhub/public/locales/hi/translation.json index 03af477a0..9ba8ec907 100644 --- a/zubhub_frontend/zubhub/public/locales/hi/translation.json +++ b/zubhub_frontend/zubhub/public/locales/hi/translation.json @@ -30,7 +30,12 @@ "createActivity": "गतिविधि बनाएं", "createActivityMenu": "गतिविधि बनाएं", "unpublishedActivities": "अप्रकाशित गतिविधियाँ", - "myActivities": "मेरी गतिविधियां" + "myActivities": "मेरी गतिविधियां", + "searchType": { + "projects": "परियोजनाओं", + "creators": "रचनाकारों", + "tags": "टैग" + } }, "footer": { @@ -244,7 +249,7 @@ }, "projects": { - "1":"खोजें, ", + "1":"खोजें,", "2":"बनाएं, शेयर करें!", "shareProject": "एक परियोजना साझा करें", "exploreIdeas": "विचारों का अन्वेषण करें", @@ -427,7 +432,16 @@ }, "errors": { "unexpected": "यह क्रिया करते समय एक त्रुटि हुई। बाद में पुन: प्रयास करें", - "noResult": "आपके खोज शब्द के लिए कोई परिणाम नहीं मिला" + "noResult": "आपके खोज शब्द के लिए कोई परिणाम नहीं मिला", + "noResult2": "क्षम्यतां, परन्तु वयं तस्य किमपि परिणामं न प्राप्नुमः", + "noResultDescription": "अन्यत् किमपि अन्वेष्टुं प्रयतध्वम् अथवा अस्माकं सुझावः ब्राउज् कुर्वन्तु!" + }, + "loginCard": { + "title": { + "projects": "परियोजनानि अन्वेष्टुं प्रवेशं कुर्वन्तु अथवा पञ्जीकरणं कुर्वन्तु", + "creators": "उपयोक्तृणां अन्वेषणार्थं प्रवेशं कुर्वन्तु अथवा पञ्जीकरणं कुर्वन्तु", + "tags": "विशिष्टटैग्युक्तानि परियोजनानि अन्वेष्टुं प्रवेशं कुर्वन्तु अथवा पञ्जीकरणं कुर्वन्तु" + } } }, @@ -513,7 +527,7 @@ }, "deleteProjectToastSuccess": "आपका प्रोजेक्ट सफलतापूर्वक हटा दिया गया था !!", "socialShare": { - "fbwa": "ZubHub पर इस प्रोजेक्ट को देखें! ", + "fbwa": "ZubHub पर इस प्रोजेक्ट को देखें!", "copySuccess": "लिंक सफलतापूर्वक कॉपी किया गया!", "copyFail": "उस लिंक को कॉपी करने का प्रयास करते समय कुछ गलत हुआ... :(" }, @@ -559,7 +573,7 @@ "label": "यूज़रनेम", "errors": { "required": "ऐसा लगता है कि आप यह भूल गये" - } + } }, "password": { "label": "पासवर्ड", @@ -595,7 +609,7 @@ }, "activityLog": { "activity": "गतिविधि लॉग", - "addActivityLog": "ऐसा लगता है कि आपके खाते पर अभी तक कोई गतिविधि नहीं है! गतिविधि लॉग देखने के लिए शामिल हों!" + "addActivityLog": "ऐसा लगता है कि आपके खाते पर अभी तक कोई गतिविधि नहीं है! गतिविधि लॉग देखने के लिए शामिल हों!" }, "badge": { "badges": "बैज", @@ -825,7 +839,7 @@ } }, "actions": { - "submit": "परिवर्तनों को सुरक्षित करें", + "submit": "परिवर्तनों को सुरक्षित करें" }, "delete": { "label": "टीम हटाएं", diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/search_results/searchResultsStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/search_results/searchResultsStyles.js index 4de684371..cda8a2984 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/search_results/searchResultsStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/search_results/searchResultsStyles.js @@ -1,4 +1,4 @@ -const styles = theme => ({ +export const styles = theme => ({ root: { marginTop: '2em', marginBottom: '2em', @@ -21,11 +21,41 @@ const styles = theme => ({ maxWidth: '1190px', width: '100%', }, - + mainContainerLoggedOutStyle: { + background: 'white', + padding: '0 3em', + marginTop: '3em', + borderRadius: '15px', + [theme.breakpoints.down('1080')]: { + padding: '0 1em', + }, + [theme.breakpoints.down('500')]: { + padding: 0, + }, + }, pageHeaderStyle: { + margin: '1em 0', fontWeight: 'bold', textAlign: 'center', }, + creatorsContainerStyle: { + width: '100%', + display: 'flex', + gap: '1em', + }, + creatorsContainerLoggedOutStyle: { + justifyContent: 'center', + }, + projectsContainerStyle: { + margin: 0, + }, + loggedOutResultsContainer: { + width: '100%', + display: 'flex', + justifyContent: 'center', + maxHeight: '400px', + overflow: 'hidden', + }, cardStyle: { display: 'flex', flexDirection: 'column', @@ -39,6 +69,10 @@ const styles = theme => ({ textAlign: 'left', backgroundColor: '#ffffff', }, + cardLoggedOutStyle: { + marginBottom: '9em', + boxShadow: 'none', + }, avatarStyle: { width: '100%', height: '100%', @@ -65,6 +99,10 @@ const styles = theme => ({ maxWidth: '2000px', width: '100%', }, + buttonGroupLoggedOut: { + // background: 'white' + display: 'none', + }, floatRight: { float: 'right', }, @@ -93,6 +131,100 @@ const styles = theme => ({ fontWeight: '900', fontSize: '1.5rem', }, + transitionStyle: { + height: '20em', + marginTop: '-20em', + background: 'linear-gradient(0deg, rgba(255,255,255,80%) 34%, rgba(255,255,255,0) 100%)', + position: 'relative', + zIndex: 1060, + }, + loginCardStyle: { + background: 'white', + marginTop: '-2em', + }, + notFoundRobotStyle: { + width: '20em', + marginTop: '3em', + [theme.breakpoints.down('600')]: { + width: '15em', + }, + [theme.breakpoints.down('400')]: { + width: '12em', + }, + }, + noResultContainerStyle: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + noResultTitleStyle: { + width: '70%', + padding: '0 1em', + [theme.breakpoints.down('600')]: { + width: '100%', + fontSize: '1.5rem', + }, + }, + noResultDescStyle: { + padding: '0 1em', + fontSize: '1.2rem', + textAlign: 'center', + marginBottom: '3em', + [theme.breakpoints.down('600')]: { + fontSize: '1rem', + }, + }, + marginBottom: { + marginBottom: '4em', + zIndex: 10, + }, }); -export default styles; +export const loginStyleOverrides = theme => ({ + rootStyle: { + paddingTop: 0, + }, + containerStyle: { + [theme.breakpoints.down('400')]: { + padding: 0, + }, + }, + cardStyle: { + boxShadow: 'none', + [theme.breakpoints.down('500')]: { + padding: 0, + }, + }, + titleStyle: { + textAlign: 'center', + margin: 0, + }, + descriptionStyle: { + display: 'none', + }, + gridStyle: { + margin: 0, + }, +}); + +export const staffPickStyleOverrides = theme => ({ + rootStyle: { + margin: 0, + width: '80%', + [theme.breakpoints.down('400')]: { + width: '100%', + }, + }, + mainContainerStyle: { + margin: 0, + }, + messagePrimaryStyle: { + margin: 0, + fontSize: '1.8rem', + fontWeight: 700, + [theme.breakpoints.down('600')]: { + fontSize: '1.5rem', + }, + }, +}); diff --git a/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx b/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx index 9be26b6be..ec22d6c8d 100644 --- a/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx +++ b/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx @@ -1,15 +1,20 @@ import clsx from 'clsx'; import PropTypes from 'prop-types'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { Link, useNavigate } from 'react-router-dom'; +import _ from 'lodash'; +import React, { useEffect, useRef } from 'react'; +import { Link } from 'react-router-dom'; -import { connect, useSelector } from 'react-redux'; +import { connect } from 'react-redux'; import 'react-toastify/dist/ReactToastify.css'; import { makeStyles } from '@mui/styles'; -import SearchIcon from '@mui/icons-material/Search'; -import TranslateIcon from '@mui/icons-material/Translate'; +import { + Menu as MenuIcon, + Search as SearchIcon, + Translate as TranslateIcon, + SearchOutlined, +} from '@mui/icons-material'; import { AppBar, @@ -31,6 +36,8 @@ import { useScrollTrigger, } from '@mui/material'; +import { capitalize } from '../../assets/js/utils/scripts'; + import { closeSearchFormOrIgnore, fetchHero, @@ -45,21 +52,26 @@ import commonStyles from '../../assets/js/styles'; import styles from '../../assets/js/styles/views/page_wrapper/pageWrapperStyles'; import * as AuthActions from '../../store/actions/authActions'; import * as ProjectActions from '../../store/actions/projectActions'; +import * as CreatorActions from '../../store/actions/userActions'; -import { Menu as MenuIcon, SearchOutlined } from '@mui/icons-material'; -import API from '../../api'; import languageMap from '../../assets/js/languageMap.json'; -import Autocomplete from '../../components/autocomplete/Autocomplete'; -import Option from '../../components/autocomplete/Option'; -import InputSelect from '../../components/input_select/InputSelect'; -import NotificationButton from '../../components/notification_button/NotificationButton'; -import { throttle } from '../../utils.js'; +import Autocomplete from '../autocomplete/Autocomplete'; +import Option from '../autocomplete/Option'; +import InputSelect from '../input_select/InputSelect'; +import NotificationButton from '../notification_button/NotificationButton'; import AvatarButton from '../avatar_button/AvatarButton'; import Sidenav from '../Sidenav/Sidenav'; +import { + toggleDrawer, + toggleSearchBar, + autoComplete, + isSearch, + handleSubmit, + onSearchOptionClick, +} from './navbarScripts'; const useStyles = makeStyles(styles); const useCommonStyles = makeStyles(commonStyles); -const anchor = 'left'; /** * @function NavBar View @@ -68,81 +80,45 @@ const anchor = 'left'; * @todo - describe function's signature */ function NavBar(props) { - const backToTopEl = React.useRef(null); - const navigate = useNavigate(); const classes = useStyles(); const common_classes = useCommonStyles(); const trigger = useScrollTrigger(); - const [searchType, setSearchType] = useState(getQueryParams(window.location.href).get('type') || SearchType.PROJECTS); const formRef = useRef(); - const token = useSelector(state => state.auth.token); const [state, setState] = React.useState({ - username: null, - anchor_el: null, - loading: false, open_search_form: false, - left: false, + sidenav_open: false, + is_autocomplete_open: false, + autocomplete: [], + autocomplete_reset: null, + search_type: getQueryParams(window.location.href).get('type') || SearchType.PROJECTS, + query: isSearch(props.location.pathname) ? getQueryParams(window.location.href).get('q') : '', }); - const [options, setOptions] = useState([]); - const [query, setQuery] = useState(props.location.search ? getQueryParams(window.location.href).get('q') : ''); - - const toggleDrawer = event => { - if (event && event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) { - return; + const handleSetState = obj => { + if (obj) { + Promise.resolve(obj).then(obj => { + setState(state => ({ ...state, ...obj })); + }); } - setState(oldState => ({ ...oldState, left: !state.left })); }; - const throttledFetchOptions = useMemo( - () => - throttle(async (query, searchType) => { - if (query?.length === 0) { - setOptions([]); - return; - } - - const api = new API(); - let completions = []; - if (searchType === SearchType.TAGS) { - completions = await api.autocompleteTags({ query, token }); - completions = completions?.map(({ name }) => ({ - title: name, - })); - } else if (searchType === SearchType.PROJECTS) { - completions = await api.autocompleteProjects({ query, token }); - completions = completions - .filter(c=>( c.creator.id === props.auth.id && c.publish.type !== 1 )) - .map(({ id, title, creator, images }) => ({ - title, - shortInfo: creator.username, - image: images.length > 0 ? images[0].image_url : null, - link: `/projects/${id}`, - })); - } else { - completions = await api.autocompleteCreators({ query, token }); - completions = completions?.map(({ username, avatar }) => ({ - title: username, - image: avatar, - link: `/creators/${username}`, - })); - } - setOptions(completions); - }, 2), - [props.auth.id, token], - ); - useEffect(() => { - throttledFetchOptions( - query || (props.location.search && getQueryParams(window.location.href).get('q')), - searchType, + state.autocomplete_reset && state.autocomplete_reset(); + const debouncedAutoComplete = _.debounce( + () => + handleSetState( + autoComplete( + props, + state.query || (props.location.search && getQueryParams(window.location.href).get('q')), + state.search_type, + ), + ), + 1000, ); - }, [props.location.search, query, searchType, throttledFetchOptions]); - - useEffect(() => { - throttledFetchOptions.cancel(); - }, [throttledFetchOptions]); + debouncedAutoComplete(); + handleSetState({ autocomplete_reset: debouncedAutoComplete.cancel }); + }, [state.query, state.search_type, props.auth.token]); useEffect(() => { handleSetState({ loading: true }); @@ -161,62 +137,17 @@ function NavBar(props) { handleSetState(handleProfileMenuClose()); }, [trigger]); - const handleSetState = obj => { - if (obj) { - Promise.resolve(obj).then(obj => { - setState(state => ({ ...state, ...obj })); - }); - } - }; - - const toggleSearchBar = () => setState({ ...state, open_search_form: !state.open_search_form }); - - const onSearchOptionClick = async (_, option) => { - if (!option || !option.title) return; - - await new Promise(resolve => setTimeout(resolve, 100)); - if (option.link) { - window.history.pushState({}, '', option.link); - window.location.reload(); - return; - } - - const queryParams = new URLSearchParams({ - type: searchType, - q: option.title, - }); - window.history.pushState({}, '', `/search?${queryParams}`); - window.location.reload(); - }; - - const handleSubmit = e => { - e.preventDefault(); - if (query.length == 0) return; - const queryParams = new URLSearchParams({ - type: searchType, - q: query, - }); - - window.history.pushState({}, '', `/search?${queryParams}`); - window.location.reload(); - }; - - const handleTextField = event => { - setQuery(event.target.value); - }; - - const { anchor_el, loading, open_search_form } = state; + const { open_search_form, sidenav_open, search_type, is_autocomplete_open, query, autocomplete } = state; const { t } = props; - const { zubhub, hero } = props.projects; + const { zubhub } = props.projects; - const profileMenuOpen = Boolean(anchor_el); return ( <> - + handleSetState(toggleDrawer(e, sidenav_open))}> @@ -272,7 +203,7 @@ function NavBar(props) { action="/search" className={clsx(classes.searchFormStyle, classes.removeOn894)} role="search" - onSubmit={handleSubmit} + onSubmit={e => handleSubmit(e, props, query, search_type)} ref={formRef} > - - Projects - Creators - Tags + handleSetState({ search_type: value })} + name="type" + > + {t('pageWrapper.navbar.searchType.projects')} + {t('pageWrapper.navbar.searchType.creators')} + {t('pageWrapper.navbar.searchType.tags')} props.auth.token && handleSetState({ is_autocomplete_open: true })} + onClose={() => handleSetState({ is_autocomplete_open: false })} className={classes.input} - options={options} + options={autocomplete} defaultValue={{ title: query }} value={{ title: query }} - renderOption={(props, option, { inputValue }) => ( -