diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..cc43cce3 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,5 @@ +#!/bin/bash +pack +go build +scp notify.moe eduard@arn:~/beta/notify.moe.new +ssh eduard@arn 'cd beta; killall notify.moe; rm notify.moe; mv notify.moe.new notify.moe; ./notify.moe &' \ No newline at end of file diff --git a/main.go b/main.go index 5c17a583..23897f2d 100644 --- a/main.go +++ b/main.go @@ -10,8 +10,7 @@ import ( var app = aero.New() func main() { - // app.SetStyle(bundledCSS) - app.SetStyle("") + app.SetStyle(components.BundledCSS) scripts, _ := ioutil.ReadFile("temp/scripts.js") js := string(scripts) diff --git a/scripts/admin.ts b/scripts/admin.ts new file mode 100644 index 00000000..089a0404 --- /dev/null +++ b/scripts/admin.ts @@ -0,0 +1,12 @@ +document.addEventListener('keydown', e => { + // Alt + A = Staff info + if(e.keyCode === 65 && e.altKey) { + let staffInfo = $('staff-info') + + if(staffInfo.style.display !== 'block') { + staffInfo.style.display = 'block' + } else { + staffInfo.style.display = 'none' + } + } +}) \ No newline at end of file diff --git a/scripts/aqua.ts b/scripts/aqua.ts new file mode 100644 index 00000000..d57fd596 --- /dev/null +++ b/scripts/aqua.ts @@ -0,0 +1,21 @@ +function find(id: string) { + return document.getElementById(id) +} + +function get(url: string, body?: Object): Promise { + return new Promise(function(resolve, reject) { + resolve("") + }) +} + +function post(url: string, body?: Object): Promise { + return new Promise(function(resolve, reject) { + resolve("") + }) +} + +export { + find, + get, + post +} \ No newline at end of file diff --git a/scripts/avatars.ts b/scripts/avatars.ts new file mode 100644 index 00000000..0a7ef545 --- /dev/null +++ b/scripts/avatars.ts @@ -0,0 +1,31 @@ +$.updateAvatars = function() { + let images = document.querySelectorAll('.user-image') + + for(let i = 0; i < images.length; ++i) { + let img = images[i] + + if(img.naturalWidth === 0) { + img.onload = function() { + this.style.opacity = '1.0' + } + + img.onerror = function() { + this.src = '/images/elements/no-gravatar.svg' + this.style.opacity = '1.0' + } + } else { + img.style.opacity = '1.0' + } + } + + // Tooltips + // let links = document.querySelectorAll('.user') + // + // for(let i = 0; i < links.length; ++i) { + // let link = links[i] + // + // link.classList.add('tooltip') + // link.setAttribute('data-tooltip', link.title) + // link.title = '' + // } +} \ No newline at end of file diff --git a/scripts/init.ts b/scripts/init.ts new file mode 100644 index 00000000..5eebba08 --- /dev/null +++ b/scripts/init.ts @@ -0,0 +1,10 @@ +// Fix Facebook login hash in URL +if(window.location.hash && window.location.hash === '#_=_') { + window.history.pushState('', document.title, window.location.pathname) +} + +// Fade out loading animation +document.addEventListener('DOMContentLoaded', function(event) { + $.loadingAnimation.classList.add('fade-out') + $.updateAvatars() +}) \ No newline at end of file diff --git a/scripts/posts.ts b/scripts/posts.ts new file mode 100644 index 00000000..f5f8f539 --- /dev/null +++ b/scripts/posts.ts @@ -0,0 +1,46 @@ +import * as $ from './aqua' + +function like(type, id) { + $.post(`/api/${type}/like/` + id) + $.find('like-' + id).style.display = 'none' + $.find('unlike-' + id).style.display = 'inline-block' + + let likes = $.find('likes-' + id) + likes.innerHTML = (parseInt(likes.textContent) + 1).toString() +} + +function unlike(type, id) { + $.post(`/api/${type}/unlike/` + id) + $.find('like-' + id).style.display = 'inline-block' + $.find('unlike-' + id).style.display = 'none' + + let likes = $.find('likes-' + id) + likes.innerHTML = (parseInt(likes.textContent) - 1).toString() +} + +function edit(id) { + $.find('source-' + id).style.display = 'block' + $.find('save-' + id).style.display = 'block' + $.find('render-' + id).style.display = 'none' + $.find('toolbar-' + id).style.display = 'none' +} + +function cancelEdit(id) { + $.find('source-' + id).style.display = 'none' + $.find('save-' + id).style.display = 'none' + $.find('render-' + id).style.display = '' + $.find('toolbar-' + id).style.display = '' +} + +function saveEdit(type, id) { + let source = $.find('source-' + id) + let text = source.value + + $.post(`/api/${type}/edit/` + id, { + id, + text + }).then(response => { + $.find('render-' + id).innerHTML = response + cancelEdit(id) + }) +} \ No newline at end of file diff --git a/scripts/push-notifications.ts b/scripts/push-notifications.ts new file mode 100644 index 00000000..34c21240 --- /dev/null +++ b/scripts/push-notifications.ts @@ -0,0 +1,204 @@ +var isPushEnabled = false + +declare var ServiceWorkerRegistration: any +declare var Notification: any +interface Navigator { + serviceWorker: any +} + +function subscribeOnServer(subscription) { + console.log('Send subscription to server...') + console.log(subscription) + + $.post('/api/notifications/subscribe', subscription).then(function(response) { + console.log(response) + }) +} + +function unsubscribeOnServer(subscription) { + console.log('Send unsubscription to server...') + console.log(subscription) + + $.post('/api/notifications/unsubscribe', subscription).then(function(response) { + console.log(response) + }) +} + +function sendTestNotification() { + console.log('Sending test notification...') + + $.get('/api/notifications/test').then(function(response) { + // ... + }) +} + +function subscribe() { + // Disable the button so it can't be changed while + // we process the permission request + let pushButton = document.querySelector('.push-button') + + if(pushButton) + pushButton.disabled = true + + navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) { + serviceWorkerRegistration.pushManager.subscribe({ + userVisibleOnly: true + // applicationServerKey: new TextEncoder('binary').encode('BLxjquZGLvRnYGkV_xlkuAIilZzHJLDdGUZAmvq4pqev5uQHBmYRxJqfbFQFQn2kYfe5SRBwiNfuiakHn-3KR_k') + }).then(function(subscription) { + // The subscription was successful + isPushEnabled = true + if(pushButton) { + pushButton.textContent = 'Disable Notifications' + pushButton.disabled = false + } + + let testButton = document.querySelector('.test-notification') + + if(testButton) + testButton.disabled = false + + // TODO: Send the subscription.endpoint to your server + // and save it to send a push message at a later date + return subscribeOnServer(subscription) + }).catch(function(e) { + if(Notification.permission === 'denied') { + // The user denied the notification permission which + // means we failed to subscribe and the user will need + // to manually change the notification permission to + // subscribe to push messages + console.warn('Permission for Notifications was denied') + if(pushButton) + pushButton.disabled = true + } else { + // A problem occurred with the subscription common reasons + // include network errors, and lacking gcm_sender_id and/or + // gcm_user_visible_only in the manifest. + console.error('Unable to subscribe to push.', e) + if(pushButton) { + pushButton.disabled = false + pushButton.textContent = 'Enable Notifications' + } + } + }) + }) +} + +function unsubscribe() { + let pushButton = document.querySelector('.push-button') + + if(pushButton) + pushButton.disabled = true + + let testButton = document.querySelector('.test-notification') + + if(testButton) + testButton.disabled = true + + navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) { + // To unsubscribe from push messaging, you need get the + // subscription object, which you can call unsubscribe() on. + serviceWorkerRegistration.pushManager.getSubscription().then(function(pushSubscription) { + // Check we have a subscription to unsubscribe + if(!pushSubscription) { + // No subscription object, so set the state + // to allow the user to subscribe to push + isPushEnabled = false + if(pushButton) { + pushButton.disabled = false + pushButton.textContent = 'Enable Notifications' + } + return + } + + unsubscribeOnServer(pushSubscription) + + // We have a subscription, so call unsubscribe on it + pushSubscription.unsubscribe().then(function(successful) { + if(pushButton) { + pushButton.disabled = false + pushButton.textContent = 'Enable Notifications' + } + isPushEnabled = false + }).catch(function(e) { + // We failed to unsubscribe, this can lead to + // an unusual state, so may be best to remove + // the users data from your data store and + // inform the user that you have done so + + console.log('Unsubscription error: ', e) + if(pushButton) { + pushButton.disabled = false + pushButton.textContent = 'Enable Notifications' + } + }) + }).catch(function(e) { + console.error('Error thrown while unsubscribing from push messaging.', e) + }) + }) +} + +// Once the service worker is registered set the initial state +function initialiseState(registration) { + console.log('Initialise state...') + console.log('Scope:', registration.scope) + + // Are Notifications supported in the service worker? + if(!('showNotification' in ServiceWorkerRegistration.prototype)) { + console.warn('Notifications aren\'t supported.') + return + } + + // Check the current Notification permission. + // If its denied, it's a permanent block until the + // user changes the permission + if(Notification.permission === 'denied') { + console.warn('The user has blocked notifications.') + return + } + + // Check if push messaging is supported + if(!('PushManager' in window)) { + console.warn('Push messaging isn\'t supported.') + return + } + + // We need the service worker registration to check for a subscription + navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) { + console.log('Get subscription...') + + // Do we already have a push message subscription? + serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription) { + console.log('Enable Push UI...') + + // Enable any UI which subscribes / unsubscribes from + // push messages. + let pushButton = document.querySelector('.push-button') + + if(pushButton) + pushButton.disabled = false + + if(!subscription) { + // We aren't subscribed to push, so set UI + // to allow the user to enable push + return + } + + // Keep your server in sync with the latest subscriptionId + subscribeOnServer(subscription) + + // Set your UI to show they have subscribed for + // push messages + if(pushButton) + pushButton.textContent = 'Disable Notifications' + isPushEnabled = true + + // Enable test notifications + let testButton = document.querySelector('.test-notification') + + if(testButton) + testButton.disabled = false + }).catch(function(err) { + console.warn('Error during getSubscription()', err) + }) + }) +} \ No newline at end of file diff --git a/scripts/save-on-change.ts b/scripts/save-on-change.ts new file mode 100644 index 00000000..5b0cf822 --- /dev/null +++ b/scripts/save-on-change.ts @@ -0,0 +1,61 @@ +function makeSaveable(apiEndpoint: string, postSaveCallback?: (string, any) => void) { + $.save = function(e) { + var item = e.target; + + if($.saving) + return; + + $.saving = true; + + var key = item.id; + var value = item.value ? item.value : ''; + var old = item.dataset.old ? item.dataset.old : ''; + + item.classList.add('saving'); + $.content.style.cursor = 'wait'; + + $.post(apiEndpoint, { + function: 'save', + key: key, + value: value, + old: old + }).then(function(response) { + if(postSaveCallback) + postSaveCallback(key, response); + }).catch(function(error) { + console.error(error); + }).then(function() { + $.get('/_' + location.pathname).then(function(newPageCode) { + var focusedElementId = document.activeElement.id; + var focusedElementValue = ( document.activeElement).value; + + $.setContent(newPageCode); + + // Re-focus previously selected element + if(focusedElementId) { + var focusedElement = $(focusedElementId); + + if(focusedElement) { + focusedElement.value = focusedElementValue; + + if(focusedElement.select) + focusedElement.select(); + else if(focusedElement.focus) + focusedElement.focus(); + } + } + + $.content.style.cursor = 'auto'; + $.saving = false; + }); + }); + }; + + var myNodeList = document.querySelectorAll('.save-on-change'); + + for(var i = 0; i < myNodeList.length; ++i) { + var element = myNodeList[i]; + element.onchange = $.save; + element.dataset['old'] = element.value; + } +} \ No newline at end of file diff --git a/templates/anime/anime.pixy b/templates/anime/anime.pixy index e6b0e6e8..1e9fdc16 100644 --- a/templates/anime/anime.pixy +++ b/templates/anime/anime.pixy @@ -17,7 +17,7 @@ component Anime(anime *arn.Anime) a.second-title(href="http://jisho.org/search/" + anime.Title.Japanese, target="_blank", title="Look up reading on jisho.org", rel="nofollow")= anime.Title.Japanese //- h3.anime-header.anime-summary-header Summary - p.anime-summary= anime.Description + p.anime-summary= arn.FixAnimeDescription(anime.Description) if anime.YoutubeID != "" h3.anime-header Video @@ -42,7 +42,7 @@ component Anime(anime *arn.Anime) each genre in anime.Genres if genre != "" a.light-button.ajax(href="/genres/" + arn.FixGenre(genre)) - Icon(arn.GenreIcons[genre]) + Icon(arn.GetGenreIcon(genre)) span= genre if len(anime.Studios) > 0 diff --git a/templates/genres/genres.pixy b/templates/genres/genres.pixy index 7d7453bb..d4615d2e 100644 --- a/templates/genres/genres.pixy +++ b/templates/genres/genres.pixy @@ -4,7 +4,7 @@ component GenreOverview div each genre in arn.Genres a.light-button.ajax(href="/genres/" + arn.FixGenre(genre)) - Icon(arn.GenreIcons[genre]) + Icon(arn.GetGenreIcon(genre)) span= genre component AnimeInGenre(genre string, animeList []*arn.Anime)