refactor auto change status

This commit is contained in:
pa
2026-02-14 17:07:22 +09:00
parent 64869a218e
commit 4e552bf3b9
5 changed files with 310 additions and 77 deletions

View File

@@ -537,20 +537,23 @@
"automation": {
"auto_change_status": "Auto Change Status",
"auto_state_change_tooltip": "Automatically change status when there are other people in the instance (Alone / Company)",
"alone_condition": "Alone Condition",
"alone": "Alone",
"no_friends": "No Friends",
"alone_condition": "Consider alone when",
"alone": "No other players",
"no_friends": "No friends in instance",
"alone_status": "Alone Status",
"company_status": "Company Status",
"allowed_instance_types": "Allowed Instance Types",
"allowed_instance_types": "Active in instance types",
"instance_type_placeholder": "All Instance Types",
"auto_invite_request_accept": "Auto Accept Invite Requests",
"auto_invite_request_accept_tooltip": "Automatically accept invite requests from favorite friends",
"auto_invite_request_accept_off": "Off",
"auto_invite_request_accept_favs": "All Favorites",
"auto_invite_request_accept_selected_favs": "VRCX Favorites",
"change_status_description": "Override status description",
"status_description_placeholder": "Status description (max 32 characters)"
"status_description_placeholder": "Status description (max 32 characters)",
"auto_change_status_groups": "Only count friends from",
"auto_change_status_groups_placeholder": "Choose Groups",
"auto_accept_invite_groups": "Only accept from",
"auto_accept_invite_groups_placeholder": "Choose Groups"
},
"legal_notice": {
"header": "Legal Notice",

View File

@@ -247,10 +247,40 @@ export const useNotificationStore = defineStore('Notification', () => {
}
if (
generalSettingsStore.autoAcceptInviteRequests ===
'Selected Favorites' &&
!friendStore.localFavoriteFriends.has(ref.senderUserId)
'Selected Favorites'
) {
return;
const groups = generalSettingsStore.autoAcceptInviteGroups;
if (groups.length === 0) {
return;
} else {
let found = false;
for (const groupKey of groups) {
if (groupKey.startsWith('local:')) {
const localGroup = groupKey.slice(6);
const localFavs =
favoriteStore.localFriendFavorites.get(localGroup);
if (localFavs && localFavs.has(ref.senderUserId)) {
found = true;
break;
}
} else {
const remoteFavs =
favoriteStore.cachedFavorites.get(groupKey);
if (
remoteFavs &&
remoteFavs.some(
(f) => f.favoriteId === ref.senderUserId
)
) {
found = true;
break;
}
}
}
if (!found) {
return;
}
}
}
if (!checkCanInvite(currentLocation)) {
return;

View File

@@ -39,7 +39,9 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
const autoStateChangeAloneDesc = ref('');
const autoStateChangeCompanyDescEnabled = ref(false);
const autoStateChangeCompanyDesc = ref('');
const autoStateChangeGroups = ref([]);
const autoAcceptInviteRequests = ref('Off');
const autoAcceptInviteGroups = ref([]);
async function initGeneralSettings() {
const [
@@ -64,7 +66,9 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
autoStateChangeAloneDescConfig,
autoStateChangeCompanyDescEnabledConfig,
autoStateChangeCompanyDescConfig,
autoAcceptInviteRequestsConfig
autoStateChangeGroupsStrConfig,
autoAcceptInviteRequestsConfig,
autoAcceptInviteGroupsStrConfig
] = await Promise.all([
configRepository.getBool('VRCX_StartAtWindowsStartup', false),
VRCXStorage.Get('VRCX_StartAsMinimizedState'),
@@ -102,7 +106,9 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
false
),
configRepository.getString('VRCX_autoStateChangeCompanyDesc', ''),
configRepository.getString('VRCX_autoAcceptInviteRequests', 'Off')
configRepository.getString('VRCX_autoStateChangeGroups', '[]'),
configRepository.getString('VRCX_autoAcceptInviteRequests', 'Off'),
configRepository.getString('VRCX_autoAcceptInviteGroups', '[]')
]);
isStartAtWindowsStartup.value = isStartAtWindowsStartupConfig;
@@ -146,7 +152,13 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
autoStateChangeCompanyDescEnabled.value =
autoStateChangeCompanyDescEnabledConfig;
autoStateChangeCompanyDesc.value = autoStateChangeCompanyDescConfig;
autoStateChangeGroups.value = JSON.parse(
autoStateChangeGroupsStrConfig
);
autoAcceptInviteRequests.value = autoAcceptInviteRequestsConfig;
autoAcceptInviteGroups.value = JSON.parse(
autoAcceptInviteGroupsStrConfig
);
}
initGeneralSettings();
@@ -322,6 +334,17 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
autoStateChangeCompanyDesc.value
);
}
/**
* @param {Array} value
*/
function setAutoStateChangeGroups(value) {
autoStateChangeGroups.value = value;
configRepository.setString(
'VRCX_autoStateChangeGroups',
JSON.stringify(autoStateChangeGroups.value)
);
}
/**
* @param {string} value
*/
@@ -333,6 +356,17 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
);
}
/**
* @param {string[]} value
*/
function setAutoAcceptInviteGroups(value) {
autoAcceptInviteGroups.value = value;
configRepository.setString(
'VRCX_autoAcceptInviteGroups',
JSON.stringify(autoAcceptInviteGroups.value)
);
}
function promptProxySettings() {
// Element Plus: prompt(message, title, options)
modalStore
@@ -398,7 +432,9 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
autoStateChangeAloneDesc,
autoStateChangeCompanyDescEnabled,
autoStateChangeCompanyDesc,
autoStateChangeGroups,
autoAcceptInviteRequests,
autoAcceptInviteGroups,
setIsStartAtWindowsStartup,
setIsStartAsMinimizedState,
@@ -420,7 +456,9 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => {
setAutoStateChangeAloneDesc,
setAutoStateChangeCompanyDescEnabled,
setAutoStateChangeCompanyDesc,
setAutoStateChangeGroups,
setAutoAcceptInviteRequests,
setAutoAcceptInviteGroups,
promptProxySettings
};
});

