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 all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
44 changes: 44 additions & 0 deletions app/assets/v2/js/pages/join_tribe.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,47 @@ 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).on('mouseenter focus', function(e) {
if ($(elem).hasClass('btn-outline-gc-green')) {
$(elem).addClass('btn-gc-outline-red').text('Unfollow');
$(elem).removeClass('btn-outline-gc-green');
}
});

$(elem).on('mouseleave focusout', function(e) {
if ($(elem).hasClass('btn-gc-outline-red')) {
$(elem).removeClass('btn-gc-outline-red');
$(elem).addClass('btn-outline-gc-green').text('Following');
}
});

$(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');

followRequest(tribe, elem, function(handle, elem, 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');
}
}, function(error) {
$(elem).attr('disabled', false);
});
});
});
};

newManageTribe();
16 changes: 15 additions & 1 deletion app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from django.contrib.postgres.fields import ArrayField, JSONField
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import connection, models
from django.db.models import Count, F, Q, Sum
from django.db.models import Count, F, Q, Sum, Subquery
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_save
from django.dispatch import receiver
from django.forms.models import model_to_dict
Expand Down Expand Up @@ -4852,3 +4852,17 @@ 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 = Subquery(TribeMember.objects.filter(profile=self.profile).values_list('org', flat=True))
return TribeMember.objects.filter(org=self.org, profile__in=tribe_following).exclude(profile=self.profile)

@property
def mutual_following(self):
tribe_following = Subquery(TribeMember.objects.filter(org=self.profile).values_list('profile', flat=True))
return TribeMember.objects.filter(org__in=tribe_following, profile=self.org).exclude(org=self.org)
121 changes: 121 additions & 0 deletions app/dashboard/templates/profiles/tab_follow.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{% 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}}"
data-placement="bottom" data-toggle="tooltip" data-html="true" title="Chat @{{ user.profile.handle }}">
<i class="fas fa-comment-dots"></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 %}
<div style="width: 60px" class="{% if followers|length > 1 %}d-flex flex-row align-items-center{% endif %}">
{% for follower in followers %}
{% if forloop.last %}
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ follower.org.handle }}" alt="">
{% else %}
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px " src="/dynamic/avatar/{{ follower.org.handle }}" alt="">
{% endif %}
{% endfor %}
</div>
{% if followers|length >= 1 %}
<p class="text-muted font-smaller-4 my-0">Followed by {% for follower in followers %}
{{ follower.org.handle }}{% if not forloop.last %}, {% endif %}
{% endfor %}</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 %}
<div style="width: 60px" class="{% if followers|length > 1 %} d-flex flex-row align-items-center{% endif %}">
{% for follower in followers %}
{% if forloop.last %}
<img class="rounded-circle" style="height: 25px; width: 25px;" src="/dynamic/avatar/{{ follower.profile.handle }}" alt="">
{% else %}
<img class="rounded-circle" style="height: 25px; width: 25px; margin-right: -12px " src="/dynamic/avatar/{{ follower.profile.handle }}" alt="">
{% endif %}
{% endfor %}
</div>
{% if followers|length >= 1 %}
<p class="text-muted font-smaller-4 my-0">Followed by {% for follower in followers %}
{{ follower.profile.handle }}{% if not forloop.last %}, {% endif %}
{% endfor %}</p>
{% endif %}
{% endwith %}
</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>
{% 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
9 changes: 8 additions & 1 deletion app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,8 @@ def get_profile_tab(request, profile, tab, prev_context):
pass
elif tab == 'tribe':
context['team'] = profile.team_or_none_if_timeout
elif tab == 'follow':
pass
elif tab == 'people':
if profile.is_org:
context['team'] = profile.team_or_none_if_timeout
Expand Down Expand Up @@ -2732,7 +2734,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 @@ -2766,6 +2768,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 @@ -2801,6 +2804,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 @@ -2813,6 +2817,9 @@ 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')

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