diff --git a/app/app/settings.py b/app/app/settings.py index 56f8473a743..771d3f704b1 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -812,6 +812,7 @@ def callback(request): MINICLR_ADDRESS = env('MINICLR_ADDRESS', default='0x00De4B13153673BCAE2616b67bf822500d325Fc3') MINICLR_PRIVATE_KEY = env('MINICLR_PRIVATE_KEY', default='0x00De4B13153673BCAE2616b67bf822500d325Fc3') + AVATAR_ADDRESS = env('AVATAR_ADDRESS', default='0x00De4B13153673BCAE2616b67bf822500d325Fc3') AVATAR_PRIVATE_KEY = env('AVATAR_PRIVATE_KEY', default='0x00De4B13153673BCAE2616b67bf822500d325Fc3') diff --git a/app/assets/onepager/js/send.js b/app/assets/onepager/js/send.js index babaf9857d4..5c0ded2ab8f 100644 --- a/app/assets/onepager/js/send.js +++ b/app/assets/onepager/js/send.js @@ -170,7 +170,7 @@ function isNumeric(n) { } -function sendTip(email, github_url, from_name, username, amount, comments_public, comments_priv, from_email, accept_tos, tokenAddress, expires, success_callback, failure_callback, is_for_bounty_fulfiller) { +function sendTip(email, github_url, from_name, username, amount, comments_public, comments_priv, from_email, accept_tos, tokenAddress, expires, success_callback, failure_callback, is_for_bounty_fulfiller, noAvailableUser) { if (typeof web3 == 'undefined') { _alert({ message: gettext('You must have a web3 enabled browser to do this. Please download Metamask.') }, 'warning'); failure_callback(); @@ -230,16 +230,19 @@ function sendTip(email, github_url, from_name, username, amount, comments_public failure_callback(); return; } + if (!isNumeric(amountInDenom) || amountInDenom == 0) { - _alert({ message: gettext('You must enter an number for the amount!') }, 'warning'); + _alert({ message: gettext('You must enter a number for the amount!') }, 'warning'); failure_callback(); return; } - if (username == '') { + + if (username == '' && !noAvailableUser) { _alert({ message: gettext('You must enter a username.') }, 'warning'); failure_callback(); return; } + if (!accept_tos) { _alert({ message: gettext('You must accept the terms.') }, 'warning'); failure_callback(); diff --git a/app/assets/v2/js/activity.js b/app/assets/v2/js/activity.js index 5c35919cbc9..97a281b78f6 100644 --- a/app/assets/v2/js/activity.js +++ b/app/assets/v2/js/activity.js @@ -481,6 +481,37 @@ $(document).ready(function() { }); + $(document).on('click', '.award', function(e) { + e.preventDefault(); + if (!document.contxt.github_handle) { + _alert('Please login first.', 'error'); + return; + } + + activityId = $(this).data('activity'); + commentId = $(this).data('comment'); + + // remote post + var params = { + 'method': 'award', + 'comment': commentId, + 'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val() + }; + var url = '/api/v0.1/activity/' + activityId; + var parent = $(this).parents('.row.box'); + + parent.find('.loading').removeClass('hidden'); + $.post(url, params, function(response) { + // no message to be sent + $('button[data-activity=' + activityId + ']').remove(); + parent.find('.loading').addClass('hidden'); + _alert('Tip user successful!'); + }).fail(function() { + parent.find('.error').removeClass('hidden'); + }); + }); + + // like activity $(document).on('click', '.like_activity, .flag_activity, .favorite_activity, .pin_activity', function(e) { e.preventDefault(); @@ -729,21 +760,28 @@ $(document).ready(function() { the_comment = urlify(the_comment); the_comment = linkify(the_comment); + the_comment = the_comment.replace(/\r\n|\r|\n/g, '
'); const timeAgo = timedifferenceCvrt(new Date(comment['created_on'])); const show_tip = true; const is_comment_owner = document.contxt.github_handle == comment['profile_handle']; + + const can_award = ( + response.author === document.contxt.github_handle && + response.has_tip == true && + response.tip_available == true); + const can_redeem = response.can_redeem; + const is_edited = typeof comment['is_edited'] !== 'undefined' ? comment['is_edited'] : false; + var sorted_match_curve_html = ''; if (comment['sorted_match_curve']) { - var match_curve = Array.from(convert_to_dict(comment['sorted_match_curve']).values()); for (let j = 0; j < match_curve.length; j++) { let ele = match_curve[j]; - sorted_match_curve_html += '
  • '; sorted_match_curve_html += `Your contribution of ${ele.name} could yield $${Math.round(ele.value * 1000) / 1000} in matching.`; sorted_match_curve_html += '
  • '; @@ -827,6 +865,16 @@ $(document).ready(function() {
    ${the_comment}
    + ${can_award && ` + ` || ''} + ${!!comment['redeem_link'] && can_redeem && ` + Redeem tip` || ''} + ${!!comment['redeem_link'] && !can_redeem && ` + Tip redeemed` || ''} diff --git a/app/assets/v2/js/status.js b/app/assets/v2/js/status.js index 1ac2ec5cbbf..a7e16334278 100644 --- a/app/assets/v2/js/status.js +++ b/app/assets/v2/js/status.js @@ -408,6 +408,13 @@ $(document).ready(function() { } }); + $('#btn_attach').on('click', function() { + const el = $('#attach-dropdown'); + + el.toggle(); + }); + + function submitStatusUpdate() { if ($('#btn_post').is(':disabled')) { return; @@ -470,9 +477,16 @@ $(document).ready(function() { data.append('image', image); } } + + const attach = $('#attach-dropdown')[0].style.display; + const amount = $('#attachAmount').val(); + const address = $('#attachToken').val(); + const token_name = $('#attachToken :selected').text(); + $('#bg-selector').attr('data-selected', null); $('#bg-selector').addClass('d-none'); $('#bg-selector').children('div').children('div').addClass('d-none'); + var fail_callback = function() { message.val(the_message); localStorage.setItem(lskey, the_message); @@ -482,21 +496,108 @@ $(document).ready(function() { ); }; - for (let i = 0; i < 5; i++) { - const val = $('#poll_container input[name=option' + i + ']').val(); + const success_callback = function(txid) { + const url = 'https://' + etherscanDomain() + '/tx/' + txid; + const msg = 'This payment has been sent đź‘Ś [Etherscan Link]'; + + _alert(msg, 'info', 1000); + + data.append('attachTxId', txid); + fetch('/api/v0.1/activity', { + method: 'post', + body: data + }).then(response => { + if (response.status === 200) { + $('#thumbnail').hide(); + $('#thumbnail-title').text(''); + $('#thumbnail-provider').text(''); + $('#thumbnail-desc').text(''); + $('#thumbnail-img').attr('src', ''); + $('#preview').hide(); + $('#preview-img').attr('src', ''); + $('#attach-dropdown').hide(); + $('#attachAmount').val(''); + + embedded_resource = ''; + + _alert( + { message: gettext('Status has been saved.') }, + 'success', + 1000 + ); + const activityContainer = document.querySelector('.tab-section.active .activities'); + + if (!activityContainer) { + document.run_long_poller(false); + // success + return; + } + activityContainer.setAttribute('page', 0); + $('.tab-section.active .activities').html(''); + message.val(''); + } else { + _alert( + { message: gettext('An error occurred. Please try again.') }, + 'error' + ); + } + }).catch(err => fail_callback()); + }; + + const failure_callback = function() { + $.noop(); // do nothing + }; + + if (!isNaN(parseFloat(amount)) && address) { + data.append('attachToken', address); + data.append('attachAmount', amount); + data.append('attachTokenName', token_name); + const email = ''; + const github_url = ''; + const from_name = document.contxt['github_handle']; + const username = ''; + const amountInEth = amount; + const comments_priv = ''; + const comments_public = ''; + const from_email = ''; + const accept_tos = true; + const tokenAddress = address; + const expires = 9999999999; + + sendTip( + email, + github_url, + from_name, + username, + amountInEth, + comments_public, + comments_priv, + from_email, + accept_tos, + tokenAddress, + expires, + success_callback, + failure_callback, + false, + true, // No available user to send tip at this moment + ); - if (val) { - data.append('option' + i, val); + } else { + for (let i = 0; i < 5; i++) { + const val = $('#poll_container input[name=option' + i + ']').val(); + + if (val) { + data.append('option' + i, val); + } } - } - $('#poll_container').remove(); - $('#video_container').remove(); - fetch('/api/v0.1/activity', { - method: 'post', - body: data - }) - .then(response => { + $('#poll_container').remove(); + $('#video_container').remove(); + + fetch('/api/v0.1/activity', { + method: 'post', + body: data + }).then(response => { if (response.status === 200) { $('#thumbnail').hide(); $('#thumbnail-title').text(''); @@ -505,6 +606,8 @@ $(document).ready(function() { $('#thumbnail-img').attr('src', ''); $('#preview').hide(); $('#preview-img').attr('src', ''); + $('#attach-dropdown').hide(); + $('#attachAmount').val(''); embedded_resource = ''; _alert( @@ -525,8 +628,8 @@ $(document).ready(function() { } else { fail_callback(); } - }) - .catch(err => fail_callback()); + }).catch(err => fail_callback()); + } } }); diff --git a/app/dashboard/templates/profiles/status_box.html b/app/dashboard/templates/profiles/status_box.html index 0e3f0c1f16b..510b08f5dc9 100644 --- a/app/dashboard/templates/profiles/status_box.html +++ b/app/dashboard/templates/profiles/status_box.html @@ -31,7 +31,9 @@
    - + + +
    @@ -61,6 +63,28 @@ +
    diff --git a/app/retail/views.py b/app/retail/views.py index 99eccba8322..c1eaa98585c 100644 --- a/app/retail/views.py +++ b/app/retail/views.py @@ -42,7 +42,7 @@ from app.utils import get_default_network, get_profiles_from_text from cacheops import cached_as, cached_view, cached_view_as from dashboard.models import ( - Activity, Bounty, HackathonEvent, Profile, TribeMember, get_my_earnings_counter_profiles, get_my_grants, + Activity, Bounty, HackathonEvent, Profile, TribeMember, get_my_earnings_counter_profiles, get_my_grants, Tip, ) from dashboard.notifications import amount_usdt_open_work, open_bounties from dashboard.tasks import grant_update_email_task @@ -1092,6 +1092,7 @@ def activity(request): 'pinned': None, 'target': f'/activity?what={what}&trending_only={trending_only}&page={next_page}', 'title': _('Activity Feed'), + 'TOKENS': request.user.profile.token_approvals.all() if request.user.is_authenticated else [], 'my_tribes': list(request.user.profile.tribe_members.values_list('org__handle',flat=True)) if request.user.is_authenticated else [], } context["activities"] = [a.view_props_for(request.user) for a in page] @@ -1110,6 +1111,10 @@ def create_status_update(request): resource = request.POST.get('resource', '') provider = request.POST.get('resourceProvider', '') resource_id = request.POST.get('resourceId', '') + attach_token = request.POST.get('attachToken', '') + attach_amount = request.POST.get('attachAmount', '') + attach_token_name = request.POST.get('attachTokenName', '') + tx_id = request.POST.get('attachTxId', '') kwargs = { 'activity_type': 'status_update', @@ -1125,6 +1130,15 @@ def create_status_update(request): } } + if tx_id: + kwargs['tip'] = Tip.objects.get(txid=tx_id) + amount = float(attach_amount) + kwargs['metadata']['attach'] = { + 'amount': amount, + 'token': attach_token, + 'token_name': attach_token_name, + } + if resource == 'content': meta = kwargs['metadata']['resource'] meta['title'] = request.POST.get('title', '') diff --git a/app/townsquare/migrations/0021_comment_tip.py b/app/townsquare/migrations/0021_comment_tip.py new file mode 100644 index 00000000000..3f30a5bd580 --- /dev/null +++ b/app/townsquare/migrations/0021_comment_tip.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.4 on 2020-05-26 14:29 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('dashboard', '0114_auto_20200522_0730'), + ('townsquare', '0020_auto_20200521_1020'), + ] + + operations = [ + migrations.AddField( + model_name='comment', + name='tip', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='awards', to='dashboard.Tip'), + ), + ] diff --git a/app/townsquare/models.py b/app/townsquare/models.py index f5b9ced4e8a..ffd20796aa0 100644 --- a/app/townsquare/models.py +++ b/app/townsquare/models.py @@ -57,6 +57,13 @@ class Comment(SuperModel): activity = models.ForeignKey('dashboard.Activity', on_delete=models.CASCADE, related_name='comments', blank=True, db_index=True) comment = models.TextField(default='', blank=True) + tip = models.ForeignKey( + 'dashboard.Tip', + related_name='awards', + on_delete=models.CASCADE, + blank=True, + null=True + ) likes = ArrayField(models.IntegerField(), default=list, blank=True) #pks of users who like this post likes_handles = ArrayField(models.CharField(max_length=200, blank=True), default=list, blank=True) #handles of users who like this post tip_count_eth = models.DecimalField(default=0, decimal_places=5, max_digits=50) @@ -69,6 +76,16 @@ def __str__(self): def profile_handle(self): return self.profile.handle + @property + def redeem_link(self): + if self.tip: + return self.tip.receive_url + return '' + + @property + def tip_able(self): + return self.activity.metadata.get("tip_able", False) + @property def url(self): return self.activity.url diff --git a/app/townsquare/templates/townsquare/index.html b/app/townsquare/templates/townsquare/index.html index 2717f552c1a..64b50f69bdc 100644 --- a/app/townsquare/templates/townsquare/index.html +++ b/app/townsquare/templates/townsquare/index.html @@ -215,6 +215,14 @@

    We’re excited you’re here! Let’s ge {% include 'shared/analytics.html' %} {% include 'shared/footer_scripts.html' with slim=1 %} {% include 'shared/activity_scripts.html' %} + + + + + + + + diff --git a/app/townsquare/templates/townsquare/shared/shareactivity.html b/app/townsquare/templates/townsquare/shared/shareactivity.html index 177d14a73a0..7d1a333ed73 100644 --- a/app/townsquare/templates/townsquare/shared/shareactivity.html +++ b/app/townsquare/templates/townsquare/shared/shareactivity.html @@ -40,7 +40,8 @@

    - + +
    @@ -59,6 +60,28 @@
    +