Skip to content

Commit

Permalink
Rebase and update theme logic handling
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalwengerter committed Dec 5, 2023
1 parent e77a186 commit acced1a
Show file tree
Hide file tree
Showing 25 changed files with 387 additions and 342 deletions.
1 change: 1 addition & 0 deletions changelog/unreleased/change-theme-handling
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ https://github.com/owncloud/web/issues/2404
https://github.com/owncloud/web/issues/8424
https://github.com/owncloud/web/issues/9403
https://github.com/owncloud/web/issues/9885
https://github.com/owncloud/web/issues/9939

https://github.com/owncloud/web/pull/8855
https://github.com/owncloud/web/pull/9396
Expand Down
2 changes: 0 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ Depending on the backend you are using, there are sample config files provided i
- `options.tokenStorageLocal` Specifies whether the access token will be stored in the local storage when set to `true` or in the session storage when set to `false`. If stored in the local storage, login state will be persisted across multiple browser tabs, means no additional logins are required. Defaults to `true`.
- `options.loginUrl` Specifies the target URL to the login page. This is helpful when an external IdP is used. This option is disabled by default. Example URL like: 'https://www.myidp.com/login'.
- `options.logoutUrl` Adds a link to the user's profile page to point him to an external page, where he can manage his session and devices. This is helpful when an external IdP is used. This option is disabled by default.
- `options.imprintUrl` Specifies the target URL for the imprint link valid for the ocis instance in the account menu.
- `options.privacyUrl` Specifies the target URL for the privacy link valid for the ocis instance in the account menu.
- `options.ocm.openRemotely` Specifies whether opening files in remote shares in their original ocm instance should be enabled. Defaults to `false`.
- `options.userListRequiresFilter` Defines whether one or more filters must be set in order to list users in the Web admin settings. Set this option to 'true' if running in an environment with a lot of users and listing all users could slow down performance. Defaults to `false`.

Expand Down
1 change: 1 addition & 0 deletions packages/web-app-files/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"luxon": "3.0.1",
"marked": "^4.0.12",
"p-queue": "^6.6.2",
"pinia": "2.1.7",
"qs": "6.11.2",
"sanitize-html": "^2.7.0",
"uuid": "9.0.1",
Expand Down
12 changes: 8 additions & 4 deletions packages/web-app-files/src/views/FilesDrop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
</div>

<div class="oc-text-center oc-mt-xxl">
<p v-text="currentTheme.general.slogan" />
<p v-text="themeSlogan" />
</div>
</div>
</div>
</template>

