Skip to content

Commit

Permalink
Merge pull request #3906 from gitcoinco/share-w-kudos
Browse files Browse the repository at this point in the history
add kudos section to modal
  • Loading branch information
SaptakS authored Mar 14, 2019
2 parents 38d65b6 + 362f1c6 commit efdae2b
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 32 deletions.
1 change: 1 addition & 0 deletions app/assets/v2/images/special-kudos.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion app/assets/v2/js/pages/bounty_share.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ $('.modal-link').click(function(e) {
const sendInvites = (users) => {
let usersId = [];
let msg = $('#shareText').val();
const url = document.issueURL;

$.each(users, function(index, elem) {
usersId.push(elem.id);
});

var sendEmail = fetchData('/api/v0.1/social_contribution_email/', 'POST', {usersId, msg}, {'X-CSRFToken': csrftoken});
var sendEmail = fetchData(
'/api/v0.1/social_contribution_email/',
'POST',
{usersId, msg, url},
{'X-CSRFToken': csrftoken}
);

$.when(sendEmail).then(
function(payback) {
Expand Down
12 changes: 9 additions & 3 deletions app/dashboard/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
from django.utils.safestring import mark_safe

from .models import (
Activity, BlockedUser, Bounty, BountyFulfillment, BountySyncRequest, CoinRedemption, CoinRedemptionRequest,
FeedbackEntry, Interest, LabsResearch, Profile, SearchHistory, Tip, TokenApproval, Tool, ToolVote, UserAction,
UserVerificationModel,
Activity, BlockedUser, Bounty, BountyFulfillment, BountyInvites, BountySyncRequest, CoinRedemption,
CoinRedemptionRequest, FeedbackEntry, Interest, LabsResearch, Profile, SearchHistory, Tip, TokenApproval, Tool,
ToolVote, UserAction, UserVerificationModel,
)


Expand Down Expand Up @@ -64,6 +64,11 @@ class ToolVoteAdmin(admin.ModelAdmin):
ordering = ['-id']


class BountyInvitesAdmin(admin.ModelAdmin):
raw_id_fields = ['bounty']
ordering = ['-id']


class InterestAdmin(admin.ModelAdmin):
raw_id_fields = ['profile']
ordering = ['-id']
Expand Down Expand Up @@ -178,6 +183,7 @@ def network_link(self, instance):
admin.site.register(Bounty, BountyAdmin)
admin.site.register(BountyFulfillment, BountyFulfillmentAdmin)
admin.site.register(BountySyncRequest, GeneralAdmin)
admin.site.register(BountyInvites, BountyInvitesAdmin)
admin.site.register(Tip, TipAdmin)
admin.site.register(TokenApproval, TokenApprovalAdmin)
admin.site.register(CoinRedemption, GeneralAdmin)
Expand Down
31 changes: 31 additions & 0 deletions app/dashboard/migrations/0019_bountyinvites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 2.1.7 on 2019-03-11 21:34

from django.conf import settings
from django.db import migrations, models
import economy.models


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('dashboard', '0018_merge_20190307_1314'),
]

operations = [
migrations.CreateModel(
name='BountyInvites',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_on', models.DateTimeField(db_index=True, default=economy.models.get_time)),
('modified_on', models.DateTimeField(default=economy.models.get_time)),
('status', models.CharField(blank=True, choices=[('pending', 'pending'), ('accepted', 'accepted')], max_length=20)),
('bounty', models.ManyToManyField(blank=True, related_name='bounty', to='dashboard.Bounty')),
('invitee', models.ManyToManyField(blank=True, related_name='invitee', to=settings.AUTH_USER_MODEL)),
('inviter', models.ManyToManyField(blank=True, related_name='inviter', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
]
18 changes: 18 additions & 0 deletions app/dashboard/migrations/0020_auto_20190312_0739.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-03-12 07:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0019_bountyinvites'),
]

operations = [
migrations.AlterField(
model_name='bountyinvites',
name='status',
field=models.CharField(blank=True, choices=[('pending', 'pending'), ('accepted', 'accepted'), ('completed', 'completed')], max_length=20),
),
]
29 changes: 29 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,35 @@ def __str__(self):
return f"User: {self.user}; Verified: {self.verified}"


class BountyInvites(SuperModel):
"""Define the structure of bounty invites."""

INVITE_STATUS = [
('pending', 'pending'),
('accepted', 'accepted'),
('completed', 'completed'),
]

bounty = models.ManyToManyField('dashboard.Bounty', related_name='bounty', blank=True)
inviter = models.ManyToManyField(User, related_name='inviter', blank=True)
invitee = models.ManyToManyField(User, related_name='invitee', blank=True)
status = models.CharField(max_length=20, choices=INVITE_STATUS, blank=True)

def __str__(self):
return f"Inviter: {self.inviter}; Invitee: {self.invitee}; Bounty: {self.bounty}"

@property
def get_bounty_invite_url(self):
"""Returns a unique url for each bounty and one who is inviting
Returns:
A unique string for each bounty
"""
salt = "X96gRAVvwx52uS6w4QYCUHRfR3OaoB"
string = self.inviter.username + salt + self.bounty
return base64.urlsafe_b64encode(string.encode()).decode()


class ProfileQuerySet(models.QuerySet):
"""Define the Profile QuerySet to be used as the objects manager."""

Expand Down
20 changes: 17 additions & 3 deletions app/dashboard/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from rest_framework import routers, serializers, viewsets
from retail.helpers import get_ip

from .models import Activity, Bounty, BountyFulfillment, Interest, ProfileSerializer, SearchHistory
from .models import Activity, Bounty, BountyFulfillment, BountyInvites, Interest, ProfileSerializer, SearchHistory


class BountyFulfillmentSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -108,15 +108,29 @@ def create(self, validated_data):
fulfillments_data = validated_data.pop('fulfillments')
bounty = Bounty.objects.create(**validated_data)
for fulfillment_data in fulfillments_data:
BountyFulfillment.objects.create(bounty=bounty, **fulfillment_data)
bounty_fulfillment = BountyFulfillment.objects.create(bounty=bounty, **fulfillment_data)
bounty_invitee = BountyInvites.objects.filter(
bounty=bounty,
invitee=bounty_fulfillment.profile.user
).first()
if bounty_invite:
bounty_invitee.status = 'completed'
bounty_invitee.save()
return bounty

def update(self, validated_data):
"""Handle updating of m2m relationships and other custom operations."""
fulfillments_data = validated_data.pop('fulfillments')
bounty = Bounty.objects.update(**validated_data)
for fulfillment_data in fulfillments_data:
BountyFulfillment.objects.update(bounty=bounty, **fulfillment_data)
bounty_fulfillment = BountyFulfillment.objects.create(bounty=bounty, **fulfillment_data)
bounty_invitee = BountyInvites.objects.filter(
bounty=bounty,
invitee=bounty_fulfillment.profile.user
).first()
if bounty_invite:
bounty_invitee.status = 'completed'
bounty_invitee.save()
return bounty


Expand Down
19 changes: 14 additions & 5 deletions app/dashboard/templates/social_contribution_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,26 @@
<div class="col col-md-10 center-block">
<div class="d-flex justify-content-center flex-column mt-5 w-100" id="primary_subform">
<div class="center-block w-75 text-center">
<h1>{% trans "Share this Bounty" %}</h1>
<h2 class="mb-5">{% trans "Share this Bounty" %}</h2>
<img src="{% static 'v2/images/social-sharing.svg' %}" width="240">
<h4>{% trans "Help friends and colleagues find new and interesting work!" %}</h4>
</div>
<div class="bg-light media p-3 mt-5">
<img src="{% static 'v2/images/special-kudos.svg' %}" width="150" class="mr-3">
<div class="media-body">
<h5 class="mt-0 font-weight-semibold font-body"><span class="text-highlight-green">Limited Time Only!</span> <br> Invite friend and get a special Kudos</h5>
<p class="font-body">
Get a special, limited-edition Kudos when your friend finishes this bounty.
</p>
</div>
</div>
<div class="center-block mt-5 text-right w-75">
<textarea class="w-100 form__input" id="shareText">Check out this bounty that pays out {{bounty.get_value_true}} {{bounty.token_name}} {{bounty.url}} {% for keyword in bounty.keywords_list %} #{{keyword}}{% endfor %}</textarea>
<p class="font-body text-left">{% trans "Help friends and colleagues find new and interesting work!" %}</p>
<textarea class="w-100 form__input" id="shareText">Check out this bounty that pays out {{bounty.get_value_true}} {{bounty.token_name}} {{invite_url}} {% for keyword in bounty.keywords_list %} #{{keyword}}{% endfor %}</textarea>
<button class="button button__small button--primary" data-copyclipboard="#shareText">Copy Text</button>
</div>

<div class="center-block w-75 my-5 {% if not user.is_authenticated %}d-none{%endif%}">
<p class="h5">...or invite a Gitcoiner by username</p>
<p class="font-body text-center">...or invite a Gitcoiner by username</p>
<div class="form__select2 g-multiselect">
<select id="" class="form__input form__input-lg users_share" name="users_share[]" multiple="multiple">
</select>
Expand All @@ -58,7 +67,7 @@ <h4>{% trans "Click to instantly share on the following networks" %}</h4>
</a>
<a class="btn btn-circle btn-facebook"
target="_blank"
href="https://www.facebook.com/sharer/sharer.php?u={{bounty.url}}">
href="https://www.facebook.com/sharer/sharer.php?u={{invite_url}}">
<i class="fab fa-fw fa-facebook-f"></i>
</a>
</div>
Expand Down
26 changes: 13 additions & 13 deletions app/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,24 +274,24 @@ def get_web3(network, sockets=False):
raise UnsupportedNetworkException(network)


def get_bounty_invite_url(handle, bounty_id):
"""Returns a unique url for each bounty and one who is inviting
def get_profile_from_referral_code(code):
"""Returns a profile from the unique code
Returns:
A unique string for each bounty
A unique string for each profile
"""
salt = "X96gRAVvwx52uS6w4QYCUHRfR3OaoB"
string = handle + salt + bounty_id
return base64.urlsafe_b64encode(string.encode()).decode()
return base64.urlsafe_b64decode(code.encode()).decode()


def get_profile_from_referral_code(code):
"""Returns a profile from the unique code
def get_bounty_invite_url(inviter, bounty_id):
"""Returns a unique url for each bounty and one who is inviting
Returns:
A unique string for each profile
A unique string for each bounty
"""
return base64.urlsafe_b64decode(code.encode()).decode()
salt = "X96gRAVvwx52uS6w4QYCUHRfR3OaoB"
string = str(inviter) + salt + str(bounty_id)
return base64.urlsafe_b64encode(string.encode()).decode()


def get_bounty_from_invite_url(invite_url):
Expand All @@ -303,9 +303,9 @@ def get_bounty_from_invite_url(invite_url):
salt = "X96gRAVvwx52uS6w4QYCUHRfR3OaoB"
decoded_string = base64.urlsafe_b64decode(invite_url.encode()).decode()
data_array = decoded_string.split(salt)
handle = data_array[0]
bounty_id = data_array[1]
return {'handle': handle, 'bounty_id': bounty_id}
inviter = data_array[0]
bounty = data_array[1]
return {'inviter': inviter, 'bounty': bounty}


def getStandardBountiesContractAddresss(network):
Expand Down
41 changes: 34 additions & 7 deletions app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@

from .helpers import get_bounty_data_for_activity, handle_bounty_views
from .models import (
Activity, Bounty, BountyFulfillment, CoinRedemption, CoinRedemptionRequest, FeedbackEntry, Interest, LabsResearch,
Profile, ProfileSerializer, Subscription, Tool, ToolVote, UserAction,
Activity, Bounty, BountyFulfillment, BountyInvites, CoinRedemption, CoinRedemptionRequest, FeedbackEntry, Interest,
LabsResearch, Profile, ProfileSerializer, Subscription, Tool, ToolVote, UserAction,
)
from .notifications import (
maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_email,
Expand Down Expand Up @@ -774,10 +774,8 @@ def social_contribution_modal(request):
TemplateResponse: The accept bounty view.
"""
from .utils import get_bounty_invite_url
bounty = handle_bounty_views(request)
promo_text = str(_("Check out this bounty that pays out ")) + f"{bounty.get_value_true} {bounty.token_name} {bounty.url}"
for keyword in bounty.keywords_list:
promo_text += f" #{keyword}"

params = get_context(
ref_object=bounty,
Expand All @@ -786,6 +784,10 @@ def social_contribution_modal(request):
active='social_contribute',
title=_('Social Contribute'),
)
params['invite_url'] = f'{settings.BASE_URL}issue/{get_bounty_invite_url(request.user.username, bounty.pk)}'
promo_text = str(_("Check out this bounty that pays out ")) + f"{bounty.get_value_true} {bounty.token_name} {params['invite_url']}"
for keyword in bounty.keywords_list:
promo_text += f" #{keyword}"
params['promo_text'] = promo_text
return TemplateResponse(request, 'social_contribution_modal.html', params)

Expand All @@ -799,12 +801,21 @@ def social_contribution_email(request):
"""
from marketing.mails import share_bounty

print (request.POST.getlist('usersId[]', []))
emails = []
user_ids = request.POST.getlist('usersId[]', [])
url = request.POST.get('url', '')
inviter = request.user if request.user.is_authenticated else None
bounty = Bounty.objects.current().get(github_url=url)
for user_id in user_ids:
profile = Profile.objects.get(id=int(user_id))
bounty_invite = BountyInvites.objects.create(
status='pending'
)
bounty_invite.bounty.add(bounty)
bounty_invite.inviter.add(inviter)
bounty_invite.invitee.add(profile.user)
emails.append(profile.email)

msg = request.POST.get('msg', '')
try:
share_bounty(emails, msg, request.user.profile)
Expand Down Expand Up @@ -1108,7 +1119,23 @@ def bounty_invite_url(request, invitecode):
django.template.response.TemplateResponse: The Bounty details template response.
"""
decoded_data = get_bounty_from_invite_url(invitecode)
bounty = Bounty.objects.current().filter(pk=decoded_data['bounty_id'])
bounty = Bounty.objects.current().filter(pk=decoded_data['bounty']).first()
inviter = User.objects.filter(username=decoded_data['inviter']).first()
bounty_invite = BountyInvites.objects.filter(
bounty=bounty,
inviter=inviter,
invitee=request.user
).first()
if bounty_invite:
bounty_invite.status = 'accepted'
bounty_invite.save()
else:
bounty_invite = BountyInvites.objects.create(
status='accepted'
)
bounty_invite.bounty.add(bounty)
bounty_invite.inviter.add(inviter)
bounty_invite.invitee.add(request.user)
return redirect('/funding/details/?url=' + bounty.github_url)


Expand Down

0 comments on commit efdae2b

Please sign in to comment.