From 86513a3205db19708cb5467c548c9b9aaadf7c40 Mon Sep 17 00:00:00 2001 From: octavioamu Date: Thu, 27 Feb 2020 03:33:11 -0300 Subject: [PATCH 01/63] add details vue --- app/assets/v2/css/base.css | 4 +- app/assets/v2/css/bounty.css | 21 +- app/assets/v2/css/gitcoin.css | 2 + app/assets/v2/js/pages/bounty_details2.js | 92 ++++++ app/dashboard/router.py | 1 + app/dashboard/templates/bounty/details2.html | 324 +++++++++++++++++++ app/dashboard/views.py | 10 +- 7 files changed, 441 insertions(+), 13 deletions(-) create mode 100644 app/assets/v2/js/pages/bounty_details2.js create mode 100644 app/dashboard/templates/bounty/details2.html diff --git a/app/assets/v2/css/base.css b/app/assets/v2/css/base.css index 153a73a8a12..57ea9932669 100644 --- a/app/assets/v2/css/base.css +++ b/app/assets/v2/css/base.css @@ -181,10 +181,10 @@ a { margin-left: 0; } -.interior .container-fluid { +/* .interior .container-fluid { padding-left: 0; padding-right: 0; -} +} */ .with-grey-background .body { background-color: #eee !important; diff --git a/app/assets/v2/css/bounty.css b/app/assets/v2/css/bounty.css index b1f9308905b..2dbd441a0b9 100644 --- a/app/assets/v2/css/bounty.css +++ b/app/assets/v2/css/bounty.css @@ -71,7 +71,7 @@ body { font-style: italic; } -.bounty_details .avatar { +.avatar { width: 50px; height: 50px; margin: 0px auto; @@ -83,7 +83,7 @@ body { border-radius: 0px; } -#avatar_url .avatar { +.avatar-big { width: 80px; height: 80px; } @@ -170,8 +170,9 @@ body { color: #3E00FF; } -#issue_description { +.issue_description { color: #000000; + font-size: 14px; } .bounty-subheading { @@ -460,7 +461,7 @@ a.btn { top: -2px; } -#bounty_details #issue_description img { +.issue_description img { max-height: 30rem; max-width: 30rem; cursor: pointer; @@ -469,12 +470,12 @@ a.btn { margin-bottom: 1rem; } -#bounty_details #issue_description h1, -#bounty_details #issue_description h2, -#bounty_details #issue_description h3, -#bounty_details #issue_description h4, -#bounty_details #issue_description h5, -#bounty_details #issue_description h6 { +.issue_description h1, +.issue_description h2, +.issue_description h3, +.issue_description h4, +.issue_description h5, +.issue_description h6 { letter-spacing: 1px; font-size: 16px; margin-top: 15px; diff --git a/app/assets/v2/css/gitcoin.css b/app/assets/v2/css/gitcoin.css index 8eb58da0efa..cb48cc1c757 100644 --- a/app/assets/v2/css/gitcoin.css +++ b/app/assets/v2/css/gitcoin.css @@ -11,6 +11,8 @@ h4, padding-top: 40px; } +[v-cloak] > * { display:none!important;} + #indicate-popup { position: fixed; top: 0.25rem; diff --git a/app/assets/v2/js/pages/bounty_details2.js b/app/assets/v2/js/pages/bounty_details2.js new file mode 100644 index 00000000000..098f2e7c4b3 --- /dev/null +++ b/app/assets/v2/js/pages/bounty_details2.js @@ -0,0 +1,92 @@ +let bounty = []; +let url = location.href; + +Vue.mixin({ + methods: { + fetchBounty: function() { + let vm = this; + let apiUrlBounty = `/actions/api/v0.1/bounty?github_url=${document.issueURL}`; + const getBounty = fetchData(apiUrlBounty, 'GET'); + + $.when(getBounty).then(function(response) { + vm.bounty = response[0]; + vm.isOwner = vm.checkOwner(response[0].bounty_owner_github_username) + }) + }, + checkOwner: function(handle) { + let vm = this; + if (document.contxt['github_handle']) { + return caseInsensitiveCompare(document.contxt['github_handle'], handle); + } else { + return false; + } + }, + syncGhIssue: function() { + let vm = this; + let apiUrlIssueSync = `/sync/get_issue_details?url=${encodeURIComponent(vm.bounty.github_url)}&token=${currentProfile.githubToken}`; + const getIssueSync = fetchData(apiUrlIssueSync, 'GET'); + + $.when(getIssueSync).then(function(response) { + vm.updateGhIssue(response) + }) + }, + updateGhIssue: function(response) { + let vm = this; + const payload = JSON.stringify({ + issue_description: response.description, + title: response.title + }); + let apiUrlUpdateIssue = `/bounty/change/${vm.bounty.pk}`; + const postUpdateIssue = fetchData(apiUrlUpdateIssue, 'POST', payload); + + $.when(postUpdateIssue).then(function(response) { + vm.bounty.issue_description = response.description; + vm.bounty.title = response.title; + _alert({ message: response.msg }, 'success'); + }).catch(function(response) { + _alert({ message: response.responseJSON.error }, 'error'); + }) + } + }, + computed: { + + } +}); + +Vue.filter('myfilter', function(val) { + if (!val) return ''; + const _markdown = new markdownit({ + linkify: true, + highlight: function(str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return `
${hljs.highlight(lang, str, true).value}
`; + } catch (__) {} + } + return `
${sanitize(_markdown.utils.escapeHtml(str))}
`; + } + }); + + _markdown.renderer.rules.table_open = function() { + return ''; + }; + ui_body = sanitize(_markdown.render(val)); + return ui_body; +}); + + +if (document.getElementById('gc-bounty-detail')) { + var app = new Vue({ + delimiters: [ '[[', ']]' ], + el: '#gc-bounty-detail', + data: { + bounty: bounty, + url: url, + cb_address: cb_address, + isOwner: false + }, + mounted() { + this.fetchBounty(); + } + }); +} diff --git a/app/dashboard/router.py b/app/dashboard/router.py index ca219717c4f..a1198d97b8d 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -462,4 +462,5 @@ class BountyViewSetCheckIn(BountyViewSet): router = routers.DefaultRouter() router.register(r'bounties/slim', BountyViewSetSlim) router.register(r'bounties', BountyViewSet) +router.register(r'bounty', BountyViewSet) router.register(r'checkin', BountyViewSetCheckIn) diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html new file mode 100644 index 00000000000..7e383f6dcb5 --- /dev/null +++ b/app/dashboard/templates/bounty/details2.html @@ -0,0 +1,324 @@ +{% load i18n static %} + + + + + {% include 'shared/head.html' %} + {% include 'shared/cards_pic.html' %} + + + {% if canonical_url %} + + {% endif %} + + + + + + + + + + {% include 'shared/tag_manager_2.html' %} + {% include 'shared/top_nav.html' with class='d-md-flex' %} +
+ {% include 'shared/nav.html' %} +
+ +
+
+
+ {% if event_tag %} + {% trans "Back to Hackathon Explorer" %} + {% else %} + {% trans "Back to Issue Explorer" %} + {% endif %} +
+
+
+
+ +
+
+
+ Hackathon: [[bounty.event.name]] +
+

[[bounty.title]]

+
+
+
+

+ + [[ bounty.value_true ]] [[ bounty.token_name ]] +

+
+
+

+ [[bounty.value_in_usdt_now]] + USD +

+
+
+ +
+
+
+ +
+
+
+
+ WARNING: this is a [[bounty.network]] network bounty, and is NOT real money. To see mainnet bounties, go to the bounty explorer and search for mainnet bounties. +
+
+
[[ bounty.status ]]
+
+
+
+
+ {% trans "Time left" %} + +
+
+ {% trans "Opened" %} + +
+
+ {% trans "Issue Type" %} + [[ bounty.bounty_type ]] +
+ +
+ {% trans "Workers Auto Approve" %} + [[ bounty.admin_override_suspend_auto_approval ? 'Off' : 'On' ]] +
+
+ + {% trans "Project Type" %} + + [[ bounty.project_type ]] + +
+ +
+ {% trans "Time Commitment" %} + [[ bounty.project_length ]] +
+
+ {% trans "Experience Level" %} + [[ bounty.experience_level ]] +
+
+ {% trans "Permissions" %} + [[ bounty.permission_type ]] +
+
+ {% trans "Accepted" %} + +
+
+ {% trans "Reserved For" %} + [[ bounty.reserved_for_user_handle ]] +
+
+ + {% if is_bounties_network %} +
+
+ {% trans "Funder Address:" %} [[ bounty.bounty_owner_address ]] + + Ready to Pay? Set Your Metamask to this address! + +
+
+ {% endif %} + + +
+
+
{% trans "Description" %}
+
+
+
+
+
+ +
+
+
+
+ +
+
+
{% trans "Contributors" %}
+
+
+
+
+
+ + + + + + + + {% include 'shared/bottom_notification.html' %} + {% include 'shared/analytics.html' %} + {% include 'shared/footer_scripts.html' with slim=1 %} + {% include 'shared/footer.html' %} + {% include 'shared/current_profile.html' %} + + + + + + + + + + + + + + + + + + + + + {% for message in messages %} + {% if message.tags == 'success'%} + + {% endif %} + + {% endfor %} + + + diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 013874fbaa8..c82fa75419d 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -1923,6 +1923,14 @@ def bounty_invite_url(request, invitecode): raise Http404 +def bounty_details_v2(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None): + # try the /pulls url if it doesn't exist in /issues + try: + issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/issues/' + ghissue if ghissue else request_url + bounty = Bounty.objects.current().filter(github_url=issue_url) + except Exception: + issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/pull/' + ghissue if ghissue else request_url + bounty = Bounty.objects.current().filter(github_url=issue_url) def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None): """Display the bounty details. @@ -2017,7 +2025,7 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None except Exception as e: logger.error(e) - return TemplateResponse(request, 'bounty/details.html', params) + return TemplateResponse(request, 'bounty/details2.html', params) def funder_payout_reminder_modal(request, bounty_network, stdbounties_id): From 179a1f8c18cef03a233cbca919e8f2bf9a391e30 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 27 Feb 2020 18:30:02 +0530 Subject: [PATCH 02/63] optimize bounty_details view --- app/dashboard/views.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/app/dashboard/views.py b/app/dashboard/views.py index c82fa75419d..8ff3bed149d 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -1923,15 +1923,6 @@ def bounty_invite_url(request, invitecode): raise Http404 -def bounty_details_v2(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None): - # try the /pulls url if it doesn't exist in /issues - try: - issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/issues/' + ghissue if ghissue else request_url - bounty = Bounty.objects.current().filter(github_url=issue_url) - except Exception: - issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/pull/' + ghissue if ghissue else request_url - bounty = Bounty.objects.current().filter(github_url=issue_url) - def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None): """Display the bounty details. @@ -1954,13 +1945,20 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None _access_token = request.user.profile.get_access_token() else: _access_token = request.session.get('access_token') - issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/issues/' + ghissue if ghissue else request_url - # try the /pulls url if it doesn't exist in /issues try: - assert Bounty.objects.current().filter(github_url=issue_url).exists() + if ghissue: + issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/issues/' + ghissue + bounties = Bounty.objects.current().filter(github_url=issue_url) + if not bounties.exists(): + issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/pull/' + ghissue + bounties = Bounty.objects.current().filter(github_url=issue_url) + else: + issue_url = request_url + bounties = Bounty.objects.current().filter(github_url=issue_url) + except Exception: - issue_url = 'https://github.com/' + ghuser + '/' + ghrepo + '/pull/' + ghissue if ghissue else request_url + pass params = { 'issueURL': issue_url, @@ -1976,7 +1974,6 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None } if issue_url: try: - bounties = Bounty.objects.current().filter(github_url=issue_url) if stdbounties_id and stdbounties_id.isdigit(): stdbounties_id = clean_str(stdbounties_id) bounties = bounties.filter(standard_bounties_id=stdbounties_id) @@ -2009,7 +2006,6 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None if bounty.event: params['event_tag'] = bounty.event.slug params['prize_projects'] = HackathonProject.objects.filter(hackathon=bounty.event, bounty__standard_bounties_id=bounty.standard_bounties_id).exclude(status='invalid').prefetch_related('profiles') - print(params['prize_projects']) helper_handle_snooze(request, bounty) helper_handle_approvals(request, bounty) @@ -2025,7 +2021,7 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None except Exception as e: logger.error(e) - return TemplateResponse(request, 'bounty/details2.html', params) + return TemplateResponse(request, 'bounty/details.html', params) def funder_payout_reminder_modal(request, bounty_network, stdbounties_id): From a76ca3fbf4ae23609f02341c48d2817f8b00b8a1 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 27 Feb 2020 18:32:16 +0530 Subject: [PATCH 03/63] bounty/details -> bounty/details2 --- app/dashboard/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 8ff3bed149d..ef85c5d514e 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -2021,7 +2021,7 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None except Exception as e: logger.error(e) - return TemplateResponse(request, 'bounty/details.html', params) + return TemplateResponse(request, 'bounty/details2.html', params) def funder_payout_reminder_modal(request, bounty_network, stdbounties_id): From 5f992b078c2a2445c570b6375591fa997845504f Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 27 Feb 2020 19:46:15 +0530 Subject: [PATCH 04/63] fixup issue --- app/dashboard/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/dashboard/views.py b/app/dashboard/views.py index ef85c5d514e..31370755d8a 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -3543,9 +3543,8 @@ def change_bounty(request, bounty_id): if not bounty.is_bounties_network: current_amount = float(bounty.value_true) - new_amount = float(params.get('amount')) + new_amount = float(params.get('amount')) if params.get('amount') else None if new_amount and current_amount != new_amount: - print("SHIT-2") bounty.value_true = new_amount value_in_token = params.get('value_in_token') bounty.value_in_token = value_in_token @@ -3560,7 +3559,7 @@ def change_bounty(request, bounty_id): logger.debug(e) current_hours = int(bounty.estimated_hours) - new_hours = int(params.get('hours')) + new_hours = int(params.get('hours')) if params.get('hours') else None if new_hours and current_hours != new_hours: bounty.estimated_hours = new_hours bounty.metadata['estimatedHours'] = new_hours From 5c92c37beda8e5c16d304f7bdf2f68ea43ba5663 Mon Sep 17 00:00:00 2001 From: octavioamu Date: Thu, 27 Feb 2020 12:32:35 -0300 Subject: [PATCH 05/63] add activity --- app/assets/v2/js/pages/bounty_details2.js | 29 +++++++++++++++++++- app/dashboard/router.py | 2 +- app/dashboard/templates/bounty/details2.html | 14 ++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/assets/v2/js/pages/bounty_details2.js b/app/assets/v2/js/pages/bounty_details2.js index 098f2e7c4b3..2fa994e4664 100644 --- a/app/assets/v2/js/pages/bounty_details2.js +++ b/app/assets/v2/js/pages/bounty_details2.js @@ -11,6 +11,7 @@ Vue.mixin({ $.when(getBounty).then(function(response) { vm.bounty = response[0]; vm.isOwner = vm.checkOwner(response[0].bounty_owner_github_username) + document.result = response[0]; }) }, checkOwner: function(handle) { @@ -53,7 +54,7 @@ Vue.mixin({ } }); -Vue.filter('myfilter', function(val) { +Vue.filter('markdownit', function(val) { if (!val) return ''; const _markdown = new markdownit({ linkify: true, @@ -74,6 +75,32 @@ Vue.filter('myfilter', function(val) { return ui_body; }); +Vue.filter('stringReplace', function(activity_type) { + const activity_names = { + new_bounty: gettext('Bounty Created'), + start_work: gettext('Work Started'), + stop_work: gettext('Work Stopped'), + work_submitted: gettext('Work Submitted'), + work_done: gettext('Work Done'), + worker_approved: gettext('Approved'), + worker_rejected: gettext('Rejected Contributor'), + worker_applied: gettext('Contributor Applied'), + increased_bounty: gettext('Increased Funding'), + killed_bounty: gettext('Canceled Bounty'), // All other sections become empty ? + new_crowdfund: gettext('Added new Crowdfund Contribution'), + new_tip: gettext('Tip Sent'), + receive_tip: gettext('Tip Received'), + bounty_changed: gettext('Bounty Details Updated'), + extend_expiration: gettext('Extended Bounty Expiration'), + bounty_abandonment_escalation_to_mods: gettext('Escalated for Abandonment of Bounty'), + bounty_abandonment_warning: gettext('Warned for Abandonment of Bounty'), + bounty_removed_slashed_by_staff: gettext('Dinged and Removed from Bounty by Staff'), + bounty_removed_by_staff: gettext('Removed from Bounty by Staff'), + bounty_removed_by_funder: gettext('Removed from Bounty by Funder'), + }; + return activity_names[activity_type]; +}) + if (document.getElementById('gc-bounty-detail')) { var app = new Vue({ diff --git a/app/dashboard/router.py b/app/dashboard/router.py index a1198d97b8d..36ce14cc470 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -110,7 +110,7 @@ class Meta: """Define the activity serializer metadata.""" model = Activity - fields = ('activity_type', 'created', 'profile', 'metadata', 'bounty', 'tip', 'kudos') + fields = ('activity_type', 'pk', 'created', 'profile', 'metadata', 'bounty', 'tip', 'kudos') class InterestSerializer(serializers.ModelSerializer): diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html index 7e383f6dcb5..adb41251d0b 100644 --- a/app/dashboard/templates/bounty/details2.html +++ b/app/dashboard/templates/bounty/details2.html @@ -200,7 +200,7 @@

[[bounty.title]]<
{% trans "Description" %}
-
+
@@ -215,7 +215,17 @@
{% trans "Description" %}<
-
{% trans "Contributors" %}
+
+
{% trans "All Activity" %}
+
+ + [[ activity.profile.handle ]] + [[ activity.activity_type | stringReplace ]] + +
+
From be81ba4059b62aa9fe7c0550155374b16e8e0893 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 27 Feb 2020 21:36:36 +0530 Subject: [PATCH 06/63] update viewset for bounty details --- app/dashboard/router.py | 56 +++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/app/dashboard/router.py b/app/dashboard/router.py index a1198d97b8d..01c13339db7 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -166,7 +166,8 @@ class Meta: 'attached_job_description', 'needs_review', 'github_issue_state', 'is_issue_closed', 'additional_funding_summary', 'funding_organisation', 'paid', 'event', 'admin_override_suspend_auto_approval', 'reserved_for_user_handle', 'is_featured', - 'featuring_date', 'repo_type', 'unsigned_nda', 'funder_last_messaged_on', 'can_remarket', 'is_reserved' + 'featuring_date', 'repo_type', 'unsigned_nda', 'funder_last_messaged_on', 'can_remarket', 'is_reserved', + 'payout_confirmed', 'payout_tx_id' ) def create(self, validated_data): @@ -224,8 +225,8 @@ class Meta: ) -class BountyViewSet(viewsets.ModelViewSet): - """Handle the Bounty view behavior.""" +class BountiesViewSet(viewsets.ModelViewSet): + """Handle Bounties view behavior.""" queryset = Bounty.objects.prefetch_related('fulfillments', 'interested', 'interested__profile', 'activities', 'unsigned_nda', 'event') \ .all().order_by('-web3_created') serializer_class = BountySerializer @@ -450,17 +451,56 @@ def get_queryset(self): return queryset -class BountyViewSetSlim(BountyViewSet): +class BountiesViewSetSlim(BountiesViewSet): queryset = Bounty.objects.all().order_by('-web3_created') serializer_class = BountySerializerSlim -class BountyViewSetCheckIn(BountyViewSet): +class BountiesViewSetCheckIn(BountiesViewSet): queryset = Bounty.objects.all().order_by('standard_bounties_id') serializer_class = BountySerializerCheckIn + +class BountyViewSet(viewsets.ModelViewSet): + """API response for an individual bounty by url""" + + queryset = Bounty.objects.prefetch_related( + 'fulfillments', 'interested', 'interested__profile', 'activities', + 'unsigned_nda', 'event' + ) + serializer_class = BountySerializer + filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) + + def get_queryset(self): + """Constructs queryset for an individual bounty + + Returns: + QuerySet: The Bounty queryset. + + """ + + param_keys = self.request.query_params.keys() + + queryset = Bounty.objects.prefetch_related( + 'fulfillments', 'interested', 'interested__profile', 'activities', + 'unsigned_nda', 'event' + ) + + queryset = queryset.current() + + if 'github_url' in param_keys: + url = self.request.query_params.get('github_url') + queryset = queryset.filter(github_url=url) + + queryset = queryset.order_by('-web3_created') + queryset = queryset.distinct() + + return queryset + + # Routers provide an easy way of automatically determining the URL conf. router = routers.DefaultRouter() -router.register(r'bounties/slim', BountyViewSetSlim) -router.register(r'bounties', BountyViewSet) +router.register(r'bounties/slim', BountiesViewSetSlim) +router.register(r'bounties', BountiesViewSet) +router.register(r'checkin', BountiesViewSetCheckIn) + router.register(r'bounty', BountyViewSet) -router.register(r'checkin', BountyViewSetCheckIn) From 94f1288dd4efdf1d16ff046c104c06226d81f2aa Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 27 Feb 2020 22:28:22 +0530 Subject: [PATCH 07/63] update activity section --- app/assets/v2/css/bounty.css | 2 +- app/dashboard/templates/bounty/details2.html | 71 ++++++++++++-------- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/app/assets/v2/css/bounty.css b/app/assets/v2/css/bounty.css index 2dbd441a0b9..863451b3883 100644 --- a/app/assets/v2/css/bounty.css +++ b/app/assets/v2/css/bounty.css @@ -186,7 +186,7 @@ body { .bounty-info-heading { color: #0D0764; - font-weight: 600; + font-weight: 500; text-transform: capitalize; } diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html index adb41251d0b..12da74f73df 100644 --- a/app/dashboard/templates/bounty/details2.html +++ b/app/dashboard/templates/bounty/details2.html @@ -27,25 +27,27 @@
-
+ -
-
+
-
+
Hackathon: [[bounty.event.name]]

[[bounty.title]]

-
+

@@ -74,7 +76,8 @@

[[bounty.title]]<

- WARNING: this is a [[bounty.network]] network bounty, and is NOT real money. To see mainnet bounties, go to the bounty explorer and search for mainnet bounties. + WARNING: this is a [[bounty.network]] network bounty, and is NOT real money. + To see mainnet bounties, go to the bounty explorer and search for mainnet bounties.
[[ bounty.status ]]
@@ -93,7 +96,7 @@

[[bounty.title]]< [[ bounty.web3_created | moment ]]

-
+
{% trans "Issue Type" %} [[ bounty.bounty_type ]]
@@ -110,11 +113,11 @@

[[bounty.title]]<

-
+
{% trans "Time Commitment" %} [[ bounty.project_length ]]
-
+
{% trans "Experience Level" %} [[ bounty.experience_level ]]
@@ -145,28 +148,28 @@

[[bounty.title]]<

{% endif %}
-
+

'; - }; - ui_body = sanitize(_markdown.render(val)); - return ui_body; -}); -Vue.filter('stringReplace', function(activity_type) { - const activity_names = { - new_bounty: gettext('Bounty Created'), - start_work: gettext('Work Started'), - stop_work: gettext('Work Stopped'), - work_submitted: gettext('Work Submitted'), - work_done: gettext('Work Done'), - worker_approved: gettext('Approved'), - worker_rejected: gettext('Rejected Contributor'), - worker_applied: gettext('Contributor Applied'), - increased_bounty: gettext('Increased Funding'), - killed_bounty: gettext('Canceled Bounty'), // All other sections become empty ? - new_crowdfund: gettext('Added new Crowdfund Contribution'), - new_tip: gettext('Tip Sent'), - receive_tip: gettext('Tip Received'), - bounty_changed: gettext('Bounty Details Updated'), - extend_expiration: gettext('Extended Bounty Expiration'), - bounty_abandonment_escalation_to_mods: gettext('Escalated for Abandonment of Bounty'), - bounty_abandonment_warning: gettext('Warned for Abandonment of Bounty'), - bounty_removed_slashed_by_staff: gettext('Dinged and Removed from Bounty by Staff'), - bounty_removed_by_staff: gettext('Removed from Bounty by Staff'), - bounty_removed_by_funder: gettext('Removed from Bounty by Funder'), - }; - return activity_names[activity_type]; -}) if (document.getElementById('gc-bounty-detail')) { - var app = new Vue({ + var appBounty = new Vue({ delimiters: [ '[[', ']]' ], el: '#gc-bounty-detail', - data: { - bounty: bounty, - url: url, - cb_address: cb_address, - isOwner: false + data() { + return { + bounty: bounty, + url: url, + cb_address: cb_address, + isOwner: false, + is_bounties_network: is_bounties_network + } }, mounted() { this.fetchBounty(); diff --git a/app/assets/v2/js/user_popover.js b/app/assets/v2/js/user_popover.js index 17ad45fdcca..0b8019333ab 100644 --- a/app/assets/v2/js/user_popover.js +++ b/app/assets/v2/js/user_popover.js @@ -101,7 +101,9 @@ function openContributorPopOver(contributor, element) { controller = null; element.popover({ placement: 'auto', - trigger: 'hover', + // container: element, + trigger: 'manual', + delay: { "show": 200, "hide": 500 }, template: ` `, content: renderPopOverData(response), html: true + }).on("mouseenter", function() { + var _this = this; + $(this).popover("show"); + $(".popover").on("mouseleave", function() { + $(_this).popover('hide'); + }); + }).on("mouseleave", function() { + var _this = this; + setTimeout(function() { + if (!$(".popover:hover").length) { + $(_this).popover("hide") + } + }, 100); }); $(element).popover('show'); }) @@ -119,7 +134,9 @@ function openContributorPopOver(contributor, element) { } else { element.popover({ placement: 'auto', - trigger: 'hover', + // container: element, + trigger: 'manual', + delay: { "show": 200, "hide": 500 }, template: `
'; + }; + ui_body = sanitize(_markdown.render(val)); + return ui_body; +}); + +Vue.filter('truncateHash', function(elem, _number) { + const number = !_number ? _number = 4 : _number; + let content = elem.trim(); + + return content.substr(0, number + 2) + '\u2026' + content.substr(-number); + +}) + +Vue.filter('stringReplace', function(activity_type) { + const activity_names = { + new_bounty: gettext('Bounty Created'), + start_work: gettext('Work Started'), + stop_work: gettext('Work Stopped'), + work_submitted: gettext('Work Submitted'), + work_done: gettext('Work Done'), + worker_approved: gettext('Approved'), + worker_rejected: gettext('Rejected Contributor'), + worker_applied: gettext('Contributor Applied'), + increased_bounty: gettext('Increased Funding'), + killed_bounty: gettext('Canceled Bounty'), // All other sections become empty ? + new_crowdfund: gettext('Added new Crowdfund Contribution'), + new_tip: gettext('Tip Sent'), + receive_tip: gettext('Tip Received'), + bounty_changed: gettext('Bounty Details Updated'), + extend_expiration: gettext('Extended Bounty Expiration'), + bounty_abandonment_escalation_to_mods: gettext('Escalated for Abandonment of Bounty'), + bounty_abandonment_warning: gettext('Warned for Abandonment of Bounty'), + bounty_removed_slashed_by_staff: gettext('Dinged and Removed from Bounty by Staff'), + bounty_removed_by_staff: gettext('Removed from Bounty by Staff'), + bounty_removed_by_funder: gettext('Removed from Bounty by Funder'), + new_kudos: gettext('New Kudos'), + }; + return activity_names[activity_type]; +}) + diff --git a/app/dashboard/router.py b/app/dashboard/router.py index 1d58bffad1e..7e8f760584c 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -38,13 +38,13 @@ class BountyFulfillmentSerializer(serializers.ModelSerializer): """Handle serializing the BountyFulfillment object.""" - + profile = ProfileSerializer() class Meta: """Define the bounty fulfillment serializer metadata.""" model = BountyFulfillment - fields = ('fulfiller_address', - 'fulfiller_github_username', 'fulfiller_name', + fields = ('pk', 'fulfiller_address', + 'fulfiller_github_username', 'fulfiller_name', 'fulfiller_metadata', 'fulfillment_id', 'accepted', 'profile', 'created_on', 'accepted_on', 'fulfiller_github_url') @@ -122,7 +122,7 @@ class InterestSerializer(serializers.ModelSerializer): class Meta: """Define the Interest serializer metadata.""" model = Interest - fields = ('profile', 'created', 'pending', 'signed_nda', 'issue_message') + fields = ('pk', 'profile', 'created', 'pending', 'signed_nda', 'issue_message') # Serializers define the API representation. @@ -464,7 +464,7 @@ class BountyViewSet(viewsets.ModelViewSet): """API response for an individual bounty by url""" queryset = Bounty.objects.prefetch_related( - 'fulfillments', 'interested', 'interested__profile', 'activities', + 'fulfillments', 'fulfillments__profile', 'interested', 'interested__profile', 'activities', 'unsigned_nda', 'event' ) serializer_class = BountySerializer diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html index 12da74f73df..4d907e25969 100644 --- a/app/dashboard/templates/bounty/details2.html +++ b/app/dashboard/templates/bounty/details2.html @@ -25,9 +25,9 @@ {% include 'shared/nav.html' %} -
+
- -
+
+
+
+
{% trans "SUBMISSIONS" %}
+
+ + +
+ +
+
+ +
+
+
+ PR: + + [[ fulfillment.fulfiller_github_url ]] + + [[ fulfillment.fulfiller_metadata?.data?.payload?.fulfiller?.hoursWorked ]] hours +
+
+
+
+
+
+ +
+
+
+
{% trans "APPLICANTS" %}
+
+
+ + + +
+ +
+ +
+
+ + +
+
+
+

Work Plan

+

+ [[ interest.issue_message]] +

+
+
+
+
+
+
+ +
{% trans "ALL ACTIVITY" %}
-
+
- -
+
[[ activity.activity_type | stringReplace ]] +
+ show value +
+
+ show value +
-
-
+ + +
@@ -312,26 +412,27 @@

{{ noscript.keywords }}

+ - diff --git a/app/retail/templates/shared/footer_scripts.html b/app/retail/templates/shared/footer_scripts.html index fee3bf072a2..99dd27919ce 100644 --- a/app/retail/templates/shared/footer_scripts.html +++ b/app/retail/templates/shared/footer_scripts.html @@ -23,9 +23,9 @@ -{% if user.is_authenticated and env == 'prod' %} - -{% elif user.is_authenticated %} +{% if env == 'prod' %} + +{% else %} {% endif %} {% include 'shared/sentry.html' %} @@ -60,7 +60,9 @@ + {% if user.is_authenticated %} + {% endif %} diff --git a/app/retail/templates/shared/footer_scripts_lite.html b/app/retail/templates/shared/footer_scripts_lite.html index 75257586a82..99c71ec3ea2 100644 --- a/app/retail/templates/shared/footer_scripts_lite.html +++ b/app/retail/templates/shared/footer_scripts_lite.html @@ -24,9 +24,9 @@ -{% if user.is_authenticated and env == 'prod' %} - -{% elif user.is_authenticated %} +{% if env == 'prod' %} + +{% else %} {% endif %} {% include 'shared/sentry.html' %} @@ -46,6 +46,7 @@ + {% if user.is_authenticated %} {% endif %} From c95caf3633431ed010207183acb26837a16a9338 Mon Sep 17 00:00:00 2001 From: octavioamu Date: Fri, 28 Feb 2020 02:26:17 -0300 Subject: [PATCH 09/63] add qrcode --- app/assets/v2/js/lib/qrcode.js | 614 +++++++++++++++++++ app/assets/v2/js/pages/bounty_details2.js | 3 +- app/assets/v2/js/vue-components.js | 44 ++ app/dashboard/templates/bounty/details2.html | 18 +- 4 files changed, 675 insertions(+), 4 deletions(-) create mode 100644 app/assets/v2/js/lib/qrcode.js diff --git a/app/assets/v2/js/lib/qrcode.js b/app/assets/v2/js/lib/qrcode.js new file mode 100644 index 00000000000..74563760849 --- /dev/null +++ b/app/assets/v2/js/lib/qrcode.js @@ -0,0 +1,614 @@ +/** + * @fileoverview + * - Using the 'QRCode for Javascript library' + * - Fixed dataset of 'QRCode for Javascript library' for support full-spec. + * - this library has no dependencies. + * + * @author davidshimjs + * @see http://www.d-project.com/ + * @see http://jeromeetienne.github.com/jquery-qrcode/ + */ +var QRCode; + +(function () { + //--------------------------------------------------------------------- + // QRCode for JavaScript + // + // Copyright (c) 2009 Kazuhiko Arase + // + // URL: http://www.d-project.com/ + // + // Licensed under the MIT license: + // http://www.opensource.org/licenses/mit-license.php + // + // The word "QR Code" is registered trademark of + // DENSO WAVE INCORPORATED + // http://www.denso-wave.com/qrcode/faqpatent-e.html + // + //--------------------------------------------------------------------- + function QR8bitByte(data) { + this.mode = QRMode.MODE_8BIT_BYTE; + this.data = data; + this.parsedData = []; + + // Added to support UTF-8 Characters + for (var i = 0, l = this.data.length; i < l; i++) { + var byteArray = []; + var code = this.data.charCodeAt(i); + + if (code > 0x10000) { + byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18); + byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12); + byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[3] = 0x80 | (code & 0x3F); + } else if (code > 0x800) { + byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12); + byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[2] = 0x80 | (code & 0x3F); + } else if (code > 0x80) { + byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6); + byteArray[1] = 0x80 | (code & 0x3F); + } else { + byteArray[0] = code; + } + + this.parsedData.push(byteArray); + } + + this.parsedData = Array.prototype.concat.apply([], this.parsedData); + + if (this.parsedData.length != this.data.length) { + this.parsedData.unshift(191); + this.parsedData.unshift(187); + this.parsedData.unshift(239); + } + } + + QR8bitByte.prototype = { + getLength: function (buffer) { + return this.parsedData.length; + }, + write: function (buffer) { + for (var i = 0, l = this.parsedData.length; i < l; i++) { + buffer.put(this.parsedData[i], 8); + } + } + }; + + function QRCodeModel(typeNumber, errorCorrectLevel) { + this.typeNumber = typeNumber; + this.errorCorrectLevel = errorCorrectLevel; + this.modules = null; + this.moduleCount = 0; + this.dataCache = null; + this.dataList = []; + } + + QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);} + return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row=7){this.setupTypeNumber(test);} + if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);} + this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}} + return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;} + for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}} + for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}} + this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex>>bitIndex)&1)==1);} + var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;} + this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}} + row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;itotalDataCount*8){throw new Error("code length overflow. (" + +buffer.getLengthInBits() + +">" + +totalDataCount*8 + +")");} + if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);} + while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);} + while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD1,8);} + return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r=0)?modPoly.get(modIndex):0;}} + var totalCodeCount=0;for(var i=0;i=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));} + return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));} + return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;} + return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i5){lostPoint+=(3+sameCount-5);}}} + for(var row=0;row=256){n-=255;} + return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);} + if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));} + this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]]; + + function _isSupportCanvas() { + return typeof CanvasRenderingContext2D != "undefined"; + } + + // android 2.x doesn't support Data-URI spec + function _getAndroid() { + var android = false; + var sAgent = navigator.userAgent; + + if (/android/i.test(sAgent)) { // android + android = true; + var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i); + + if (aMat && aMat[1]) { + android = parseFloat(aMat[1]); + } + } + + return android; + } + + var svgDrawer = (function() { + + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + + this.clear(); + + function makeSVG(tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) + if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]); + return el; + } + + var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight}); + svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); + _el.appendChild(svg); + + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"})); + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"})); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + if (oQRCode.isDark(row, col)) { + var child = makeSVG("use", {"x": String(col), "y": String(row)}); + child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template") + svg.appendChild(child); + } + } + } + }; + Drawing.prototype.clear = function () { + while (this._el.hasChildNodes()) + this._el.removeChild(this._el.lastChild); + }; + return Drawing; + })(); + + var useSVG = document.documentElement.tagName.toLowerCase() === "svg"; + + // Drawing in DOM by using Table tag + var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + var aHTML = ['
']; + + for (var row = 0; row < nCount; row++) { + aHTML.push(''); + + for (var col = 0; col < nCount; col++) { + aHTML.push(''); + } + + aHTML.push(''); + } + + aHTML.push('
'); + _el.innerHTML = aHTML.join(''); + + // Fix the margin values as real size. + var elTable = _el.childNodes[0]; + var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2; + var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2; + + if (nLeftMarginTable > 0 && nTopMarginTable > 0) { + elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px"; + } + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._el.innerHTML = ''; + }; + + return Drawing; + })() : (function () { // Drawing in Canvas + function _onMakeImage() { + this._elImage.src = this._elCanvas.toDataURL("image/png"); + this._elImage.style.display = "block"; + this._elCanvas.style.display = "none"; + } + + // Android 2.1 bug workaround + // http://code.google.com/p/android/issues/detail?id=5141 + if (this._android && this._android <= 2.1) { + var factor = 1 / window.devicePixelRatio; + var drawImage = CanvasRenderingContext2D.prototype.drawImage; + CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) { + if (("nodeName" in image) && /img/i.test(image.nodeName)) { + for (var i = arguments.length - 1; i >= 1; i--) { + arguments[i] = arguments[i] * factor; + } + } else if (typeof dw == "undefined") { + arguments[1] *= factor; + arguments[2] *= factor; + arguments[3] *= factor; + arguments[4] *= factor; + } + + drawImage.apply(this, arguments); + }; + } + + /** + * Check whether the user's browser supports Data URI or not + * + * @private + * @param {Function} fSuccess Occurs if it supports Data URI + * @param {Function} fFail Occurs if it doesn't support Data URI + */ + function _safeSetDataURI(fSuccess, fFail) { + var self = this; + self._fFail = fFail; + self._fSuccess = fSuccess; + + // Check it just once + if (self._bSupportDataURI === null) { + var el = document.createElement("img"); + var fOnError = function() { + self._bSupportDataURI = false; + + if (self._fFail) { + self._fFail.call(self); + } + }; + var fOnSuccess = function() { + self._bSupportDataURI = true; + + if (self._fSuccess) { + self._fSuccess.call(self); + } + }; + + el.onabort = fOnError; + el.onerror = fOnError; + el.onload = fOnSuccess; + el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data. + return; + } else if (self._bSupportDataURI === true && self._fSuccess) { + self._fSuccess.call(self); + } else if (self._bSupportDataURI === false && self._fFail) { + self._fFail.call(self); + } + }; + + /** + * Drawing QRCode by using canvas + * + * @constructor + * @param {HTMLElement} el + * @param {Object} htOption QRCode Options + */ + var Drawing = function (el, htOption) { + this._bIsPainted = false; + this._android = _getAndroid(); + + this._htOption = htOption; + this._elCanvas = document.createElement("canvas"); + this._elCanvas.width = htOption.width; + this._elCanvas.height = htOption.height; + el.appendChild(this._elCanvas); + this._el = el; + this._oContext = this._elCanvas.getContext("2d"); + this._bIsPainted = false; + this._elImage = document.createElement("img"); + this._elImage.alt = "Scan me!"; + this._elImage.style.display = "none"; + this._el.appendChild(this._elImage); + this._bSupportDataURI = null; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _elImage = this._elImage; + var _oContext = this._oContext; + var _htOption = this._htOption; + + var nCount = oQRCode.getModuleCount(); + var nWidth = _htOption.width / nCount; + var nHeight = _htOption.height / nCount; + var nRoundedWidth = Math.round(nWidth); + var nRoundedHeight = Math.round(nHeight); + + _elImage.style.display = "none"; + this.clear(); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + var bIsDark = oQRCode.isDark(row, col); + var nLeft = col * nWidth; + var nTop = row * nHeight; + _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.lineWidth = 1; + _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.fillRect(nLeft, nTop, nWidth, nHeight); + + // 안티 앨리어싱 방지 처리 + _oContext.strokeRect( + Math.floor(nLeft) + 0.5, + Math.floor(nTop) + 0.5, + nRoundedWidth, + nRoundedHeight + ); + + _oContext.strokeRect( + Math.ceil(nLeft) - 0.5, + Math.ceil(nTop) - 0.5, + nRoundedWidth, + nRoundedHeight + ); + } + } + + this._bIsPainted = true; + }; + + /** + * Make the image from Canvas if the browser supports Data URI. + */ + Drawing.prototype.makeImage = function () { + if (this._bIsPainted) { + _safeSetDataURI.call(this, _onMakeImage); + } + }; + + /** + * Return whether the QRCode is painted or not + * + * @return {Boolean} + */ + Drawing.prototype.isPainted = function () { + return this._bIsPainted; + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height); + this._bIsPainted = false; + }; + + /** + * @private + * @param {Number} nNumber + */ + Drawing.prototype.round = function (nNumber) { + if (!nNumber) { + return nNumber; + } + + return Math.floor(nNumber * 1000) / 1000; + }; + + return Drawing; + })(); + + /** + * Get the type by string length + * + * @private + * @param {String} sText + * @param {Number} nCorrectLevel + * @return {Number} type + */ + function _getTypeNumber(sText, nCorrectLevel) { + var nType = 1; + var length = _getUTF8Length(sText); + + for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { + var nLimit = 0; + + switch (nCorrectLevel) { + case QRErrorCorrectLevel.L : + nLimit = QRCodeLimitLength[i][0]; + break; + case QRErrorCorrectLevel.M : + nLimit = QRCodeLimitLength[i][1]; + break; + case QRErrorCorrectLevel.Q : + nLimit = QRCodeLimitLength[i][2]; + break; + case QRErrorCorrectLevel.H : + nLimit = QRCodeLimitLength[i][3]; + break; + } + + if (length <= nLimit) { + break; + } else { + nType++; + } + } + + if (nType > QRCodeLimitLength.length) { + throw new Error("Too long data"); + } + + return nType; + } + + function _getUTF8Length(sText) { + var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); + return replacedText.length + (replacedText.length != sText ? 3 : 0); + } + + /** + * @class QRCode + * @constructor + * @example + * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie"); + * + * @example + * var oQRCode = new QRCode("test", { + * text : "http://naver.com", + * width : 128, + * height : 128 + * }); + * + * oQRCode.clear(); // Clear the QRCode. + * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode. + * + * @param {HTMLElement|String} el target element or 'id' attribute of element. + * @param {Object|String} vOption + * @param {String} vOption.text QRCode link data + * @param {Number} [vOption.width=256] + * @param {Number} [vOption.height=256] + * @param {String} [vOption.colorDark="#000000"] + * @param {String} [vOption.colorLight="#ffffff"] + * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H] + */ + QRCode = function (el, vOption) { + this._htOption = { + width : 256, + height : 256, + typeNumber : 4, + colorDark : "#000000", + colorLight : "#ffffff", + correctLevel : QRErrorCorrectLevel.H + }; + + if (typeof vOption === 'string') { + vOption = { + text : vOption + }; + } + + // Overwrites options + if (vOption) { + for (var i in vOption) { + this._htOption[i] = vOption[i]; + } + } + + if (typeof el == "string") { + el = document.getElementById(el); + } + + if (this._htOption.useSVG) { + Drawing = svgDrawer; + } + + this._android = _getAndroid(); + this._el = el; + this._oQRCode = null; + this._oDrawing = new Drawing(this._el, this._htOption); + + if (this._htOption.text) { + this.makeCode(this._htOption.text); + } + }; + + /** + * Make the QRCode + * + * @param {String} sText link data + */ + QRCode.prototype.makeCode = function (sText) { + this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel); + this._oQRCode.addData(sText); + this._oQRCode.make(); + this._el.title = sText; + this._oDrawing.draw(this._oQRCode); + this.makeImage(); + }; + + /** + * Make the Image from Canvas element + * - It occurs automatically + * - Android below 3 doesn't support Data-URI spec. + * + * @private + */ + QRCode.prototype.makeImage = function () { + if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) { + this._oDrawing.makeImage(); + } + }; + + /** + * Clear the QRCode + */ + QRCode.prototype.clear = function () { + this._oDrawing.clear(); + }; + + /** + * @name QRCode.CorrectLevel + */ + QRCode.CorrectLevel = QRErrorCorrectLevel; +})(); diff --git a/app/assets/v2/js/pages/bounty_details2.js b/app/assets/v2/js/pages/bounty_details2.js index 4443643612d..6c5250fb5b0 100644 --- a/app/assets/v2/js/pages/bounty_details2.js +++ b/app/assets/v2/js/pages/bounty_details2.js @@ -78,7 +78,8 @@ if (document.getElementById('gc-bounty-detail')) { url: url, cb_address: cb_address, isOwner: false, - is_bounties_network: is_bounties_network + is_bounties_network: is_bounties_network, + inputAmount: undefined } }, mounted() { diff --git a/app/assets/v2/js/vue-components.js b/app/assets/v2/js/vue-components.js index 9899c15c445..a769d5b9c02 100644 --- a/app/assets/v2/js/vue-components.js +++ b/app/assets/v2/js/vue-components.js @@ -80,3 +80,47 @@ Vue.component('loading-screen', {
` }); + + +Vue.component('qrcode', { + props: [ 'string' ], + template: `
`, + data() { + return { + jqEl: null, + qrcode: null + }; + }, + mounted() { + let vm = this; + + vm.jqEl = $(this.$el); + vm.qrcode = new QRCode(vm.jqEl[0], vm.string); + return vm.qrcode + + // document.getElementsByClassName("qrcode")[0].innerHTML = qr.createImgTag(); + // var qrcode = new QRCode(document.getElementById("qrcode"), { + // text: "http://jindo.dev.naver.com/collie", + // width: 128, + // height: 128, + // colorDark : "#000000", + // colorLight : "#ffffff", + // correctLevel : QRCode.CorrectLevel.H + // }); + // qrcode.makeCode("http://naver.com") + }, + watch: { + string: function(string) { + let vm = this; + + return vm.qrcode.makeCode(string); + } + }, + computed: { + // normalizedSize: function () { + // console.log(vm.jqEl) + // return new QRCode(vm.jqEl, "http://jindo.dev.naver.com/collie"); + // // return this.size.trim().toLowerCase() + // } + } +}) diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html index 4d907e25969..8b15d513dc2 100644 --- a/app/dashboard/templates/bounty/details2.html +++ b/app/dashboard/templates/bounty/details2.html @@ -246,7 +246,7 @@
{% trans "SUBMISSIONS" %}
- +
@@ -255,6 +255,16 @@
{% trans "SUBMISSIONS" %}
[[ fulfillment.fulfiller_github_url ]] [[ fulfillment.fulfiller_metadata?.data?.payload?.fulfiller?.hoursWorked ]] hours + +
+ Payout contributor + Payout Xetc to [[ fulfillment.profile.handle ]] [[ fulfillment.fulfiller_address | truncateHash ]] + + + Scan the QR code above with your ETC Wallet to payout +
+
+
@@ -343,10 +353,9 @@
{% trans "ALL ACTIVITY" %}
- - + +