<script lang="ts">
import { storeToRefs } from 'pinia'
import { mapGetters } from 'vuex'
import { createLocationPublic, createLocationSpaces, useThemeStore } from '@ownclouders/web-pkg'
import ResourceUpload from '../components/AppBar/Upload/ResourceUpload.vue'
Expand Down Expand Up @@ -78,7 +79,7 @@ export default defineComponent({
setup() {
const uppyService = useService<UppyService>('$uppyService')
const store = useStore()
const { currentTheme } = useThemeStore()
const themeStore = useThemeStore()
const router = useRouter()
const route = useRoute()
const language = useGettext()
Expand All @@ -90,6 +91,9 @@ export default defineComponent({
const { getInternalSpace } = useGetMatchingSpace()
useUpload({ uppyService })
const { currentTheme } = storeToRefs(themeStore)
const themeSlogan = computed(() => currentTheme.value.common.slogan)
const fileIdQueryItem = useRouteQuery('fileId')
const fileId = computed(() => {
return queryItemAsString(unref(fileIdQueryItem))
Expand Down Expand Up @@ -205,11 +209,11 @@ export default defineComponent({
})
return {
currentTheme,
dragareaEnabled,
loading,
errorMessage,
share
share,
themeSlogan
}
},
computed: {
Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@uppy/xhr-upload": "^3.0.1",
"@vueuse/core": "^10.0.0",
"axios": "1.6.2",
"deepmerge": "^4.2.2",
"design-system": "workspace:@ownclouders/design-system@*",
"filesize": "^9.0.11",
"fuse.js": "6.6.2",
Expand Down
12 changes: 8 additions & 4 deletions packages/web-pkg/src/components/AppBanner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import { computed, defineComponent, ref, unref } from 'vue'
import { useRouter, useThemeStore } from '../composables'
import { buildUrl } from '../helpers/router'
import { useSessionStorage } from '@vueuse/core'
import { storeToRefs } from 'pinia'
export default defineComponent({
components: {},
Expand All @@ -58,13 +57,17 @@ export default defineComponent({
const isVisible = ref<boolean>(unref(appBannerWasClosed) === null)
const router = useRouter()
const themeStore = useThemeStore()
const { currentTheme } = storeToRefs(themeStore)
const { currentTheme } = useThemeStore()
const appBannerSettings = currentTheme.appBanner
const isAppBannerAvailable = computed(
() => appBannerSettings && Object.keys(appBannerSettings).length != 0
)
const appUrl = computed(() => {
return buildUrl(router, `/f/${props.fileId}`)
.toString()
.replace('https', currentTheme.value.appBanner.appScheme)
.replace('https', currentTheme.appBanner.appScheme)
})
const close = () => {
Expand All @@ -76,6 +79,7 @@ export default defineComponent({
appUrl,
close,
currentTheme,
isAppBannerAvailable,
isVisible
}
}
Expand Down
12 changes: 5 additions & 7 deletions packages/web-pkg/src/composables/appDefaults/useDocumentTitle.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { watch, Ref, unref } from 'vue'
import { useStore } from '../store'
import { Store } from 'vuex'
import { useEventBus } from '../eventBus'
import { useThemeStore } from '../piniaStores'
import { EventBus } from '../../services'

interface DocumentTitleOptions {
titleSegments: Ref<string[]>
store?: Store<any>
eventBus?: EventBus
}

export function useDocumentTitle({ titleSegments, store, eventBus }: DocumentTitleOptions): void {
store = store || useStore()
export function useDocumentTitle({ titleSegments, eventBus }: DocumentTitleOptions): void {
const { currentTheme } = useThemeStore()

eventBus = eventBus || useEventBus()

watch(
Expand All @@ -20,10 +19,9 @@ export function useDocumentTitle({ titleSegments, store, eventBus }: DocumentTit
const titleSegments = unref(newTitleSegments)

const glue = ' - '
const generalName = store.getters['configuration'].currentTheme.general.name
const payload = {
shortDocumentTitle: titleSegments.join(glue),
fullDocumentTitle: [...titleSegments, generalName].join(glue)
fullDocumentTitle: [...titleSegments, currentTheme.common.name].join(glue)
}

eventBus.publish('runtime.documentTitle.changed', payload)
Expand Down
115 changes: 71 additions & 44 deletions packages/web-pkg/src/composables/piniaStores/theme.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,80 @@
import merge from 'deepmerge'
import { defineStore } from 'pinia'
import { ref, computed, unref } from 'vue'
import { useLocalStorage, usePreferredDark } from '@vueuse/core'

interface CommonTheme {
interface AppBanner {
title: string
publisher: string
additionalInformation: string
ctaText: string
icon: string
appScheme: string
}

interface CommonSection {
name: string
slogan: string
logo: string
accessDeniedHelpUrl: string
urls: {
accessDeniedHelp: string
imprint: string
privacy: string
}
}

interface DesignTokens {
breakpoints?: Record<string, string>
colorPalette?: Record<string, string>
fontSizes?: Record<string, string>
sizes?: Record<string, string>
spacing?: Record<string, string>
}

interface LoginPage {
autoRedirect: boolean
backgroundImg: string
}

interface Logo {
topbar: string
favicon: string
login: string
notFound: string
}

interface ThemeDefaults {
appBanner?: AppBanner
common: CommonSection
designTokens: DesignTokens
loginPage: LoginPage
logo: Logo
}

interface WebTheme {
general: {
name: string
slogan: string
privacyUrl: string
imprintUrl: string
}
appBanner: {
title: string
publisher: string
additionalInformation: string
ctaText: string
icon: string
appScheme: string
}
appBanner?: AppBanner
common?: CommonSection
designTokens: DesignTokens

isDark: boolean
logo: {
topbar: string
favicon: string
login: string
notFound: string
}
loginPage: {
autoRedirect: boolean
backgroundImg: string
}
// TODO: Refine designTokens according to existing theme.json contents
designTokens: {
colorPalette: Record<string, string>
}
name: string
loginPage?: LoginPage
logo?: Logo
}

const themeNameLight = 'default'
const themeNameDark = 'default-dark'
interface WebThemeConfig {
defaults: ThemeDefaults
themes: WebTheme[]
}

const themeStorageKey = 'oc_currentThemeName'

export const useThemeStore = defineStore('theme', () => {
const commonTheme = ref<CommonTheme | undefined>()
const currentTheme = ref<WebTheme | undefined>()

const availableThemes = ref<WebTheme[]>([])

const hasOnlyOneTheme = computed(() => availableThemes.value.length > 1)
const hasOnlyOneTheme = computed(() => availableThemes.value.length === 1)

const hasOnlyTwoThemesForLightDarkMode = computed(
() =>
Expand All @@ -59,26 +83,29 @@ export const useThemeStore = defineStore('theme', () => {
availableThemes.value.some((t) => t.isDark !== true)
)

const initializeThemes = (themes: WebTheme[], newCommonTheme: CommonTheme) => {
availableThemes.value = themes
commonTheme.value = newCommonTheme
const initializeThemes = (themeConfig: WebThemeConfig) => {
availableThemes.value = themeConfig.themes.map((theme) => merge(themeConfig.defaults, theme))

const currentThemeName = useLocalStorage(themeStorageKey, null) // null as default to make fallback possible

const currentThemeName = useLocalStorage('oc_currentThemeName', null) // note: use null as default so that we can fall back to system preferences
// Set default theme name as fallback
if (unref(currentThemeName) === null) {
const isDark = usePreferredDark()
currentThemeName.value = isDark.value ? themeNameDark : themeNameLight
currentThemeName.value = availableThemes.value.find((t) => t.isDark === isDark.value)
}

// TODO: Fix this by passing full theme?
// TODO: Discuss handling (former) default scenario
setAndApplyTheme(availableThemes.value.find((t) => t.general.name === currentThemeName.value))
setAndApplyTheme(
availableThemes.value.find((t) => t.name === currentThemeName.value) ||
availableThemes.value[0]
)
}

const setAndApplyTheme = (theme: WebTheme) => {
currentTheme.value = theme
const currentLocalStorageThemeName = useLocalStorage(themeStorageKey, theme.name)
currentLocalStorageThemeName.value = currentTheme.value.name

for (const param in currentTheme.value.designTokens.colorPalette) {
// TODO: Shouldn't we loop over all designTokens and set them?
for (const param in currentTheme.value.designTokens?.colorPalette) {
;(document.querySelector(':root') as HTMLElement).style.setProperty(
`--oc-color-${param}`,
theme.designTokens.colorPalette[param]
Expand Down
7 changes: 5 additions & 2 deletions packages/web-runtime/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,17 @@ import { eventBus, useRouter } from '@ownclouders/web-pkg'
import { useHead } from './composables/head'
import { useStore, useThemeStore } from '@ownclouders/web-pkg'
import { RouteLocation } from 'vue-router'
import { storeToRefs } from 'pinia'
export default defineComponent({
components: {
SkipTo
},
setup() {
const store = useStore()
const { currentTheme } = useThemeStore()
const themeStore = useThemeStore()
const { currentTheme } = storeToRefs(themeStore)
const router = useRouter()
useHead({ store })
Expand Down Expand Up @@ -216,7 +219,7 @@ export default defineComponent({
const titleSegments = [routeTitle]
return {
shortDocumentTitle: titleSegments.join(glue),
fullDocumentTitle: [...titleSegments, this.currentTheme.general.name].join(glue)
fullDocumentTitle: [...titleSegments, this.currentTheme.common.name].join(glue)
}
}
}
Expand Down
Loading

0 comments on commit acced1a

Please sign in to comment.