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

Implement Trust Bonus Twitter Verification #7519

Merged
merged 3 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
dashboard.views.profile_tax_settings,
name='profile_set_tax_settings'
),
url(r'^api/v0.1/profile/(?P<handle>.*)/verify_user_twitter', dashboard.views.verify_user_twitter, name='verify_user_twitter'),
url(r'^api/v0.1/profile/(?P<handle>.*)', dashboard.views.profile_details, name='profile_details'),
url(r'^api/v0.1/user_card/(?P<handle>.*)', dashboard.views.user_card, name='user_card'),
url(r'^api/v0.1/banners', dashboard.views.load_banners, name='load_banners'),
Expand Down
152 changes: 150 additions & 2 deletions app/assets/v2/js/pages/profile-trust.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,154 @@ let show_brightid_verify_modal = function(brightid_uuid) {
$('#verify_brightid_modal').bootstrapModal('show');
};

Vue.component('twitter-verify-modal', {
delimiters: [ '[[', ']]' ],
data: function() {
return {
showValidation: false,
validationStep: 'send-tweet',
tweetText: '',
twitterHandle: '',
validationError: ''
};
},
computed: {
encodedTweetText: function() {
return encodeURIComponent(this.tweetText);
},
tweetIntentURL: function() {
return `https://twitter.com/intent/tweet?text=${this.encodedTweetText}`;
}
},
mounted: function() {
this.tweetText = verifyTweetText; // Global from tab_trust.html <script> tag

$(document).on('click', '#verify-twitter-link', function(event) {
event.preventDefault();
this.showValidation = true;
}.bind(this));
},
template: `<b-modal id="twitter-modal" @hide="dismissVerification()" :visible="showValidation" center hide-header hide-footer>
<template v-slot:default="{ hide }">
<div class="mx-5 mt-5 mb-4 text-center">
<div class="mb-3">
<h1 class="font-bigger-4 font-weight-bold">Verify your Twitter account</h1>
</div>
<div v-if="validationStep === 'send-tweet'">
<p class="mb-4 font-subheader text-left">
We want to verify your Twitter account. To do so, you must first send a standardized
Tweet from your account, then we'll validate it's there.
</p>
<p class="mb-4 font-subheader text-left">
The Tweet should say:
</p>
<p class="mb-4 font-subheader text-left">
<em>[[tweetText]]</em>
</p>
<div class="mt-2 mb-2">
<a :href="tweetIntentURL" @click="clickedSendTweet" role="button" style="font-size: 1.3em" class="button button--primary mb-2" target="_blank">
Send Tweet
</a>
</div>
<a href="" @click="clickedAlreadySent">
I have already Tweeted this
</a>
</div>
<div v-if="validationStep === 'validate-tweet' || validationStep == 'perform-validation'">
<p class="mb-4">
Now we'll validate that you've sent the tweet. Enter your Twitter handle and press validate.
</p>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text form-control" id="basic-addon1">@</span>
</div>
<input type="text" class="form-control" placeholder="handle" aria-label="handle" aria-describedby="basic-addon1" required maxlength="15" v-model="twitterHandle">
</div>
<div v-if="validationError !== ''" style="color: red">
<small>[[validationError]]</small>
</div>
<b-button @click="clickedValidate" :disabled="validationStep === 'perform-validation'" class="btn-gc-blue mt-3 mb-2" size="lg">
<b-spinner v-if="validationStep === 'perform-validation'" type="grow"></b-spinner>
Validate
</b-button>
<br />
<a href="" v-if="validationError !== ''" @click="clickedGoBack">
Go Back
</a>
</div>
<div v-if="validationStep === 'validation-complete'">
Your Twitter verification was successful. Thank you for helping make Gitcoin more sybil resistant!
<a href="" class="btn btn-gc-blue px-5 mt-3 mb-2 mx-2" role="button" style="font-size: 1.3em">Done</a>
</div>
</div>
</template>
</b-modal>`,
methods: {
dismissVerification() {
this.showValidation = false;
},
clickedSendTweet(event) {
this.validationStep = 'validate-tweet';
},
clickedAlreadySent(event) {
event.preventDefault();
this.validationStep = 'validate-tweet';
},
clickedGoBack(event) {
event.preventDefault();
this.validationStep = 'send-tweet';
this.validationError = '';
},
clickedValidate(event) {
event.preventDefault();

this.twitterHandle = this.twitterHandle.trim();

// Strip leading @ if user includes it
if (this.twitterHandle.startsWith('@')) {
this.twitterHandle = this.twitterHandle.split('@')[1];
}

// Validate handle is 15 word characters
const isValidHandle = null !== this.twitterHandle.match(/^(\w){1,15}$/);

if (!isValidHandle) {
this.validationError = 'Please enter a valid Twitter handle';
return;
}

// Reset after a prior error
this.validationError = '';

this.validationStep = 'perform-validation';

this.verifyTwitter();
},
verifyTwitter() {
const csrfmiddlewaretoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const payload = JSON.stringify({
'twitter_handle': this.twitterHandle
});
const headers = {'X-CSRFToken': csrfmiddlewaretoken};

const verificationRequest = fetchData(`/api/v0.1/profile/${trustHandle}/verify_user_twitter`, 'POST', payload, headers);

$.when(verificationRequest).then(response => {
if (response.ok) {
this.validationStep = 'validation-complete';
} else {
this.validationError = response.msg;
this.validationStep = 'validate-tweet';
}

}).catch((_error) => {
this.validationError = 'There was an error; please try again later';
this.validationStep = 'validate-tweet';
});
}
}
});