View File

@@ -1616,7 +1616,39 @@ export const useUserStore = defineStore('User', () => {
let withCompany = locationStore.lastLocation.playerList.size > 1;
if (generalSettingsStore.autoStateChangeNoFriends) {
withCompany = locationStore.lastLocation.friendList.size >= 1;
const selectedGroups = generalSettingsStore.autoStateChangeGroups;
if (selectedGroups.length > 0) {
const groupFriendIds = new Set();
for (const ref of favoriteStore.cachedFavorites.values()) {
if (
ref.type === 'friend' &&
selectedGroups.includes(ref.$groupKey)
) {
groupFriendIds.add(ref.favoriteId);
}
}
for (const selectedKey of selectedGroups) {
if (selectedKey.startsWith('local:')) {
const groupName = selectedKey.slice(6);
const userIds =
favoriteStore.localFriendFavorites[groupName];
if (userIds) {
for (let i = 0; i < userIds.length; ++i) {
groupFriendIds.add(userIds[i]);
}
}
}
}
withCompany = false;
for (const friendId of locationStore.lastLocation.friendList.keys()) {
if (groupFriendIds.has(friendId)) {
withCompany = true;
break;
}
}
} else {
withCompany = locationStore.lastLocation.friendList.size >= 1;
}
}
const currentStatus = currentUser.value.status;

View File

@@ -12,6 +12,93 @@
:tooltip="t('view.settings.general.automation.auto_state_change_tooltip')"
@change="setAutoStateChangeEnabled" />
<Field>
<FieldLabel>{{ t('view.settings.general.automation.alone_condition') }}</FieldLabel>
<FieldContent>
<RadioGroup
:model-value="autoStateChangeNoFriends ? 'true' : 'false'"
:disabled="!autoStateChangeEnabled"
class="gap-2 flex"
@update:modelValue="handleAutoStateChangeNoFriendsRadio">
<div class="flex items-center space-x-2">
<RadioGroupItem id="autoStateChangeNoFriends-false" value="false" />
<label for="autoStateChangeNoFriends-false">
{{ t('view.settings.general.automation.alone') }}
</label>
</div>
<div class="flex items-center space-x-2">
<RadioGroupItem id="autoStateChangeNoFriends-true" value="true" />
<label for="autoStateChangeNoFriends-true">
{{ t('view.settings.general.automation.no_friends') }}
</label>
</div>
</RadioGroup>
</FieldContent>
</Field>
<Field>
<FieldLabel>{{ t('view.settings.general.automation.auto_change_status_groups') }}</FieldLabel>
<FieldContent>
<Select
:model-value="autoStateChangeGroups"
:disabled="!autoStateChangeEnabled || !autoStateChangeNoFriends"
multiple
@update:modelValue="setAutoStateChangeGroups">
<SelectTrigger size="sm">
<SelectValue
:placeholder="
t('view.settings.general.automation.auto_change_status_groups_placeholder')
" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem
v-for="group in favoriteFriendGroups"
:key="group.key"
:value="group.key">
{{ group.displayName }}
</SelectItem>
</SelectGroup>
<template v-if="localFriendFavoriteGroups.length">
<SelectSeparator />
<SelectGroup>
<SelectItem
v-for="group in localFriendFavoriteGroups"
:key="'local:' + group"
:value="'local:' + group">
{{ group }}
</SelectItem>
</SelectGroup>
</template>
</SelectContent>
</Select>
</FieldContent>
</Field>
<Field>
<FieldLabel>{{ t('view.settings.general.automation.allowed_instance_types') }}</FieldLabel>
<FieldContent>
<Select
:model-value="autoStateChangeInstanceTypes"
:disabled="!autoStateChangeEnabled"
multiple
@update:modelValue="setAutoStateChangeInstanceTypes">
<SelectTrigger size="sm">
<SelectValue
:placeholder="t('view.settings.general.automation.instance_type_placeholder')" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="instanceType in instanceTypes"
:key="instanceType"
:value="instanceType">
{{ translateAccessType(instanceType) }}
</SelectItem>
</SelectContent>
</Select>
</FieldContent>
</Field>
<Field>
<FieldLabel>{{ t('view.settings.general.automation.alone_status') }}</FieldLabel>
<FieldContent>
@@ -96,48 +183,32 @@
</FieldContent>
</Field>
<Field>
<FieldLabel>{{ t('view.settings.general.automation.allowed_instance_types') }}</FieldLabel>
<FieldContent>
<Select
:model-value="autoStateChangeInstanceTypes"
:disabled="!autoStateChangeEnabled"
multiple
@update:modelValue="setAutoStateChangeInstanceTypes">
<SelectTrigger size="sm">
<SelectValue
:placeholder="t('view.settings.general.automation.instance_type_placeholder')" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="instanceType in instanceTypes"
:key="instanceType"
:value="instanceType">
{{ instanceType }}
</SelectItem>
</SelectContent>
</Select>
</FieldContent>
</Field>
<FieldSeparator></FieldSeparator>
<SimpleSwitch
:label="t('view.settings.general.automation.auto_invite_request_accept')"
:tooltip="t('view.settings.general.automation.auto_invite_request_accept_tooltip')"
:value="autoAcceptInviteRequests !== 'Off'"
@change="handleAutoAcceptInviteSwitch" />
<Field>
<FieldLabel>{{ t('view.settings.general.automation.alone_condition') }}</FieldLabel>
<FieldLabel>{{ t('view.settings.general.automation.auto_invite_request_accept') }}</FieldLabel>
<FieldContent>
<RadioGroup
:model-value="autoStateChangeNoFriends ? 'true' : 'false'"
:disabled="!autoStateChangeEnabled"
:model-value="autoAcceptInviteMode"
:disabled="autoAcceptInviteRequests === 'Off'"
class="gap-2 flex"
@update:modelValue="handleAutoStateChangeNoFriendsRadio">
@update:modelValue="handleAutoAcceptInviteModeChange">
<div class="flex items-center space-x-2">
<RadioGroupItem id="autoStateChangeNoFriends-false" value="false" />
<label for="autoStateChangeNoFriends-false">
{{ t('view.settings.general.automation.alone') }}
<RadioGroupItem id="autoAcceptInvite-all" value="All Favorites" />
<label for="autoAcceptInvite-all">
{{ t('view.settings.general.automation.auto_invite_request_accept_favs') }}
</label>
</div>
<div class="flex items-center space-x-2">
<RadioGroupItem id="autoStateChangeNoFriends-true" value="true" />
<label for="autoStateChangeNoFriends-true">
{{ t('view.settings.general.automation.no_friends') }}
<RadioGroupItem id="autoAcceptInvite-selected" value="Selected Favorites" />
<label for="autoAcceptInvite-selected">
{{ t('view.settings.general.automation.auto_invite_request_accept_selected_favs') }}
</label>
</div>
</RadioGroup>
@@ -145,32 +216,41 @@
</Field>
<Field>
<FieldLabel>
{{ t('view.settings.general.automation.auto_invite_request_accept') }}
<TooltipWrapper
side="top"
:content="t('view.settings.general.automation.auto_invite_request_accept_tooltip')">
<Info class="inline-block" />
</TooltipWrapper>
</FieldLabel>
<FieldLabel>{{ t('view.settings.general.automation.auto_accept_invite_groups') }}</FieldLabel>
<FieldContent>
<ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="autoAcceptInviteRequests"
@update:model-value="setAutoAcceptInviteRequests">
<ToggleGroupItem value="Off">{{
t('view.settings.general.automation.auto_invite_request_accept_off')
}}</ToggleGroupItem>
<ToggleGroupItem value="All Favorites">{{
t('view.settings.general.automation.auto_invite_request_accept_favs')
}}</ToggleGroupItem>
<ToggleGroupItem value="Selected Favorites">{{
t('view.settings.general.automation.auto_invite_request_accept_selected_favs')
}}</ToggleGroupItem>
</ToggleGroup>
<Select
:model-value="autoAcceptInviteGroups"
:disabled="autoAcceptInviteRequests !== 'Selected Favorites'"
multiple
@update:modelValue="setAutoAcceptInviteGroups">
<SelectTrigger size="sm">
<SelectValue
:placeholder="
t('view.settings.general.automation.auto_accept_invite_groups_placeholder')
" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem
v-for="group in favoriteFriendGroups"
:key="group.key"
:value="group.key">
{{ group.displayName }}
</SelectItem>
</SelectGroup>
<template v-if="localFriendFavoriteGroups.length">
<SelectSeparator />
<SelectGroup>
<SelectItem
v-for="group in localFriendFavoriteGroups"
:key="'local:' + group"
:value="'local:' + group">
{{ group }}
</SelectItem>
</SelectGroup>
</template>
</SelectContent>
</Select>
</FieldContent>
</Field>
</FieldGroup>
@@ -179,18 +259,25 @@
</template>
<script setup>
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
SelectValue
} from '@/components/ui/select';
import { Field, FieldContent, FieldGroup, FieldLabel, FieldSeparator } from '@/components/ui/field';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { Info } from 'lucide-vue-next';
import { Input } from '@/components/ui/input';
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useGeneralSettingsStore } from '../../../stores';
import { useFavoriteStore, useGeneralSettingsStore } from '../../../stores';
import { accessTypeLocaleKeyMap } from '../../../shared/constants';
import SimpleSwitch from '../../Settings/components/SimpleSwitch.vue';
@@ -215,9 +302,14 @@
autoStateChangeAloneDesc,
autoStateChangeCompanyDescEnabled,
autoStateChangeCompanyDesc,
autoAcceptInviteRequests
autoStateChangeGroups,
autoAcceptInviteRequests,
autoAcceptInviteGroups
} = storeToRefs(generalSettingsStore);
const favoriteStore = useFavoriteStore();
const { favoriteFriendGroups, localFriendFavoriteGroups } = storeToRefs(favoriteStore);
const {
setAutoStateChangeEnabled,
setAutoStateChangeAloneStatus,
@@ -228,7 +320,9 @@
setAutoStateChangeAloneDesc,
setAutoStateChangeCompanyDescEnabled,
setAutoStateChangeCompanyDesc,
setAutoAcceptInviteRequests
setAutoStateChangeGroups,
setAutoAcceptInviteRequests,
setAutoAcceptInviteGroups
} = generalSettingsStore;
const instanceTypes = computed(() => [
@@ -242,6 +336,23 @@
'groupOnly'
]);
const instanceTypeToMapKey = {
groupOnly: 'groupMembers'
};
function translateAccessType(accessTypeNameRaw) {
const mapKey = instanceTypeToMapKey[accessTypeNameRaw] || accessTypeNameRaw;
const key = accessTypeLocaleKeyMap[mapKey];
if (!key) {
return accessTypeNameRaw;
}
if (mapKey === 'groupPublic' || mapKey === 'groupPlus' || mapKey === 'groupMembers') {
const groupKey = accessTypeLocaleKeyMap['group'];
return t(groupKey) + ' ' + t(key);
}
return t(key);
}
function handleAutoStateChangeNoFriendsRadio(value) {
const nextValue = value === 'true';
if (nextValue !== autoStateChangeNoFriends.value) {
@@ -249,6 +360,25 @@
}
}
const autoAcceptInviteMode = computed(() => {
if (autoAcceptInviteRequests.value === 'Off') {
return 'All Favorites';
}
return autoAcceptInviteRequests.value;
});
function handleAutoAcceptInviteSwitch(enabled) {
if (enabled) {
setAutoAcceptInviteRequests(autoAcceptInviteMode.value);
} else {
setAutoAcceptInviteRequests('Off');
}
}
function handleAutoAcceptInviteModeChange(value) {
setAutoAcceptInviteRequests(value);
}
function closeDialog() {
emit('close');
}