Skip to content

Commit

Permalink
Kiosk Media Rotation (#21)
Browse files Browse the repository at this point in the history
* Main page works

- main page will go to carousel after inactivity, touching the screen will bring it back to the main page
- Google Drive API works for a public folder

* Added media check

If the media fetch fails or there isn't any media in the folder, the main page won't set any timers/click listeners

* Added main pages to rotation and an indicator that kiosk is touchscreen

* Fix variable typo

* Switch from Google Drive to AWS

* remove unneeded hiring page

* lower refresh interval
  • Loading branch information
s-egge authored Jul 25, 2024
1 parent e4f6241 commit c9c2881
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 161 deletions.
43 changes: 38 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"vue": "^2.6.10",
"vue-i18n": "^8.17.3",
"vue-router": "^3.1.6",
"vuex": "^3.2.0"
"vuex": "^3.2.0",
"xml2js": "^0.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.12.0",
Expand Down
Empty file added prettier.rc
Empty file.
99 changes: 82 additions & 17 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,11 @@
</template>

<script>
import axios from 'axios'
export default {
name: 'App',
components: {},
async created () {
// For future reference on how back button doesn't clear cache (for MU kiosk)
// https://stackoverflow.com/a/75952012
// https://stackoverflow.com/questions/58652880/what-is-the-replacement-for-performance-navigation-type-in-angular
// turned off for local development for reasons explained in "watch: " section
if (process.env.NODE_ENV !== 'development') {
this.timer = setInterval(
this.fetchLastModified,
this.refreshInterval * 1000
) // setInterval expects milliseconds
}
},
data () {
return {
card: {
Expand All @@ -41,7 +30,10 @@ export default {
url: process.env.VUE_APP_HOST_ADDRESS,
modifiedDateUnix: 0,
timeDiffUnix: 0,
refreshInterval: 600 // 10 minutes refresh interval. Time in seconds (lower for debug)
refreshInterval: 300, // 5 minutes refresh interval. Time in seconds (lower for debug)
inactivityTimeout: 30000, // 30 seconds of inactivity. Time in milliseconds
inactivityTimer: null,
mediaList: [],
}
},
methods: {
Expand All @@ -62,12 +54,80 @@ export default {
// Log the modifiedDateUnix and timeDiffUnix here if needed for debug
})
},
cancelAutoUpdate () {
clearInterval(this.timer)
// fetch media from Google Drive folder
async fetchMedia () {
const bucketURL = "https://osu-kiosk-media.s3.us-west-2.amazonaws.com"
// fetch media list from S3 bucket
try {
const response = await axios.get(bucketURL)
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(response.data, "text/xml");
const keys = xmlDoc.getElementsByTagName("Key");
this.mediaList = Array.from(keys).map(key => `${bucketURL}/${key.textContent}`);
} catch (error) {
console.error('Error fetching media:', error)
}
console.log("Media list:", this.mediaList);
},
// creates a timer that routes to the Carousel page after time is up
createInactivityTimer () {
this.inactivityTimer = setTimeout(() => {
this.$router.push({
name: 'Carousel',
params: {
images: this.mediaList,
returnRoute: this.$route.path,
touchScreenIndicator: this.$route.path === '/'
}
})
}, this.inactivityTimeout)
},
navigateToHomepage () {
// clear timer
if (this.inactivityTimer) {
clearTimeout(this.inactivityTimer)
}
// set a new timer for navigating to image carousel
this.createInactivityTimer()
this.$router.push('/')
}
},
async created () {
// For future reference on how back button doesn't clear cache (for MU kiosk)
// https://stackoverflow.com/a/75952012
// https://stackoverflow.com/questions/58652880/what-is-the-replacement-for-performance-navigation-type-in-angular
// turned off for local development for reasons explained in "watch: " section
if (process.env.NODE_ENV !== 'development') {
this.timer = setInterval(
this.fetchLastModified,
this.refreshInterval * 1000
) // setInterval expects milliseconds
}
// get media for rotation
await this.fetchMedia()
// if there is media, create a timer and click listener for media rotation
if (this.mediaList.length > 0) {
this.$el.addEventListener('click', this.navigateToHomepage)
this.createInactivityTimer()
}
},
beforeDestroy () {
this.cancelAutoUpdate()
clearInterval(this.timer)
if (this.inactivityTimer) {
clearTimeout(this.inactivityTimer)
}
this.$el.removeEventListener('click', this.navigateToHomepage)
},
watch: {
// modifiedDateUnix is checked periodically as defined by this.refreshInterval
Expand All @@ -83,6 +143,11 @@ export default {
window.location.reload()
}, 10000)
}
},
$route (to, from) {
if (from.path === '/carousel' && to.path !== '/carousel') {
this.createInactivityTimer()
}
}
}
}
Expand Down
Binary file removed src/assets/assessment_and_planning_intern.png
Binary file not shown.
Binary file removed src/assets/qr-code_hiring_apr_2024-test.png
Binary file not shown.
Binary file removed src/assets/qr-code_hiring_apr_2024.png
Binary file not shown.
Binary file removed src/assets/student_engagement_intern.png
Binary file not shown.
94 changes: 94 additions & 0 deletions src/components/carousel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<template>
<el-row class="img-container" type="flex" justify="center" align="middle">
<img :src="currentImage" />
<div v-if="touchScreenIndicator">
<h1 class="text-content">
Tap to learn more about the Sustainability Office
</h1>
</div>
</el-row>
</template>

<script>
export default {
props: {
images: {
type: Array,
required: true
},
returnRoute: {
type: String,
default: null
},
touchScreenIndicator: {
type: Boolean,
default: false
}
},
data () {
return {
imgIndex: 0,
rotationInterval: 10000, // time in ms (10 seconds)
}
},
computed: {
currentImage () {
return this.images[this.imgIndex]
}
},
methods: {
rotateImage () {
this.imgIndex = (this.imgIndex + 1) % this.images.length
// if a return route was given and all images have been shown,
// return to the previous route
if (this.returnRoute && this.imgIndex === 0) {
this.$router.push(this.returnRoute)
}
}
},
// rotate the image based on the given interval (in ms)
// ref: https://developer.mozilla.org/en-US/docs/Web/API/setInterval
mounted () {
this.timer = setInterval(this.rotateImage, this.rotationInterval)
// if there are no images, redirect to the home page
if (!this.images || this.images.length === 0) {
this.$router.push('/')
}
},
beforeDestroy () {
clearInterval(this.timer)
}
}
</script>

<style scoped lang="scss">
.img-container {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
margin: 0px;
padding: 0px;
z-index: 0;
background-color: black;
}
// grow image to fill screen but maintain aspect-ratio and full view
img {
width: 100%;
height: 100%;
object-fit: contain;
}
.text-content {
position: absolute;
bottom: 10px;
left: 10px;
color: white;
padding: 5px;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
Loading

0 comments on commit c9c2881

Please sign in to comment.