1$(document).ready(function(){ 2 3 // load the credit view data on page load so that we're ready to display the view as soon 4 // as they open the credit views 5 6 // removed for now since we 7 // getCreditViewDataFromServer(); 8}) 9 10// if the PWA or web app becomes focused and they're currently looking at the number of credits they have 11// then we update the number. This is pretty important if they're actively referring their friends, 12// we want them to see the number update without them having to dismiss the view and re-open it. 13ifvisible.on("focus", function () { 14 var creditsModalVisibleOnPWA = (window.isPWA && window.modalStack && window.modalStack.includes("modal#getPaidModal")); 15 var creditsModalVisibleOnWeb = (!window.isPWA && $("#getPaidModal").css('display') != 'none'); 16 if(creditsModalVisibleOnPWA || creditsModalVisibleOnWeb){ 17 var paymentAmountType = $("#getPaidModal").find(".payoutAmount").attr('class').replace("payoutAmount", "").trim(); 18 // just in case we change the classes in there, don't want to send bad data to the server 19 if(["monetary", "premium"].includes(paymentAmountType)){ 20 // getUsersCreditsFromServer(paymentAmountType); 21 } 22 } 23}); 24 25 26 27async function getCreditViewDataFromServer(){ 28 var data = {} 29 if(store.get('uuid')){ 30 data['uuid'] = store.get('uuid'); 31 } 32 $.ajax({ 33 url: "/shared/credits/getCreditViewData.php", 34 type: 'GET', 35 dataType: 'json', 36 data: data, 37 success: function(response){ 38 setCreditViewData(response); 39 } 40 }) 41} 42 43//get referral URL 44function generateReferralLink(subtype){ 45 var data = {type:"referral", subtype:subtype}; 46 let params = { 47 "uuid" : store.get("uuid"), 48 "data" : JSON.stringify(data) 49 }; 50 $.ajax({ 51 url: "/shared/link/generate.php", 52 type: 'POST', 53 dataType: 'json', 54 data: params, 55 success: function(response){ 56 if(response.error == true){ 57 return 58 } 59 //we don't want to continue the animation if the link has loaded successfully. 60 checkLinkLoadingAnimation() 61 setUserLink(response.url, data); 62 //note that this function also updates the modal. 63 $('#webShareLink').text(getUserLink(data)) 64 } 65 }); 66} 67 68async function getUsersCreditsFromServer(type){ 69 var params = {type:type,uuid:store.get('uuid')} 70 $.ajax({ 71 url: "/shared/credits/getUsersCredits.php", 72 type: 'GET', 73 dataType: 'json', 74 data: params, 75 success: function(response){ 76 77 if(response.error == true){ 78 // 79 }else{ 80 81 setUsersCredits(type, response.credits) 82 setUsersReferrals(type, response.referrals) 83 //note that this function also updates the modal. 84 displayAndFormatUsersCredits(type, response.credits, response.referrals) 85 86 }} 87 }); 88} 89 90function checkLinkLoadingAnimation(){ 91 if(window.linkLoadingAnimation){ 92 clearInterval(window.linkLoadingAnimation) 93 } 94} 95 96//GETTERS AND SETTERS 97//getters to be used to display existing values while calls to server are in transit 98//setters to be used by network call callbacks when retrieving link, credits from server. 99 100function setCreditViewData(data){ 101 store.set("creditViewData", data); 102} 103 104function getCreditViewData(){ 105 return store.get("creditViewData") 106} 107 108function setUsersCredits(type,credits){ 109 var data = store.get("usersCredits") != null ? store.get("usersCredits") : {} 110 data[type] = credits 111 store.set("usersCredits",data) 112} 113 114function setUsersReferrals(type,referrals){ 115 var data = store.get("usersReferrals") != null ? store.get("usersReferrals") : {} 116 data[type] = referrals 117 store.set("usersReferrals",data) 118} 119 120function getUsersCredits(type){ 121 if(store.get("usersCredits") != null && type in store.get("usersCredits")){ 122 return store.get("usersCredits")[type] 123 }else{ 124 return null; 125 } 126} 127function getUsersReferrals(type){ 128 if(store.get("usersReferrals") != null && type in store.get("usersReferrals")){ 129 return store.get("usersReferrals")[type] 130 }else{ 131 return null; 132 } 133} 134 135// also used in purchase.js when purchasing premium 136function markAsPurchasedOrRedeemedOnServer(purchasedOrRedeemed){ 137 138 var userData = store.get('userData') 139 var purchases = userData["premium"] 140 141 if (purchases == null) { 142 userData["premium"] = [] 143 } 144 145 var premiumObj = {} 146 premiumObj[window.registrationSemester] = purchasedOrRedeemed 147 userData["premium"].push(premiumObj) 148 149 150 // make explicit change to server userData 151 setUserData(uuid=store.get("uuid"), deviceID=null, token=null, school=null, userDataJ
151sonString=JSON.stringify(userData)) 152 store.set("userData", userData) 153 154 // make explicit log on apache 155 // note: currently deprecated as of new premium purchase because we no longer do free credits for now 156 // we'll be using an entirely different mechanism of credits if we re-implement it. 157 // endpoint has been deleted, just so you know 158 $.ajax({ 159 url: '/shared/premium/log.php', 160 type: 'POST', 161 dataType: 'text', 162 data: { 163 uuid: store.get('uuid'), 164 client: (!window.isPWA ? 'web' : 'android'), 165 school: retrieveSchoolParameter() , 166 type: purchasedOrRedeemed, 167 }, 168 success(response) { 169 // noice 170 } 171 }) 172} 173 174//Apply all of the required classes for css. 175function displayAndFormatUsersCredits(monetaryOrPremium, credits, referrals){ 176 177 //strip all of the formatting from the cash out button and payout amount. The function below should reapply as necessary. 178 $(".payoutAmount").removeClass("greaterThanThreshhold") 179 $(".cashOutBtn").removeClass("tenOrMore") 180 $("#cashingOutSpinner").hide(); 181 182 if(monetaryOrPremium == "monetary"){ 183 var threshold = getCreditViewData().monetary.threshold 184 }else if(monetaryOrPremium == "premium"){ 185 var threshold = getCreditViewData().premium.threshold 186 } 187 188 if(credits>=threshold){ 189 $(".payoutAmount").addClass("greaterThanThreshhold") 190 $(".cashOutBtn").addClass("tenOrMore") 191 } 192 193 if(monetaryOrPremium == "monetary"){ 194 $('.payoutAmount').text("$"+credits) 195 }else if(monetaryOrPremium == "premium"){ 196 $('.payoutAmount').text(credits) 197 } 198 199 //add a class so we can determine the context when cashout button pressed. 200 $('.payoutAmount').removeClass("monetary").removeClass("premium").addClass(monetaryOrPremium) 201 $('.referralSubtext').text(`You've referred ${referrals} friend${referrals != 1 ? 's' : ''} so far`); 202 203} 204 205 206 207 208 209//this is the function being called when clicking a banner 210async function formatAndRenderGetPaidModal(monetaryOrPremium){ 211 //pre-load the font set necessary for the copied to clipboard icon 212 $.ajax({ 213 url: "/all/libCSS/webfonts/fa-light-300.woff2", 214 type: 'GET', 215 cache:true, 216 success: function(){ 217 } 218 }); 219 //if we don't do this, successive opens of the modal will layer setInterval calls and make the animation irregular. 220 checkLinkLoadingAnimation() 221 //preset the credits and the share link from localstorage if possible. 222 if (getUsersCredits(monetaryOrPremium) !== null){ 223 displayAndFormatUsersCredits(monetaryOrPremium, getUsersCredits(monetaryOrPremium), getUsersReferrals(monetaryOrPremium)); 224 }else{ 225 // only if this is the first ever load, we just need to format the 0 basically 226 var countText = (monetaryOrPremium == "monetary") ? "$0" : "0"; 227 $(".payoutAmount").text(countText); 228 } 229 //do the same thing with the referraL LINK 230 let storedLink = getUserLink({type:"referral", subtype:monetaryOrPremium}) 231 if (storedLink !== null){ 232 $('#webShareLink').text(storedLink) 233 }else{ 234 //here we want to put in a moving ellipsis loading animation 235 //https://stackoverflow.com/questions/15382608/punctuation-loading-animation-javascript 236 var count = 0 237 $('#webShareLink').text("Generating") 238 window.linkLoadingAnimation = setInterval(function(){ 239 //we don't want to continue to do this if the link has loaded. 240 count++; 241 var dots = new Array(count % 4+1).join('.'); 242 $('#webShareLink').text("Generating" + dots) 243 },1000) 244 } 245 246 //update the share link and credits from the server. 247 generateReferralLink(monetaryOrPremium) 248 getUsersCreditsFromServer(monetaryOrPremium) 249 250 //format the modal according to what the server says is appropriate as of first page load 251 var info; 252 if(monetaryOrPremium == "monetary"){ 253 info = getCreditViewData().monetary; 254 }else if (monetaryOrPremium == "premium"){ 255 info = getCreditViewData().premium; 256 } 257 $('.cashOutBtn').text(info.actionButton); 258 $('.getPaidShareBtn').text(info.leftShareBtn); 259 $('.addToStoryBtn').text(info.rightShareBtn); 260 $('.getPaidRowText1').text(info.steps[0]); 261 $('.getPaidRowText2').text(info.steps[1]); 262 $('.getPaidRowText3').text(info.steps[2]); 263 $('.cashOutExplanation').text(info.subtext); 264 265 //Here we need to switch the href to open the instagram app rather than the web page. While we're here, let's also modify modal width for pwa. 266 //https://stackoverflow.com/questions/21295051/link-on-a-webpage-to-open-instagram-app 267 // note: clicking on the Instagram button will now also copy their referral link to their clipboard 268 if(window.isPWA){ 269 $('.instagramLink').attr({"href":"intent://instagram.com/_n/mainfeed/#Intent;package=com.instagram.android;scheme=https;end","target":"_blank"}) 270 }else{ 271 $('.instagramLink').attr({"href":"https://www.instagram.com","target":"_blank"}) 272 } 273 //now that all the formatting has been done, display the modal. 274 if(window.isPWA){ 275 renderSlidableModal('#getPaidModal',true) 276 }else{ 277 $('#getPaidModal').modal("show") 278 } 279} 280 281//CLICK LISTENERS 282$('.getPaidShareBtn').on("click",function(){ 283 284 //a way to get the monetary or premium context at the time of button click. 285 let monetaryOrPremium = $("#getPaidModal .payoutAmount").hasClass("monetary")? "monetary" : "premium" 286 let shareUrl = "https://" + getUserLink({type:"referral", subtype:monetaryOrPremium}) 287 //if we can use the native share dialogue 288 if (navigator.share) { 289 navigator.share({ 290 title: 'Coursicle', 291 text: 'This app notifies you when classes open up ' + shareUrl, 292 }) 293 }else if (navigator.userAgent && navigator.userAgent.indexOf("Mac") != -1) {
294 //if mac send an sms 295 window.open("sms:?&body=" + encodeURIComponent("This app notifies you when classes open up " + shareUrl)) 296 }else{ 297 //all other cases: send an email 298 window.open("mailto:?subject=Coursicle&body=" + encodeURIComponent("This app notifies you when classes open up "+ shareUrl)) 299 } 300}) 301 302$('.cashOutBtn').on('click',function(){ 303 304 //only continue if the cashOutBtn is blue 305 if(!$('.cashOutBtn').hasClass('tenOrMore')){ 306 alert($('.cashOutExplanation').text()); 307 return; 308 } 309 310 //take note of the current text so we can change the btn back after the network call is made 311 let currentBtnText = $('#getPaidModal .cashOutBtn').text() 312 //don't allow additional network calls while the text says checking. 313 if ($('.cashOutBtn').hasClass("redeeming")){ 314 return 315 } 316 317 //a way to get the monetary or premium context at the time of button click. 318 let monetaryOrPremium = $("#getPaidModal .payoutAmount").hasClass("monetary")? "monetary" : "premium" 319 320 if(monetaryOrPremium == "monetary"){ 321 var processingText = "Cashing out..."; 322 var completedText = "Cashed out"; 323 }else if(monetaryOrPremium == "premium"){ 324 var processingText = "Redeeming..."; 325 var completedText = "Redeemed"; 326 } 327 328 $('.cashOutBtn').text(processingText); 329 $("#cashingOutSpinner").show(); 330 $('.cashOutBtn').addClass("redeeming"); 331 $.ajax({ 332 url: "/shared/credits/redeem.php", 333 type: 'POST', 334 dataType: 'json', 335 data: {'uuid':store.get('uuid'),type:monetaryOrPremium}, 336 success: function(response){ 337 338 if(monetaryOrPremium == "premium"){ 339 // mark it as redeemed in their user data and log it 340 markAsPurchasedOrRedeemedOnServer("redeemed"); 341 } 342 343 // make sure it takes at least 2 seconds for the cashout 344 let btnTextReset = setTimeout(function(){ 345 $('.cashOutBtn').text(completedText) 346 $('.cashOutBtn').addClass("redeemed"); 347 if(response.error == false){ 348 displayAndFormatUsersCredits(monetaryOrPremium, getUsersCredits(monetaryOrPremium)-response.redeemed, getUsersReferrals(monetaryOrPremium)); 349 } 350 setTimeout(function(){ 351 $('.cashOutBtn').text(currentBtnText) 352 $('.cashOutBtn').removeClass("redeeming redeemed"); 353 // need to call it again here so that the next time they open it we have the updated data and it doesn't flash 354 getUsersCreditsFromServer(monetaryOrPremium); 355 if(monetaryOrPremium == "monetary"){ 356 $('#getPaidModal').modal("hide") 357 showBaseModal('#universalContactModal') 358 $("#universalContactModalTopNote").hide(); 359 $('#universalContactModalTextArea').val("Cashing out $" + response.redeemed + ". Code: " + btoa(response.redeemed) + " \n\nVenmo: [username]\nOR\nCash App: [username]") 360 } 361 },1000) 362 },2000); 363 } 364 }) 365}) 366 367$(document).on("click",".instagramLink",function(){ 368 var instructions = [ 369 "Create a new story", 370 "Tap the sticker icon at the top", 371 "Tap \"Link\"", 372 "Paste your link", 373 "Tap \"Done\"" 374 ]; 375 376 // if it's desktop, you specify it has to be on the phone 377 // and you don't have to wait as long to show the alert 378 var delay = 2000; 379 if(!window.isPWA){ 380 delay = 600; 381 instructions[0] = "Create a new story on your phone"; 382 } 383 384 var numberedInstructions = ""; 385 for (var i = 0; i < instructions.length; i++) { 386 numberedInstructions += (i + 1) + ". " + instructions[i] + "\n"; 387 } 388 389 // note: it seems like on the PWA, displaying this alert causes 390 // the URL bar to appear sometimes. Really weird. Not going to do this 391 // on the PWA for now and will just wait until we can create the story 392 // natively once we have the PWA and wrapper bicommunication channel established.
393 // update: it seems like we have this issue elsewhere though, anytime we use alert() 394 // so we should write our own alert and then use that everywhere in the app instead 395 // if(window.isPWA){return} 396 397 setTimeout(function(){ 398 alert("To add your link to your story:\n"+numberedInstructions); 399 }, delay); 400}); 401 402//copy user referral link to clipboard. 403$(document).on("click",".shareLinkBox, .instagramLink",function(){ 404 //prevent pressing the button multiple times and timeouts backing up 405 if($(".shareLinkBox").hasClass("alreadyCopied")){ 406 return 407 } 408 $(".shareLinkBox").addClass('alreadyCopied') 409 //copy to clipboard 410 let existingLink = $('#webShareLink').text() 411 navigator.clipboard.writeText("https://" + $('#webShareLink').text()) 412 413 $(".shareLinkText").text("Link copied to clipboard") 414 $(".copyBtnContainer").empty().html('<i class="fa-light fa-clipboard-check"></i>') 415 //after 2.5 seconds, reset the link container. 416 currentTimeout = setTimeout(function(){ 417 $('.shareLinkText').text(existingLink) 418 $(".copyBtnContainer").empty().html('<i class="fa fa-clone"></i>') 419 $(".shareLinkBox").removeClass('alreadyCopied') 420 },2500) 421})
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.