From 364f1d4c3820a8513a43f6d80d5e69f9bd28dd79 Mon Sep 17 00:00:00 2001 From: Janis Hutz Date: Sat, 16 Sep 2023 11:43:42 +0200 Subject: [PATCH] android app progress + polls plugin --- src/apps/Android/app/build.gradle.kts | 1 + .../Android/app/src/main/AndroidManifest.xml | 4 +- .../com/janishutz/libreevent/MainActivity.kt | 10 ++ .../janishutz/libreevent/ScannerActivity.kt | 65 +++++++++- .../src/main/res/layout/activity_scanner.xml | 17 ++- .../backend/plugins/others/poll/css/popup.css | 47 ++++++++ .../backend/plugins/others/poll/css/style.css | 57 +++++++++ .../plugins/others/poll/data/voting.json | 1 + .../others/poll/data/votingSettings.json | 1 + .../plugins/others/poll/html/settings.html | 108 +++++++++++++++++ .../plugins/others/poll/html/voting.html | 114 ++++++++++++++++++ .../plugins/others/poll/js/settings.js | 63 ++++++++++ .../backend/plugins/others/poll/js/voting.js | 91 ++++++++++++++ .../backend/plugins/others/poll/plugin.json | 4 +- .../backend/plugins/others/poll/pollRoutes.js | 95 ++++++++++++++- 15 files changed, 669 insertions(+), 9 deletions(-) create mode 100644 src/server/backend/plugins/others/poll/css/popup.css create mode 100644 src/server/backend/plugins/others/poll/css/style.css create mode 100644 src/server/backend/plugins/others/poll/data/voting.json create mode 100644 src/server/backend/plugins/others/poll/data/votingSettings.json create mode 100644 src/server/backend/plugins/others/poll/html/settings.html create mode 100644 src/server/backend/plugins/others/poll/html/voting.html create mode 100644 src/server/backend/plugins/others/poll/js/settings.js create mode 100644 src/server/backend/plugins/others/poll/js/voting.js diff --git a/src/apps/Android/app/build.gradle.kts b/src/apps/Android/app/build.gradle.kts index 90d7015..6556df0 100644 --- a/src/apps/Android/app/build.gradle.kts +++ b/src/apps/Android/app/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.8.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("com.journeyapps:zxing-android-embedded:4.2.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/src/apps/Android/app/src/main/AndroidManifest.xml b/src/apps/Android/app/src/main/AndroidManifest.xml index b1f7f92..14e8f78 100644 --- a/src/apps/Android/app/src/main/AndroidManifest.xml +++ b/src/apps/Android/app/src/main/AndroidManifest.xml @@ -22,5 +22,7 @@ - + + + \ No newline at end of file diff --git a/src/apps/Android/app/src/main/java/com/janishutz/libreevent/MainActivity.kt b/src/apps/Android/app/src/main/java/com/janishutz/libreevent/MainActivity.kt index d351806..2639cd4 100644 --- a/src/apps/Android/app/src/main/java/com/janishutz/libreevent/MainActivity.kt +++ b/src/apps/Android/app/src/main/java/com/janishutz/libreevent/MainActivity.kt @@ -1,5 +1,6 @@ package com.janishutz.libreevent +import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button @@ -9,5 +10,14 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + val loginButton = findViewById + +
+

{{ poll.display }}

+

{{ poll.comment }}

+ +
+ + + + + + + \ No newline at end of file diff --git a/src/server/backend/plugins/others/poll/html/voting.html b/src/server/backend/plugins/others/poll/html/voting.html new file mode 100644 index 0000000..72e63ff --- /dev/null +++ b/src/server/backend/plugins/others/poll/html/voting.html @@ -0,0 +1,114 @@ + + + + + + + + Polls :: libreevent-plugin + + + + + + +
+
+

Voting on {{ votingDetails.display ?? 'untitled' }}

+

{{ votingDetails.description }}

+
+ + +
+
+

{{ entry.title }}

+

{{ entry.comment }}

+
+ arrow_upward +

{{ entry.count ?? 0 }}

+ arrow_downward +
+
+ +
+
+

This poll does not exist!

+
+
+ + + + + \ No newline at end of file diff --git a/src/server/backend/plugins/others/poll/js/settings.js b/src/server/backend/plugins/others/poll/js/settings.js new file mode 100644 index 0000000..3ea2220 --- /dev/null +++ b/src/server/backend/plugins/others/poll/js/settings.js @@ -0,0 +1,63 @@ +const { createApp } = Vue; + +createApp( { + data() { + return { + polls: {}, + newPoll: {}, + operation: 'Add new', + }; + }, + methods: { + getData() { + fetch( '/admin/plugins/polls/getData' ).then( response => { + response.json().then( data => { + this.polls = data; + this.newPoll = {}; + } ); + } ); + }, + save() { + if ( this.newPoll.comment && this.newPoll.display && this.newPoll.id ) { + this.polls[ this.newPoll.id ] = this.newPoll; + let fetchOptions = { + method: 'post', + body: JSON.stringify( this.polls ), + headers: { + 'Content-Type': 'application/json', + 'charset': 'utf-8' + }, + }; + fetch( '/admin/plugins/polls/save', fetchOptions ).then( response => { + if ( response.status !== 200 ) { + alert( 'there was an error updating' ); + } + } ); + this.closePopup(); + this.getData(); + } else { + alert( 'Not all required fields are filled out!' ); + } + }, + closePopup() { + $( '#popup' ).fadeOut( 500 ); + $( 'body' ).removeClass( 'menuOpen' ); + this.getData(); + }, + addPoll () { + this.newPoll = { 'allowAdding': true }; + this.operation = 'Add new'; + $( '#popup' ).fadeIn( 500 ); + $( 'body' ).addClass( 'menuOpen' ); + }, + editPoll ( pollID ) { + this.operation = 'Edit'; + this.newPoll = this.polls[ pollID ]; + $( '#popup' ).fadeIn( 500 ); + $( 'body' ).addClass( 'menuOpen' ); + } + }, + mounted() { + this.getData(); + } +} ).mount( '#app' ); diff --git a/src/server/backend/plugins/others/poll/js/voting.js b/src/server/backend/plugins/others/poll/js/voting.js new file mode 100644 index 0000000..65b5081 --- /dev/null +++ b/src/server/backend/plugins/others/poll/js/voting.js @@ -0,0 +1,91 @@ +const { createApp } = Vue; + +createApp( { + data() { + return { + entries: {}, + newSuggestion: {}, + votingDetails: {}, + votedOn: {}, + }; + }, + methods: { + getData() { + fetch( '/polls/get/' + location.pathname.substring( 7 ) ).then( response => { + response.json().then( data => { + this.entries = data; + } ); + } ); + fetch( '/polls/getDetails/' + location.pathname.substring( 7 ) ).then( response => { + response.json().then( data => { + this.votingDetails = data; + } ); + } ); + this.votedOn = JSON.parse( localStorage.getItem( 'itemsVotedOn' ) ?? '{}' ); + }, + save() { + if ( this.newSuggestion.comment && this.newSuggestion.title ) { + let fetchOptions = { + method: 'post', + body: JSON.stringify( this.newSuggestion ), + headers: { + 'Content-Type': 'application/json', + 'charset': 'utf-8' + }, + }; + fetch( '/polls/add/' + location.pathname.substring( 7 ), fetchOptions ).then( response => { + if ( response.status !== 200 ) { + alert( 'there was an error updating' ); + } + } ); + this.closePopup(); + this.getData(); + } else { + alert( 'Not all required fields are filled out!' ); + } + }, + vote( type, suggestionID ) { + let voteType = type; + let didDeactivate = false; + if ( this.votedOn[ suggestionID ] === type ) { + didDeactivate = true; + if ( type === 'up' ) { + voteType = 'down'; + } else { + voteType = 'up'; + } + } else if ( this.votedOn[ suggestionID ] ) { + return; + } + let fetchOptions = { + method: 'post', + body: JSON.stringify( { 'voteType': voteType, 'id': suggestionID } ), + headers: { + 'Content-Type': 'application/json', + 'charset': 'utf-8' + }, + }; + fetch( '/polls/vote/' + location.pathname.substring( 7 ), fetchOptions ).then( response => { + if ( response.status !== 200 ) { + alert( 'there was an error updating' ); + } else { + this.votedOn[ suggestionID ] = didDeactivate ? undefined : voteType; + localStorage.setItem( 'itemsVotedOn', JSON.stringify( this.votedOn ) ); + this.getData(); + } + } ); + }, + closePopup() { + $( '#popup' ).fadeOut( 500 ); + $( 'body' ).removeClass( 'menuOpen' ); + this.getData(); + }, + addSuggestion () { + $( '#popup' ).fadeIn( 500 ); + $( 'body' ).addClass( 'menuOpen' ); + } + }, + mounted() { + this.getData(); + } +} ).mount( '#app' ); diff --git a/src/server/backend/plugins/others/poll/plugin.json b/src/server/backend/plugins/others/poll/plugin.json index d3d9ba5..7cb40a0 100644 --- a/src/server/backend/plugins/others/poll/plugin.json +++ b/src/server/backend/plugins/others/poll/plugin.json @@ -6,8 +6,8 @@ "pluginWebsite": "https://libreevent.janishutz.com/plugins/polls", "pluginDocs": "https://libreevent.janishutz.com/docs/plugins/polls", "gitURL": "https://github.com/simplePCBuilding/libreevent/tree/master/src/server/backend/plugins/others/poll", - "settingsURL": "/admin/plugins/polls/settings", - "mainPluginURL": "/polls", + "settingsURL": "/admin/plugins/polls", + "mainPluginURL": "/voting", "logo": "", "version": "1.0.0" } \ No newline at end of file diff --git a/src/server/backend/plugins/others/poll/pollRoutes.js b/src/server/backend/plugins/others/poll/pollRoutes.js index d031565..57fe156 100644 --- a/src/server/backend/plugins/others/poll/pollRoutes.js +++ b/src/server/backend/plugins/others/poll/pollRoutes.js @@ -7,6 +7,99 @@ * */ -module.exports = ( app, settings ) => { +const path = require( 'path' ); +const fs = require( 'fs' ); +const bodyParser = require( 'body-parser' ); +module.exports = ( app ) => { + app.get( '/polls/:vote', ( req, res ) => { + res.sendFile( path.join( __dirname + '/html/voting.html' ) ); + } ); + + app.get( '/polls/css/:file', ( req, res ) => { + res.sendFile( path.join( __dirname + '/css/' + req.params.file ) ); + } ); + + app.get( '/polls/js/:file', ( req, res ) => { + res.sendFile( path.join( __dirname + '/js/' + req.params.file ) ); + } ); + + app.get( '/polls/getDetails/:vote', ( req, res ) => { + fs.readFile( path.join( __dirname + '/data/votingSettings.json' ), ( error, filedata ) => { + res.send( JSON.parse( filedata )[ req.params.vote ] ?? {} ); + } ); + } ); + + app.get( '/polls/get/:vote', ( req, res ) => { + fs.readFile( path.join( __dirname + '/data/voting.json' ), ( error, filedata ) => { + res.send( JSON.parse( filedata )[ req.params.vote ] ?? {} ); + } ); + } ); + + app.post( '/polls/vote/:vote/', bodyParser.json(), ( req, res ) => { + // up / down-voting + fs.readFile( path.join( __dirname + '/data/voting.json' ), ( error, filedata ) => { + let json = JSON.parse( filedata ); + if ( json[ req.params.vote ] ) { + if ( req.body.voteType === 'up' ) { + json[ req.params.vote ][ req.body.id ].count += 1; + } else if ( req.body.voteType === 'down' ) { + json[ req.params.vote ][ req.body.id ].count -= 1; + } + fs.writeFile( path.join( __dirname + '/data/voting.json' ), JSON.stringify( json ), ( err ) => { + if ( err ) res.status( 500 ).send( 'ERR_VOTING' ); + res.send( 'ok' ); + } ); + } else { + res.status( 404 ).send( 'ok' ); + } + } ); + } ); + + app.post( '/polls/add/:vote', bodyParser.json(), ( req, res ) => { + let data = req.body; + if ( data.title && data.comment ) { + fs.readFile( path.join( __dirname + '/data/voting.json' ), ( error, filedata ) => { + let file = JSON.parse( filedata ); + if ( !file[ req.params.vote ] ) { + file[ req.params.vote ] = {}; + } + const id = parseInt( Object.keys( file[ req.params.vote ] )[ Object.keys( file[ req.params.vote ] ).length - 1 ] ?? 0 ) + 1; + file[ req.params.vote ][ id ] = data; + file[ req.params.vote ][ id ][ 'id' ] = id; + file[ req.params.vote ][ id ][ 'count' ] = 1; + fs.writeFile( path.join( __dirname + '/data/voting.json' ), JSON.stringify( file ), ( error ) => { + if ( error ) console.error( 'failed to write data', file ); + res.send( 'ok' ); + } ); + } ); + } else { + res.status( 400 ).send( 'incomplete' ); + } + } ); + + app.get( '/admin/plugins/polls', ( req, res ) => { + if ( req.session.loggedInAdmin ) { + res.sendFile( path.join( __dirname + '/html/settings.html' ) ); + } else { + res.status( 403 ).send( 'unauthorized' ); + } + } ); + + app.get( '/admin/plugins/polls/getData', ( req, res ) => { + if ( req.session.loggedInAdmin ) { + res.sendFile( path.join( __dirname + '/data/votingSettings.json' ) ); + } else { + res.status( 403 ).send( 'unauthorized' ); + } + } ); + + app.post( '/admin/plugins/polls/save', bodyParser.json(), ( req, res ) => { + if ( req.session.loggedInAdmin ) { + fs.writeFileSync( path.join( __dirname + '/data/votingSettings.json' ), JSON.stringify( req.body ) ); + res.send( 'ok' ); + } else { + res.status( 403 ).send( 'unauthorized' ); + } + } ); }; \ No newline at end of file