Skip to content

Instantly share code, notes, and snippets.

@alshdavid
Last active November 12, 2021 21:19
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alshdavid/032ea535f222646dc74420e20b28faa1 to your computer and use it in GitHub Desktop.
Save alshdavid/032ea535f222646dc74420e20b28faa1 to your computer and use it in GitHub Desktop.
import { Injectable } from '@angular/core'
import { Http } from '@angular/http'
import { environment } from '../../environments/environment'
const sleep = (d: number) => new Promise(res => setTimeout(res, d))
@Injectable()
export class NewVersionService {
pollRate = 1000 * 60 * 10 // 10 minutes
currentBuild = environment.build
showingPrompt = false
get trys() {
return parseInt(localStorage.getItem('update.retries')) || 0
}
set trys(value: number) {
localStorage.setItem('update.retries', value.toString())
}
constructor(
private http: Http
) {}
// startPolling will start polling a textfile with a build. if the build differs
// to the build number stored in the environement file then prompt the user to
// refresh the page. If the build is still wrong after two refreshes, provide a
// resource to tell them how to hard refresh their browser.
async startPolling() {
if (environment.production === false) {
return
}
if (this.trys > 1) {
await this.showPrompt(`
Looks like you're having trouble updating.<br>
Try hard refreshing your browser manually.<br><br>`,
'Click here to find out how')
window.open(
'https://fabricdigital.co.nz/blog/how-to-hard-refresh-your-browser-and-clear-cache',
'_blank'
)
return
}
while(true) {
const build = await this.getBuild()
if (build !== this.currentBuild) {
await this.showPrompt('New version is available', 'click here to update')
this.trys++
window.location.reload(true)
} else {
this.trys = 0
}
await sleep(this.pollRate)
}
}
// getBuild fetches the text file that holds the build
getBuild(): string {
return this.http.get('/assets/build.txt')
.toPromise()
.then(response => response.text())
}
// showPrompt is a cheap and dirty modal creator using standard DOM manipulation.
// I do this because it's too small a job to setup entryComponents and this ensures
// the service is portable and (outside of Angular) dependency free.
showPrompt(text = '', link = '') {
return new Promise(res => {
const [ container, mountContainer ] = createElement({
styles: `
display: flex;
position: fixed;
bottom: 20px;
left: 0;
right: 0;
z-index: 9999999999;`
})
const [ textBox, mountTextBox ] = createElement({
html: text,
styles: `
display: block;
background-color: #333;
color: white;
padding: 20px 40px;
line-height: 26px;
margin: 0 auto;
text-align: center;
box-shadow: 0 2px 5px 0 rgba(0,0,0,.3);`
})
const [ linkBox, mountlinkBox ] = createElement({
html: link,
onClick: (e) => onClick(e),
styles: `
display: inline-block;
color: #2196F3;
cursor: pointer;
text-decoration: underline;
padding-left: 8px;`
})
mountTextBox(container)
mountlinkBox(textBox)
mountContainer(document.body)
function onClick(e: MouseEvent) {
res(true)
document.body.removeChild(container)
}
})
}
}
// A simple wrapper to help with the whole DOM manipluation thing
// though maybe I should have just used hyperscript or something.
const createElement = ({
html = '',
styles = '',
tag = 'div',
onClick = (e: MouseEvent) => {},
} = {}): [ HTMLElement, any ] => {
const el = document.createElement(tag)
el.style.cssText = styles
el.innerHTML = html
el.onclick = (e) => onClick(e)
const mount = (target: HTMLElement) => {
target.appendChild(el)
el.getBoundingClientRect()
}
return [ el, mount ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment