Compare commits

...

9 commits

Author SHA1 Message Date
13d4369c48 Merge pull request 'Add support for 12 and 24 hour time formats (and add radio buttons)' (#33) from time-format into main
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
Reviewed-on: #33
Reviewed-by: SauceyRed <saucey@saucey.red>
2025-07-12 20:48:35 +00:00
0a8ae5fe31
fix: move radio buttons to subfolder
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
ci/woodpecker/pr/build-and-publish Pipeline was successful
ci/woodpecker/pull_request_closed/build-and-publish Pipeline was successful
2025-07-12 22:48:11 +02:00
885fc5f906
fix: PR complaints
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
ci/woodpecker/pr/build-and-publish Pipeline was successful
2025-07-12 22:40:54 +02:00
195322f3b0
feat: make settings typed and store "12" and "24" over "12-hour" and "24-hour" internally 2025-07-12 22:39:26 +02:00
eb49450756
feat: support 12 and 24 hour formats
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
ci/woodpecker/pr/build-and-publish Pipeline was successful
2025-07-12 20:43:25 +02:00
de6c9bb7eb
feat: finish radio buttons 2025-07-12 20:35:05 +02:00
562409b660
fix: move radio buttons.vue into UI folder 2025-07-12 19:34:34 +02:00
963da24046
Merge remote-tracking branch 'origin/main' into time-format 2025-07-12 19:32:04 +02:00
87a5b99e50
feat: add radio buttons and start integrating them into time format setting
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
2025-07-12 18:49:28 +02:00
8 changed files with 167 additions and 11 deletions

View file

@ -15,7 +15,6 @@
<script lang="ts" setup>
import ContextMenu from '~/components/ContextMenu.vue';
import { render } from 'vue';
import settingLoad from './utils/settingLoad';
const banner = useState("banner", () => false);
@ -51,7 +50,7 @@ function contextMenuHandler(e: MouseEvent) {
//]);
}
const currentTheme = settingLoad("selectedThemeId") ?? "dark"
const currentTheme = settingsLoad().selectedThemeId ?? "dark"
const baseURL = useRuntimeConfig().app.baseURL;
useHead({

View file

@ -46,7 +46,8 @@
<span class="message-date" :title="date.toString()">
<span v-if="getDayDifference(date, currentDate) === 1">Yesterday at</span>
<span v-else-if="getDayDifference(date, currentDate) > 1 ">{{ date.toLocaleDateString(undefined) }},</span>
{{ date.toLocaleTimeString(undefined, { timeStyle: "short" }) }}
{{ date.toLocaleTimeString(undefined, { hour12: props.format == "12", timeStyle: "short" }) }}
</span>
</div>
<div class="message-text" v-html="sanitized" tabindex="0"></div>

View file

@ -3,7 +3,7 @@
<div id="messages" ref="messagesElement">
<Message v-for="(message, i) of messages" :username="message.user.display_name ?? message.user.username"
:text="message.message" :timestamp="messageTimestamps[message.uuid]" :img="message.user.avatar"
format="12" :type="messagesType[message.uuid]"
:format="timeFormat" :type="messagesType[message.uuid]"
:margin-bottom="(messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal') ?? false"
:last="i == messages.length - 1" :message-id="message.uuid" :author="message.user" :me="me"
:message="message" :is-reply="message.reply_to"
@ -49,6 +49,7 @@ const me = await fetchWithApi("/me") as UserResponse;
const messageTimestamps = ref<Record<string, number>>({});
const messagesType = ref<Record<string, "normal" | "grouped">>({});
const messageGroupingMaxDifference = useRuntimeConfig().public.messageGroupingMaxDifference
const timeFormat = getPreferredTimeFormat()
const messagesRes: MessageResponse[] | undefined = await fetchWithApi(
`${props.channelUrl}/messages`,

View file

@ -17,20 +17,27 @@
</div>
</div>
<p class="subtitle">ICONS</p>
<div class="themes">
<!-- <p class="subtitle">Icons</p>
<div class="icons">
</div> -->
<p class="subtitle">TIME FORMAT</p>
<div class="icons">
<RadioButtons :button-count="3" :text-strings="timeFormatTextStrings"
:default-button-index="settingsLoad().timeFormat?.index ?? 0" :callback="onTimeFormatClicked"></RadioButtons>
</div>
</div>
</template>
<script lang="ts" setup>
import RadioButtons from '~/components/UserInterface/RadioButtons.vue';
import type { TimeFormat } from '~/types/settings';
import settingSave from '~/utils/settingSave';
const runtimeConfig = useRuntimeConfig()
const defaultThemes = runtimeConfig.public.defaultThemes
const baseURL = runtimeConfig.app.baseURL;
const timeFormatTextStrings = ["Auto", "12-Hour", "24-Hour"]
let themeLinkElement: HTMLLinkElement | null = null;
const themes: Array<Theme> = []
@ -70,6 +77,21 @@ async function fetchThemes() {
}
await fetchThemes()
async function onTimeFormatClicked(index: number) {
let format: "auto" | "12" | "24" = "auto"
if (index == 0) {
format = "auto"
} else if (index == 1) {
format = "12"
} else if (index == 2) {
format = "24"
}
const timeFormat: TimeFormat = {index, format}
settingSave("timeFormat", timeFormat)
}
</script>
<style scoped>

View file

@ -0,0 +1,111 @@
<template>
<div class="radio-buttons-container" ref="radioButtonsContainer">
<div v-for="index in indices" :key="index" class="radio-button" @click="onClick(index)">
<span class="radio-button-radio"></span>
<span class="radio-button-text">{{ textStrings[index] }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
const radioButtonsContainer = ref<HTMLDivElement>()
const props = defineProps<{
textStrings: string[],
buttonCount: number,
defaultButtonIndex: number,
callback: CallableFunction,
}>();
// makes an array from 0 to buttonCount - 1
const indices = Array.from({ length: props.buttonCount }, (_, i) => i)
// select default selected button
onMounted(async () => {
await nextTick()
if (props.defaultButtonIndex != undefined && radioButtonsContainer.value) {
const children = radioButtonsContainer.value.children
const defaultButton = children.item(props.defaultButtonIndex)
defaultButton?.classList.add("selected-radio-button")
defaultButton?.children.item(0)?.classList.add("selected-radio-button-radio")
}
})
function onClick(clickedIndex: number) {
// remove selected-radio-button class from all buttons except the clicked one
if (radioButtonsContainer.value) {
const children = radioButtonsContainer.value.children
for (let i = 0; i < children.length; i++) {
children.item(i)?.classList.remove("selected-radio-button")
children.item(i)?.children.item(0)?.classList.remove("selected-radio-button-radio")
}
children.item(clickedIndex)?.classList.add("selected-radio-button")
children.item(clickedIndex)?.children.item(0)?.classList.add("selected-radio-button-radio")
}
props.callback(clickedIndex)
}
</script>
<style scoped>
.radio-buttons-container {
display: flex;
flex-direction: column;
}
.radio-button {
cursor: pointer;
display: flex;
align-items: center;
border-radius: .66em;
background-color: unset;
color: var(--text-color);
padding: 0.4em 0.75em;
margin: 0.4em 0em;
font-size: 1.1em;
transition: background-color 0.2s;
}
.radio-button:hover {
background-color: var(--secondary-highlighted-color);
}
.selected-radio-button {
background-color: var(--accent-color);
}
.selected-radio-button:hover {
background-color: var(--accent-highlighted-color);
}
.radio-button-radio, .selected-radio-button-radio {
position: relative;
display: inline-block;
border-radius: 1em;
}
.radio-button-radio {
height: 1em;
width: 1em;
border: .15em solid var(--primary-color);
}
.selected-radio-button-radio {
height: 1em;
width: 1em;
border: 0.15em solid var(--primary-color);
background-color: var(--primary-highlighted-color);
}
.radio-button-text {
margin-left: .5em;
}
</style>

9
types/settings.ts Normal file
View file

@ -0,0 +1,9 @@
export interface ClientSettings {
selectedThemeId?: string, // the ID of the theme, not the URL, for example "dark"
timeFormat?: TimeFormat
}
export interface TimeFormat {
index: number,
format: "auto" | "12" | "24"
}

View file

@ -0,0 +1,11 @@
export default (): "12" | "24" => {
const format = settingsLoad().timeFormat?.format ?? "auto"
if (format == "12") {
return "12"
} else if (format == "24") {
return "24"
}
return "24"
}

View file

@ -1,10 +1,12 @@
export default (key: string): any => {
import type { ClientSettings } from "~/types/settings"
export default (): ClientSettings => {
let clientSettingsItem: string | null = localStorage.getItem("clientSettings")
if (typeof clientSettingsItem != "string") {
clientSettingsItem = "{}"
}
let clientSettings: { [key: string]: any } = {}
let clientSettings: ClientSettings = {}
try {
clientSettings = JSON.parse(clientSettingsItem)
} catch {
@ -15,5 +17,5 @@ export default (key: string): any => {
clientSettings = {}
}
return clientSettings[key]
return clientSettings
}