MERGE - integrate latest dev changes

This commit is contained in:
Dominik
2026-08-09 05:39:11 +02:00
57 changed files with 2725 additions and 70 deletions
+73
View File
@@ -615,6 +615,62 @@ const defaultLocales: LocaleTree = {
default: 'The phone request failed.',
},
},
radio: {
name: 'Radio',
disconnected: 'Not connected',
connectedTo: 'Connected - {frequency} MHz',
noProvider: 'Voice service unavailable',
channel: 'Channel',
primaryFrequency: 'Primary frequency',
secondaryFrequency: 'Secondary frequency',
frequencyPlaceholder: 'e.g. 120.5',
optional: 'Optional',
mhz: 'MHz',
volume: 'Volume',
connect: 'Connect',
disconnect: 'Disconnect',
members: 'Currently connected ({count})',
noMembers: 'No participants',
history: 'Recently connected',
noHistory: 'No history',
badge: 'Service number',
badgePlaceholder: 'e.g. 231',
profileSaved: 'Radio profile saved',
displayName: 'Radio display name',
displayNamePlaceholder: 'Leave empty to use your character name',
displayNameDescription:
'This name is shown to other participants and in the built-in radio overlay.',
displayNameNotAllowed:
'Your current job or grade is not allowed to change the radio display name.',
otherSettings: 'Other',
autoRejoin: 'Automatic rejoin',
autoRejoinDescription:
'Reconnect after spawning or restarting the phone resource',
radioNotifications: 'Radio notifications',
notificationsDescription: 'Show joins and leaves on your current channel',
memberJoined: '{name} joined the radio',
memberLeft: '{name} left the radio',
unknownMember: 'Unknown',
tabs: { radio: 'Radio', settings: 'Settings' },
errors: {
invalid_frequency: 'Enter a valid frequency.',
invalid_volume: 'Enter a valid volume.',
channel_locked: 'You do not have access to this channel.',
secondary_locked: 'You do not have access to the secondary channel.',
voice_unavailable: 'The configured radio voice service is unavailable.',
player_unavailable: 'Your player data is not available.',
rate_limited: 'Please wait before changing channel again.',
invalid_setting: 'This setting is invalid.',
badge_disabled: 'Service numbers are disabled.',
badge_forbidden: 'This service number is not allowed.',
display_name_disabled: 'Radio display names are disabled.',
display_name_forbidden:
'Your job or grade cannot change the radio display name.',
invalid_display_name: 'Enter a valid radio display name.',
request_failed: 'The radio request failed.',
default: 'The radio request failed.',
},
},
banking: {
name: 'Banking',
welcome: 'Welcome back',
@@ -1685,6 +1741,13 @@ const defaultLocales: LocaleTree = {
notificationVolume: 'Notification Volume',
ringtone: 'Ringtone',
notificationSound: 'Notification Sound',
graphicsMode: 'Graphics Mode',
performanceMode: 'Performance Mode',
performanceModeDescription:
'Blur-free glass simulation for better CEF performance',
ultimateMode: 'Ultimate Mode',
ultimateModeDescription:
'Full blur and glass effects for compatible FiveM clients',
appearanceMode: 'Appearance Mode',
automatic: 'Automatic',
light: 'Light',
@@ -1772,7 +1835,17 @@ const defaultLocales: LocaleTree = {
blue: 'Blue',
green: 'Green',
lavender: 'Lavender',
red: 'Red',
white: 'White',
orange: 'Orange',
yellow: 'Yellow',
lime: 'Lime',
teal: 'Teal',
cyan: 'Cyan',
purple: 'Purple',
pink: 'Pink',
gold: 'Gold',
rgb: 'RGB',
},
ringtones: {
skyline: 'Skyline',
+142
View File
@@ -0,0 +1,142 @@
import { reactive, ref } from 'vue'
import { defineStore } from 'pinia'
import type { RadioData, RadioMember, RadioSettings } from '@/types/radio'
import { nuiCall } from '@/utils/nui'
const defaults: RadioData = {
badge: '',
badgeEnabled: true,
badgeMaxLength: 8,
connected: false,
displayName: '',
displayNameAllowed: false,
displayNameEnabled: true,
displayNameMaxLength: 32,
frequency: 0,
frequencyMax: 999.9,
frequencyMin: 0.1,
frequencyStep: 0.1,
history: [],
members: [],
provider: null,
secondaryFrequency: 0,
secondarySupported: true,
settings: { autoRejoin: false, notifications: false },
volume: 50,
}
export const useRadioStore = defineStore('radio', () => {
const data = reactive<RadioData>(structuredClone(defaults))
const error = ref('')
const isLoading = ref(false)
function apply(next: Partial<RadioData>): void {
Object.assign(data, next)
}
async function load(): Promise<void> {
isLoading.value = true
error.value = ''
const response = await nuiCall<RadioData>('radio:get')
if (response.success && response.data) apply(response.data)
else error.value = response.error ?? 'request_failed'
isLoading.value = false
}
async function connect(
frequency: number,
secondaryFrequency: number,
): Promise<boolean> {
isLoading.value = true
error.value = ''
const response = await nuiCall<Partial<RadioData>>('radio:connect', {
frequency,
secondaryFrequency,
})
if (response.success && response.data) apply(response.data)
else error.value = response.error ?? 'request_failed'
isLoading.value = false
return response.success
}
async function disconnect(): Promise<void> {
error.value = ''
const response = await nuiCall('radio:disconnect')
if (!response.success) {
error.value = response.error ?? 'request_failed'
return
}
apply({
connected: false,
frequency: 0,
members: [],
secondaryFrequency: 0,
})
}
async function setVolume(volume: number): Promise<void> {
data.volume = volume
const response = await nuiCall<{ volume: number }>('radio:set-volume', {
volume,
})
if (response.success && response.data) data.volume = response.data.volume
}
async function saveSetting(
key: keyof RadioSettings,
value: boolean,
): Promise<void> {
const previous = data.settings[key]
data.settings[key] = value
const response = await nuiCall<RadioSettings>('radio:save-settings', {
key,
value,
})
if (response.success && response.data) data.settings = response.data
else {
data.settings[key] = previous
error.value = response.error ?? 'request_failed'
}
}
async function saveBadge(badge: string): Promise<boolean> {
error.value = ''
const response = await nuiCall<{ badge: string }>('radio:save-badge', {
badge,
})
if (response.success && response.data) data.badge = response.data.badge
else error.value = response.error ?? 'request_failed'
return response.success
}
async function saveDisplayName(displayName: string): Promise<boolean> {
error.value = ''
const response = await nuiCall<{ displayName: string }>(
'radio:save-display-name',
{ displayName },
)
if (response.success && response.data)
data.displayName = response.data.displayName
else error.value = response.error ?? 'request_failed'
return response.success
}
function updateMembers(members: RadioMember[]): void {
data.members = members
}
return {
connect,
data,
disconnect,
error,
isLoading,
load,
saveBadge,
saveDisplayName,
saveSetting,
setVolume,
updateMembers,
}
})