Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Following section #6301

Merged
merged 18 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/assets/v2/css/buttons.css
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ button:focus {
color: white;
}

.btn-gc-outline-red {
border-color: #dc3545;
color: #dc3545;
background: none;
}

.btn-gc-outline-red:hover {
border-color: #dc3545;
color: #dc3545;
background: none;
}

.btn-outline-gc-blue {
border-color: var(--gc-blue);
color: var(--gc-blue);
Expand Down
56 changes: 56 additions & 0 deletions app/assets/v2/js/pages/join_tribe.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,59 @@ const tribeLeader = () => {
};

tribeLeader();

const newManageTribe = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file is loaded in many pleases probably is better to move it to his own file or even loaded on that page only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we omit this at this moment? i'll move this in another PR due i'll be working this weeks on tribes functionality.

$('[data-tribe]').each(function(index, elem) {
$(elem).mouseenter(function(e) {
if ($(elem).hasClass('btn-outline-gc-green')) {
$(elem).addClass('btn-gc-outline-red').text('Unfollow');
$(elem).removeClass('btn-outline-gc-green');
}
}).mouseleave(function(e) {
if ($(elem).hasClass('btn-gc-outline-red')) {
$(elem).removeClass('btn-gc-outline-red');
$(elem).addClass('btn-outline-gc-green').text('Following');
}
});

$(elem).focus(function(e) {
if ($(elem).hasClass('btn-outline-gc-green')) {
$(elem).addClass('btn-gc-outline-red').text('Unfollow');
$(elem).removeClass('btn-outline-gc-green');
}
}).focusout(function(e) {
if ($(elem).hasClass('btn-gc-outline-red')) {
$(elem).removeClass('btn-gc-outline-red');
$(elem).addClass('btn-outline-gc-green').text('Following');
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should use multiple event to be more DRY .on( "mouseenter focus", fnc ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


$(elem).on('click', function(e) {
if (!document.contxt.github_handle) {
e.preventDefault();
_alert('Please login first.', 'error');
return;
}

$(elem).attr('disabled', true);
e.preventDefault();
const tribe = $(elem).data('tribe');
const url = `/tribe/${tribe}/join/`;
const sendJoin = fetchData (url, 'POST', {}, {'X-CSRFToken': $("input[name='csrfmiddlewaretoken']").val()});

$.when(sendJoin).then(function(response) {
$(elem).attr('disabled', false);
$(elem).attr('member', response.is_member);
if (response.is_member) {
$(elem).addClass('btn-outline-gc-green').removeClass([ 'btn-gc-blue', 'btn-gc-outline-red' ]).text('Following');
} else {
$(elem).removeClass([ 'btn-outline-gc-green', 'btn-gc-outline-red' ]).addClass('btn-gc-blue').text('Follow');
}
}).fail(function(error) {
$(elem).attr('disabled', false);
});
});
});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you rebase this then you will see this file now have const followRequest = (handle, elem, cb, cbError) function is just the request with a callback you can use it in a way you will have the handle, elem, response and in that way you don't need to copy paste the request part.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done :)


newManageTribe();
15 changes: 15 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4896,3 +4896,18 @@ class TribeMember(SuperModel):
max_length=20,
blank=True
)

@property
def mutual_follow(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tribesmembers is used for users following but also for orgs tribes seems this will show tribes orgs as following

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a handy method for Tribe Members, this method will be used as predicate to know if the current pair (org, profile) follow mutually.

return TribeMember.objects.filter(profile=self.org, org=self.profile).exists()

@property
def mutual_follower(self):
tribe_following = TribeMember.objects.filter(profile=self.profile).values('org')
return TribeMember.objects.filter(org=self.org, profile__in=tribe_following).exclude(profile=self.profile)

@property
def mutual_following(self):
tribe_following = TribeMember.objects.filter(org=self.profile).values('profile')
print(tribe_following)
return TribeMember.objects.filter(org__in=tribe_following, profile=self.org)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this logic, if you already have those who already following you why not better cross both and have the result to avoid many queries?
Also I see a missing print there

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the missing print? Is there a rule for printing objects here we have to follow?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, only one query is executed on each of these methods using a subquery

145 changes: 145 additions & 0 deletions app/dashboard/templates/profiles/tab_follow.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{% load i18n static %}

{% if not hidden %}
<div id="profile-tabs" class="tab-container font-body mt-3 mb-4">
<button type="button" id="nav-description" href="{{profile.url}}/follow?sub=followers" class="text-center section-tab {% if foltab == "followers" %} active {% endif %}">
{% trans "FOLLOWERS" %} <span class="nav-badge">({{followers.count}})</span>
</button>
<button type="button" id="nav-description" href="{{profile.url}}/follow?sub=following" class="text-center section-tab {% if foltab == "following" %} active {% endif %}">
{% trans "FOLLOWING" %} <span class="nav-badge">({{following.count}})</span>
</button>
</div>
{% endif %}

<div class="tab-projects d-flex flex-column">
{% if foltab == 'followers' %}
<!-- Show This users followers-->
{% for user in followers %}
<div class="tab-projects__item d-flex mb-0 pt-1 pb-1 bounty_row">
<div class="avatar-container col-1 justify-content-center hide_min_viewport">
<img class="profile-header__avatar mr-3" src="/dynamic/avatar/{{ user.profile.handle }}" alt="">
</div>
<div class="tab-projects__item-details col-3 d-flex flex-column bounty-detail">
<div class="d-flex flex-column">
<div class="font-weight-semibold font-header">{{user.profile.name}}</div>
<div class="text-highlight-gc-blue font-smaller-1">{{user.profile.handle}}</div>
</div>
</div>
<div class="mb-0 mt-1 col-5 d-flex flex-row align-items-center">
<div class="mr-5" style="height: fit-content">
<button class="btn btn-outline-gc-blue btn-sm flex-grow-1 font-smaller-5 position-relative quick-link" data-openchat="{{user.profile}}">
<i class="fas fa-comment-dots" data-placement="bottom" data-toggle="tooltip" data-html="true"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets move the tooltip to the button to show it on all the button hover not just the icon

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

title="Chat @{{ user.profile.handle }}">
</i>
</button>
</div>
<div style="height: fit-content">
<a class="btn btn-outline-gc-blue btn-sm flex-grow-1 mr-5 font-smaller-5 position-relative quick-link btn-outline-gc-blue" href="https://gitcoin.co/users?invite={{user.profile.handle}}" data-placement="bottom" data-toggle="tooltip" data-html="true" title="Invite {{ user.profile.handle }} to Bounty">
<i class="fas fa-envelope-open-text"></i>
</a>
</div>
<div class="d-flex align-items-center" style="height: fit-content">
{% with followers=user.mutual_following %}
{% if followers|length == 1 %}
<div style="width: 60px">
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ followers.0.org.handle }}" alt="">
</div>
<p class="text-muted font-smaller-4 my-0">Followed by {{ followers.0.org.handle }}</p>
{% endif %}
{% if followers|length == 2 %}
<div style="width: 60px" class="d-flex flex-row align-items-center">
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px " src="/dynamic/avatar/{{ followers.0.org.handle }}" alt="">
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ followers.1.org.handle }}" alt="">
</div>
<p class="text-muted font-smaller-4 my-0">Followed by {{ followers.0.org.handle }} and {{ followers.1.org.handle }}</p>
{% endif %}
{% if followers|length >= 3 %}
<div style="width: 60px" class="d-flex flex-row align-items-center">
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px" src="/dynamic/avatar/{{ followers.0.org.handle }}" alt="">
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px" src="/dynamic/avatar/{{ followers.1.org.handle }}" alt="">
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ followers.2.org.handle }}" alt="">
</div>
<p class="text-muted font-smaller-4 my-0">Followed by {{ followers.0.org.handle }}, {{ followers.1.org.handle }} and {{ followers|length|add:-2}} others you follow</p>
{% endif %}
{% endwith %}
</div>
</div>
<div class="col-12 col-md-4 col-lg-3 font-caption my-auto text-right">
{% if user.mutual_follow %}
<button class="btn btn-sm m-2 px-4 font-caption py-1 font-weight-bold btn-outline-gc-green" style="border-width: 2px !important;" data-tribe="{{user.profile.handle}}">Following</button>
{% else %}
<button class="btn btn-gc-blue btn-sm m-2 px-2 font-caption" data-jointribe="{{user.profile.handle}}">Follow<i class="fas fa-user-plus font-smaller-4 ml-2"></i></button>
{% endif %}
</div>
</div>
{% endfor %}

{% elif foltab == 'following' %}
<!-- Show which users this one is following-->
{% for user in following %}
<div class="tab-projects__item d-flex mb-0 pt-1 pb-1 bounty_row">
<div class="avatar-container col-1 justify-content-center hide_min_viewport">
<img class="profile-header__avatar mr-3" src="/dynamic/avatar/{{ user.org.handle }}" alt="">
</div>
<div class="tab-projects__item-details col-3 d-flex flex-column bounty-detail">
<div class="d-flex flex-column">
<div class="font-weight-semibold font-header">{{user.org.name}}</div>
<div class="text-highlight-gc-blue font-smaller-1">{{user.org.handle}}</div>
</div>
</div>
<div class="mb-0 mt-1 col-5 d-flex flex-row align-items-center">
<div class="mr-5" style="height: fit-content">
<button class="btn btn-outline-gc-blue btn-sm flex-grow-1 font-smaller-5 position-relative quick-link" data-openchat="{{user.org}}">
<i class="fas fa-comment-dots" data-p lacement="bottom" data-toggle="tooltip" data-html="true"
title="Chat @{{ user.org.handle }}">
</i>
</button>
</div>
<div style="height: fit-content">
<a class="btn btn-sm flex-grow-1 mr-5 font-smaller-5 position-relative quick-link btn-outline-gc-blue" {% if not user.org.email %} hidden {% endif %} href="mailto:{{user.org.email}}" data-placement="bottom" data-toggle="tooltip" data-html="true" title="@{{ user.org.handle }}'s Email">
<i class="far fa-envelope"></i>
</a>
</div>
<div class="d-flex align-items-center" style="height: fit-content">
{% with followers=user.mutual_follower %}
{% if followers|length == 1 %}
<div style="width: 60px">
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ followers.0.profile.handle }}" alt="">
</div>
<p class="text-muted font-smaller-4 my-0">Followed by {{ followers.0.profile.handle }}</p>
{% endif %}
{% if followers|length == 2 %}
<div style="width: 60px" class="d-flex flex-row align-items-center">
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px " src="/dynamic/avatar/{{ followers.0.profile.handle }}" alt="">
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ followers.1.profile.handle }}" alt="">
</div>
<p class="text-muted font-smaller-4 my-0">Followed by {{ followers.0.profile.handle }} and {{ followers.1.profile.handle }}</p>
{% endif %}
{% if followers|length >= 3 %}
<div style="width: 60px" class="d-flex flex-row align-items-center">
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px" src="/dynamic/avatar/{{ followers.0.profile.handle }}" alt="">
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px" src="/dynamic/avatar/{{ followers.1.profile.handle }}" alt="">
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ followers.2.profile.handle }}" alt="">
</div>
<p class="text-muted font-smaller-4 my-0">Followed by {{ followers.0.profile.handle }}, {{ followers.1.profile.handle }} and {{ followers|length|add:-2}} others you follow</p>
{% endif %}
{% endwith %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets move this logic to a loop instead and limit the looping to be more DRY

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this would be better as a loop

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</div>

</div>
<div class="col-12 col-md-3 col-lg-3 font-caption my-auto text-right">
<button class="btn btn-sm m-4 px-4 font-caption py-1 font-weight-bold btn-outline-gc-green" style="border-width: 2px !important;" data-tribe="{{user.org.handle}}">Following</button>
</div>
</div>
{% endfor %}
</div>


<script>
setInterval(function(){
if($ && $('.load-more').length && $(".load-more").isInViewport()){
$('.load-more').click();
}
}, 1000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how this work? doesn't seems to be any ajax request handling this and if checking each second to click the button? Or I am missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

borrowed from a different tab, I think tab_kudos.html. Possibly didn't connect it, will check that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</script>
{% endif %}
2 changes: 1 addition & 1 deletion app/dashboard/templates/profiles/tab_viewers.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ <h3 class="font-body mt-1 mb-2 font-weight-semibold">Monthly Summary</h3>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% endif %}
5 changes: 5 additions & 0 deletions app/dashboard/templates/profiles/tabs.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
{% trans "TRIBE" %}{%if not profile.is_org %}S <span class="nav-badge">({{profile.tribe_members.count}})</span>{% endif %}
</button>
{% endif %}
<button type="button" id="nav-description" href="{{profile.url}}/follow" class="text-center section-tab {% if tab == "follow" %} active {% endif %}">
{% trans "FOLLOW" %}
</button>
{% if show_resume_tab %}
<button type="button" id="nav-description" href="{{profile.url}}/resume" class="text-center section-tab {% if tab == "resume" %} active {% endif %}">
{% if not profile.show_job_status and is_my_profile %}
Expand Down Expand Up @@ -106,6 +109,8 @@
<div class="col-12 px-lg-0">
{% if tab == 'kudos' %}
{% include 'profiles/tab_kudos.html' %}
{% elif tab == 'follow' %}
{% include 'profiles/tab_follow.html' %}
{% elif tab == 'resume' %}
{% include 'profiles/tab_resume.html' %}
{% elif tab == 'active' %}
Expand Down
17 changes: 16 additions & 1 deletion app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2663,6 +2663,15 @@ def get_profile_tab(request, profile, tab, prev_context):
pass
elif tab == 'tribe':
context['team'] = profile.team_or_none_if_timeout
elif tab == 'follow':
context['org_kudos'] = profile.get_org_kudos
owned_kudos = profile.get_my_kudos.order_by('id', order_by)
sent_kudos = profile.get_sent_kudos.order_by('id', order_by)
kudos_limit = 8
context['kudos'] = owned_kudos[0:kudos_limit]
context['sent_kudos'] = sent_kudos[0:kudos_limit]
context['kudos_count'] = owned_kudos.count()
context['sent_kudos_count'] = sent_kudos.count()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this context here instead on the general context? shouldn't be here the follow data you need only for that tab?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

elif tab == 'people':
if profile.is_org:
context['team'] = profile.team_or_none_if_timeout
Expand Down Expand Up @@ -2783,7 +2792,7 @@ def profile(request, handle, tab=None):
disable_cache = False

# make sure tab param is correct
all_tabs = ['active', 'ratings', 'portfolio', 'viewers', 'activity', 'resume', 'kudos', 'earnings', 'spent', 'orgs', 'people', 'grants', 'quests', 'tribe', 'hackathons']
all_tabs = ['active', 'ratings', 'follow', 'portfolio', 'viewers', 'activity', 'resume', 'kudos', 'earnings', 'spent', 'orgs', 'people', 'grants', 'quests', 'tribe', 'hackathons']
tab = default_tab if tab not in all_tabs else tab
if handle in all_tabs and request.user.is_authenticated:
# someone trying to go to their own profile?
Expand Down Expand Up @@ -2817,6 +2826,7 @@ def profile(request, handle, tab=None):
context = {
'hidden': True,
'ratings': range(0,5),
'followers': TribeMember.objects.filter(org=request.user.profile),
'profile': {
'handle': handle,
'avatar_url': f"/dynamic/avatar/Self",
Expand Down Expand Up @@ -2852,6 +2862,7 @@ def profile(request, handle, tab=None):

context['is_my_profile'] = is_my_profile
context['show_resume_tab'] = profile.show_job_status or context['is_my_profile']
context['show_follow_tab'] = True
context['is_editable'] = context['is_my_profile'] # or context['is_my_org']
context['tab'] = tab
context['show_activity'] = request.GET.get('p', False) != False
Expand All @@ -2864,7 +2875,11 @@ def profile(request, handle, tab=None):
context['feedbacks_got'] = [fb.pk for fb in profile.feedbacks_got.all() if fb.visible_to(request.user)]
context['all_feedbacks'] = context['feedbacks_got'] + context['feedbacks_sent']
context['tags'] = [('#announce','bullhorn'), ('#mentor','terminal'), ('#jobs','code'), ('#help','laptop-code'), ('#other','briefcase'), ]
context['followers'] = TribeMember.objects.filter(org=request.user.profile)
context['following'] = TribeMember.objects.filter(profile=request.user.profile)
context['foltab'] = request.GET.get('sub', 'followers')

print(f'===== TRIBES COUNT: {TribeMember.objects.filter(org=request.user.profile)}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing print

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

tab = get_profile_tab(request, profile, tab, context)
if type(tab) == dict:
context.update(tab)
Expand Down