Skip to content

Commit

Permalink
feat: display sidebarLogo als favicon from theme
Browse files Browse the repository at this point in the history
also added a function to change the logo color in the sidebar

Signed-off-by: Stefan Dej <[email protected]>
  • Loading branch information
meteyou committed Jun 30, 2024
1 parent a824fb4 commit fc4aa57
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 84 deletions.
156 changes: 94 additions & 62 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -235,73 +235,105 @@ export default class App extends Mixins(BaseMixin, ThemeMixin) {
doc.className = dark ? 'theme--dark' : 'theme--light'
}
drawFavicon(val: number): void {
async drawFavicon(val: number): Promise<void> {
const favicon16: HTMLLinkElement | null = document.querySelector("link[rel*='icon'][sizes='16x16']")
const favicon32: HTMLLinkElement | null = document.querySelector("link[rel*='icon'][sizes='32x32']")
if (favicon16 && favicon32) {
if (this.progressAsFavicon && this.printerIsPrinting) {
let faviconSize = 64
let canvas = document.createElement('canvas')
canvas.width = faviconSize
canvas.height = faviconSize
const context = canvas.getContext('2d')
const centerX = canvas.width / 2
const centerY = canvas.height / 2
const radius = 32
// draw the grey circle
if (context) {
context.beginPath()
context.moveTo(centerX, centerY)
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false)
context.closePath()
context.fillStyle = '#ddd'
context.fill()
context.strokeStyle = 'rgba(200, 208, 218, 0.66)'
context.stroke()
// draw the green circle based on percentage
let startAngle = 1.5 * Math.PI
let endAngle = 0
let unitValue = (Math.PI - 0.5 * Math.PI) / 25
if (val >= 0 && val <= 25) endAngle = startAngle + val * unitValue
else if (val > 25 && val <= 50) endAngle = startAngle + val * unitValue
else if (val > 50 && val <= 75) endAngle = startAngle + val * unitValue
else if (val > 75 && val <= 100) endAngle = startAngle + val * unitValue
context.beginPath()
context.moveTo(centerX, centerY)
context.arc(centerX, centerY, radius, startAngle, endAngle, false)
context.closePath()
context.fillStyle = this.logoColor
context.fill()
favicon16.href = canvas.toDataURL('image/png')
favicon32.href = canvas.toDataURL('image/png')
}
} else if (this.customFavicons) {
const [favicon16Path, favicon32Path] = this.customFavicons
favicon16.href = favicon16Path
favicon32.href = favicon32Path
} else {
const favicon =
'data:image/svg+xml;base64,' +
window.btoa(`
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 599.38 523.11" xml:space="preserve">
<g>
<path style="fill:${this.logoColor};" d="M382.29,142.98L132.98,522.82L0,522.68L344.3,0l0,0C352.18,49.06,365.2,97.68,382.29,142.98"/>
<path style="fill:${this.logoColor};" d="M413.28,213.54L208.5,522.92l132.94,0.19l135.03-206.33l0,0C452.69,284.29,431.53,249.77,413.28,213.54 L413.28,213.54"/>
<path style="fill:${this.logoColor};" d="M599.38,447.69l-49.25,75.42L417,522.82l101.6-153.67l0,0C543.48,397.35,570.49,423.61,599.38,447.69 L599.38,447.69z"/>
</g>
</svg>
`)
favicon16.href = favicon
favicon32.href = favicon
// if no favicon is found, stop
if (!favicon16 || !favicon32) return
// if progressAsFavicon is enabled and the printer is printing, draw the progress as favicon
if (this.progressAsFavicon && this.printerIsPrinting) {
let faviconSize = 64
let canvas = document.createElement('canvas')
canvas.width = faviconSize
canvas.height = faviconSize
const context = canvas.getContext('2d')
const centerX = canvas.width / 2
const centerY = canvas.height / 2
const radius = 32
if (!context) return
// draw the grey circle
context.beginPath()
context.moveTo(centerX, centerY)
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false)
context.closePath()
context.fillStyle = '#ddd'
context.fill()
context.strokeStyle = 'rgba(200, 208, 218, 0.66)'
context.stroke()
// draw the green circle based on percentage
let startAngle = 1.5 * Math.PI
let endAngle = 0
let unitValue = (Math.PI - 0.5 * Math.PI) / 25
if (val >= 0 && val <= 25) endAngle = startAngle + val * unitValue
else if (val > 25 && val <= 50) endAngle = startAngle + val * unitValue
else if (val > 50 && val <= 75) endAngle = startAngle + val * unitValue
else if (val > 75 && val <= 100) endAngle = startAngle + val * unitValue
context.beginPath()
context.moveTo(centerX, centerY)
context.arc(centerX, centerY, radius, startAngle, endAngle, false)
context.closePath()
context.fillStyle = this.logoColor
context.fill()
favicon16.href = canvas.toDataURL('image/png')
favicon32.href = canvas.toDataURL('image/png')
return
}
// if custom favicons are set, use them
if (this.customFavicons) {
const [favicon16Path, favicon32Path] = this.customFavicons
favicon16.href = favicon16Path
favicon32.href = favicon32Path
return
}
// if a theme sidebar logo is set, use it
if ((this.theme.sidebarLogo ?? false) !== false && this.sidebarLogo.endsWith('.svg')) {
const response = await fetch(this.sidebarLogo)
if (!response.ok) return
const text = await response.text()
const modifiedSvg = text.replace(/fill="var\(--color-logo, #[0-9a-fA-F]{6}\)"/g, `fill="${this.logoColor}"`)
const blob = new Blob([modifiedSvg], { type: 'image/svg+xml' })
const reader = new FileReader()
reader.onloadend = () => {
const base64data = reader.result as string
favicon16.href = base64data
favicon32.href = base64data
}
reader.readAsDataURL(blob)
return
}
// if no custom favicon is set, use the default one
const favicon =
'data:image/svg+xml;base64,' +
window.btoa(`
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 599.38 523.11" xml:space="preserve">
<g>
<path style="fill:${this.logoColor};" d="M382.29,142.98L132.98,522.82L0,522.68L344.3,0l0,0C352.18,49.06,365.2,97.68,382.29,142.98"/>
<path style="fill:${this.logoColor};" d="M413.28,213.54L208.5,522.92l132.94,0.19l135.03-206.33l0,0C452.69,284.29,431.53,249.77,413.28,213.54 L413.28,213.54"/>
<path style="fill:${this.logoColor};" d="M599.38,447.69l-49.25,75.42L417,522.82l101.6-153.67l0,0C543.48,397.35,570.49,423.61,599.38,447.69 L599.38,447.69z"/>
</g>
</svg>
`)
favicon16.href = favicon
favicon32.href = favicon
}
@Watch('customFavicons')
Expand Down
19 changes: 0 additions & 19 deletions src/components/TheTopbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -193,25 +193,6 @@ export default class TheTopbar extends Mixins(BaseMixin, ThemeMixin) {
return this.$store.state.gui.uiSettings.boolHideUploadAndPrintButton ?? false
}
get themeObject() {
return themes.find((t) => t.name === this.theme) ?? null
}
get sidebarLogo(): string {
let url = this.$store.getters['files/getSidebarLogo']
if (url !== '' || this.theme === 'mainsail') return url
// if no theme is set, return empty string to load the default logo
if (this.themeObject === null || (this.themeObject.sidebarLogo ?? false) === false) return ''
// return light logo if theme is light and sidebarLogo is set to both
if (this.themeObject.sidebarLogo === 'both' && this.themeMode === 'light')
return `/img/themes/sidebarLogo-${this.theme}-light.svg`
// return dark/generic theme logo
return `/img/themes/sidebarLogo-${this.theme}.svg`
}
get isSvgLogo() {
return this.sidebarLogo.includes('.svg?timestamp=') || this.sidebarLogo.endsWith('.svg')
}
Expand Down
21 changes: 20 additions & 1 deletion src/components/mixins/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ export default class ThemeMixin extends Vue {
return this.fgColor(alpha, !this.$vuetify.theme.dark)
}

get theme() {
get themeName() {
return this.$store.getters['gui/theme']
}

get theme() {
return this.$store.getters['gui/getTheme']
}

get themeMode() {
return this.$store.state.gui.uiSettings.mode ?? 'dark'
}
Expand Down Expand Up @@ -52,4 +56,19 @@ export default class ThemeMixin extends Vue {
get sidebarBgImage() {
return this.$vuetify.theme.dark ? '/img/sidebar-background.svg' : '/img/sidebar-background-light.svg'
}

get sidebarLogo(): string {
const url = this.$store.getters['files/getSidebarLogo']
if (url !== '' || this.themeName === 'mainsail') return url

// if no theme is set, return empty string to load the default logo
if ((this.theme.sidebarLogo ?? false) === false) return ''

// return light logo if theme is light and sidebarLogo is set to both
if (this.theme.sidebarLogo === 'both' && this.themeMode === 'light')
return `/img/themes/sidebarLogo-${this.themeName}-light.svg`

// return dark/generic theme logo
return `/img/themes/sidebarLogo-${this.themeName}.svg`
}
}
2 changes: 1 addition & 1 deletion src/components/settings/SettingsUiSettingsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ export default class SettingsUiSettingsTab extends Mixins(BaseMixin, ThemeMixin)
}
get defaultLogoColor() {
return themes.find((theme) => theme.name === this.theme)?.colorLogo ?? defaultLogoColor
return themes.find((theme) => theme.name === this.themeName)?.colorLogo ?? defaultLogoColor
}
get primaryColor() {
Expand Down
6 changes: 5 additions & 1 deletion src/store/gui/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Theme } from '@/store/types'

// eslint-disable-next-line
export const getters: GetterTree<GuiState, any> = {
theme: (state) => {
theme: (state): string => {
const theme = state.uiSettings.theme

// return defaultTheme, if theme doesnt exists
Expand All @@ -15,6 +15,10 @@ export const getters: GetterTree<GuiState, any> = {
return theme
},

getTheme: (state, getters): Theme => {
return themes.find((theme: Theme) => theme.name === getters.theme) ?? themes[0]
},

getDatasetValue: (state) => (payload: { name: string; type: string }) => {
if (
payload.name in state.view.tempchart.datasetSettings &&
Expand Down

0 comments on commit fc4aa57

Please sign in to comment.