// TODO: This component consists primarily of code taken from the SMS verification flow in the cart.
// This approach is not DRY, and after Grants Round 7 completes, the cart should be refactored to include
// this as a shared component, rather than duplicating the code.
Expand Down Expand Up @@ -362,11 +510,11 @@ $(document).ready(function() {
});
});

if (document.getElementById('gc-sms-modal')) {
if (document.getElementById('gc-trust-verify-modal')) {

const smsVerificationApp = new Vue({
delimiters: [ '[[', ']]' ],
el: '#gc-sms-modal',
el: '#gc-trust-verify-modal',
data: { }
});
}
20 changes: 20 additions & 0 deletions app/dashboard/migrations/0149_add_twitter_verify_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.db import migrations, models

class Migration(migrations.Migration):

dependencies = [
('dashboard', '0148_add_brightid_status'),
]

operations = [
migrations.AddField(
model_name='profile',
name='is_twitter_verified',
field=models.BooleanField(default=False)
),
migrations.AddField(
model_name='profile',
name='twitter_handle',
field=models.CharField(blank=True, max_length=15)
),
]
2 changes: 2 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2871,6 +2871,8 @@ class Profile(SuperModel):
objects_full = ProfileQuerySet.as_manager()
brightid_uuid=models.UUIDField(default=uuid.uuid4, unique=True)
is_brightid_verified=models.BooleanField(default=False)
is_twitter_verified=models.BooleanField(default=False)
twitter_handle=models.CharField(blank=True, max_length=15)
thelostone-mc marked this conversation as resolved.
Show resolved Hide resolved

@property
def is_blocked(self):
Expand Down
31 changes: 16 additions & 15 deletions app/dashboard/templates/profiles/tab_trust.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

{% if not profile.is_org and is_my_profile %}
<div id="gc-trust-bonus-tab">
<div id="gc-sms-modal">
<sms-verify-modal></sms-verify-modald>
<div id="gc-trust-verify-modal">
<sms-verify-modal></sms-verify-modal>
<twitter-verify-modal></twitter-verify-modal>
</div>
<h3 class="py-3 font-weight-bold">Trust Bonus</h3>
The higher the Trust
Expand All @@ -14,7 +15,6 @@ <h3 class="py-3 font-weight-bold">Trust Bonus</h3>
<br>
To increase your Trust Bonus, here are some steps you can take:
<div class="container mt-4">

<h5>Active Now</h5>
<!-- SMS ROW -->
<div class="row mb-4">
Expand Down Expand Up @@ -43,7 +43,7 @@ <h5>Active Now</h5>
{% if is_sms_verified %}
<span style="color:limegreen"><i class="fas fa-check"></i> Verified</span>
{% else %}
<a href="/grants/cart?verify=true" role="button" id="verify-sms-link" class="button button--primary text-nowrap" target="_blank">Verify</a>
<a href="" role="button" id="verify-sms-link" class="button button--primary text-nowrap">Verify</a>
{% endif %}
</div>
</div>
Expand Down Expand Up @@ -106,9 +106,6 @@ <h5>Active Now</h5>
{% endif %}
</div>

<h5>Coming Soon ™️</h5>
<a href="https://twitter.com/owocki/status/1304422182043303937" target="_blank">To view our full roadmap click here</a>

<!-- TWITTER ROW -->
<div class="row mt-2 mb-4">
<div class="col-12 col-md-1 mx-auto text-center pt-1">
Expand All @@ -126,22 +123,24 @@ <h5>Coming Soon ™️</h5>
</div>
<div class="col-6 col-md-2 text-center">
<div class="font-weight-bold">
+?%
+5%
</div>
<div style="color:grey">
<small>Grants CLR Match</small>
</div>
</div>
<div class="col-6 col-md-2 text-center">
<div>
🚧
</div>
<div style="color:grey">
<small>Coming Soon</small>
</div>
<div class="col-6 col-md-2">
{% if is_twitter_verified %}
<span style="color:limegreen"><i class="fas fa-check"></i> Verified</span>
{% else %}
<a href="" role="button" id="verify-twitter-link" class="button button--primary text-nowrap">Verify</a>
{% endif %}
</div>
</div>

<h5>Coming Soon ™️</h5>
<a href="https://twitter.com/owocki/status/1304422182043303937" target="_blank">To view our full roadmap click here</a>

{% include "profiles/trust_soon_row.html" with service="Activity on Gitcoin" %}
{% include "profiles/trust_soon_row.html" with service="Idena Network" %}
{% include "profiles/trust_soon_row.html" with service="POAP" %}
Expand All @@ -158,5 +157,7 @@ <h5>Coming Soon ™️</h5>
{{upcoming_calls | json_script:"calendarData" }}
<script>
const calendarData = JSON.parse(document.getElementById('calendarData').textContent);
const trustHandle = '{{profile.handle}}'.toLowerCase();
const verifyTweetText = '{{verify_tweet_text}}';
</script>
{% endif %}
Loading