diff --git a/src/App.vue b/src/App.vue index b9855be0..a756848f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -104,7 +104,7 @@ import LaunchDialog from './components/dialogs/LaunchDialog.vue'; import LaunchOptionsDialog from './views/Settings/dialogs/LaunchOptionsDialog.vue'; import Login from './views/Login/Login.vue'; - import MacOSTitleBar from './components/TitleBar/MacOSTitleBar.vue'; + import MacOSTitleBar from './components/MacOSTitleBar.vue'; import NavMenu from './components/NavMenu.vue'; import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue'; import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue'; diff --git a/src/components/Location.vue b/src/components/Location.vue index c6663e99..fc53af2e 100644 --- a/src/components/Location.vue +++ b/src/components/Location.vue @@ -53,7 +53,10 @@ type: Boolean, default: true }, - isOpenPreviousInstanceInfoDialog: Boolean + isOpenPreviousInstanceInfoDialog: { + type: Boolean, + default: false + } }); const text = ref(''); diff --git a/src/components/TitleBar/MacOSTitleBar.vue b/src/components/MacOSTitleBar.vue similarity index 100% rename from src/components/TitleBar/MacOSTitleBar.vue rename to src/components/MacOSTitleBar.vue diff --git a/src/components/NavMenu.vue b/src/components/NavMenu.vue index 4a7bc2d8..2a751b8a 100644 --- a/src/components/NavMenu.vue +++ b/src/components/NavMenu.vue @@ -64,6 +64,7 @@ const navItems = [ { index: 'feed', icon: 'ri-rss-line', tooltip: 'nav_tooltip.feed' }, + { index: 'friend', icon: 'ri-group-line', tooltip: 'nav_tooltip.friend' }, { index: 'gameLog', icon: 'ri-history-line', tooltip: 'nav_tooltip.game_log' }, { index: 'playerList', icon: 'ri-group-3-line', tooltip: 'nav_tooltip.player_list' }, { index: 'search', icon: 'ri-search-line', tooltip: 'nav_tooltip.search' }, diff --git a/src/localization/en/en.json b/src/localization/en/en.json index 3683ace7..579e0206 100644 --- a/src/localization/en/en.json +++ b/src/localization/en/en.json @@ -2,6 +2,7 @@ "language": "English (en)", "translator": "-", "nav_tooltip": { + "friend": "Friend", "feed": "Feed", "game_log": "Game Log", "player_list": "Player List", diff --git a/src/plugin/router.js b/src/plugin/router.js index af9e6eb9..61c6582b 100644 --- a/src/plugin/router.js +++ b/src/plugin/router.js @@ -3,6 +3,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'; import Charts from './../views/Charts/Charts.vue'; import Favorites from './../views/Favorites/Favorites.vue'; import Feed from './../views/Feed/Feed.vue'; +import Friend from './../views/Friend/Friend.vue'; import FriendList from './../views/FriendList/FriendList.vue'; import FriendLog from './../views/FriendLog/FriendLog.vue'; import GameLog from './../views/GameLog/GameLog.vue'; @@ -14,6 +15,7 @@ import Settings from './../views/Settings/Settings.vue'; import Tools from './../views/Tools/Tools.vue'; const routes = [ + { path: '/friend', name: 'friend', component: Friend }, { path: '/feed', name: 'feed', component: Feed }, { path: '/gamelog', name: 'gameLog', component: GameLog }, { path: '/playerlist', name: 'playerList', component: PlayerList }, diff --git a/src/shared/utils/location.js b/src/shared/utils/location.js index 850c2abb..9ba19d1f 100644 --- a/src/shared/utils/location.js +++ b/src/shared/utils/location.js @@ -1,3 +1,6 @@ +import { isRealInstance } from './instance.js'; +import { useLocationStore } from '../../stores/location.js'; + /** * * @param {string} location @@ -141,4 +144,28 @@ function parseLocation(tag) { return ctx; } -export { parseLocation, displayLocation }; +function getFriendsLocations(friendsArr) { + const locaationStore = useLocationStore(); + // prevent the instance title display as "Traveling". + if (!friendsArr?.length) { + return ''; + } + for (const friend of friendsArr) { + if (isRealInstance(friend.ref?.location)) { + return friend.ref.location; + } + } + for (const friend of friendsArr) { + if (isRealInstance(friend.ref?.travelingToLocation)) { + return friend.ref.travelingToLocation; + } + } + for (const friend of friendsArr) { + if (locaationStore.lastLocation.friendList.has(friend.id)) { + return locaationStore.lastLocation.location; + } + } + return friendsArr[0].ref?.location; +} + +export { parseLocation, displayLocation, getFriendsLocations }; diff --git a/src/stores/friend.js b/src/stores/friend.js index 73b032f8..a60c3f27 100644 --- a/src/stores/friend.js +++ b/src/stores/friend.js @@ -23,6 +23,7 @@ import { useFavoriteStore } from './favorite'; import { useFeedStore } from './feed'; import { useGeneralSettingsStore } from './settings/general'; import { useGroupStore } from './group'; +import { useLocationStore } from './location'; import { useNotificationStore } from './notification'; import { useSharedFeedStore } from './sharedFeed'; import { useUiStore } from './ui'; @@ -45,6 +46,7 @@ export const useFriendStore = defineStore('Friend', () => { const sharedFeedStore = useSharedFeedStore(); const updateLoopStore = useUpdateLoopStore(); const authStore = useAuthStore(); + const locationStore = useLocationStore(); const { t } = useI18n(); const state = reactive({ @@ -135,6 +137,49 @@ export const useFriendStore = defineStore('Friend', () => { ); }); + const friendsInSameInstance = computed(() => { + const friendsList = {}; + + const allFriends = [...vipFriends.value, ...onlineFriends.value]; + allFriends.forEach((friend) => { + if (!friend.ref?.$location) { + return; + } + + let locationTag = friend.ref.$location.tag; + if ( + !friend.ref.$location.isRealInstance && + locationStore.lastLocation.friendList.has(friend.id) + ) { + locationTag = locationStore.lastLocation.location; + } + const isReal = isRealInstance(locationTag); + if (!isReal) { + return; + } + + if (!friendsList[locationTag]) { + friendsList[locationTag] = []; + } + friendsList[locationTag].push(friend); + }); + + const sortedFriendsList = []; + for (const group of Object.values(friendsList)) { + if (group.length > 1) { + sortedFriendsList.push( + group.sort( + getFriendsSortFunction( + appearanceSettingsStore.sidebarSortMethods + ) + ) + ); + } + } + + return sortedFriendsList.sort((a, b) => b.length - a.length); + }); + watch( () => watchState.isLoggedIn, (isLoggedIn) => { @@ -1570,6 +1615,7 @@ export const useFriendStore = defineStore('Friend', () => { onlineFriends, activeFriends, offlineFriends, + friendsInSameInstance, localFavoriteFriends, isRefreshFriendsLoading, diff --git a/src/stores/settings/appearance.js b/src/stores/settings/appearance.js index 6c2078e9..66775484 100644 --- a/src/stores/settings/appearance.js +++ b/src/stores/settings/appearance.js @@ -82,6 +82,7 @@ export const useAppearanceSettingsStore = defineStore( const isSideBarTabShow = computed(() => { const currentRouteName = router.currentRoute.value?.name; return !( + currentRouteName === 'friend' || currentRouteName === 'friendList' || currentRouteName === 'charts' ); diff --git a/src/views/Friend/Friend.vue b/src/views/Friend/Friend.vue new file mode 100644 index 00000000..b218ff7e --- /dev/null +++ b/src/views/Friend/Friend.vue @@ -0,0 +1,433 @@ + + + + + + Card Scale + + + + + + + + + + {{ + group.instanceId + }} + {{ group.friends.length }} + + + + + + + No matching friends + + + + + + No matching friends + + + + + + Loading more... + + + + + + + + diff --git a/src/views/Friend/components/FriendCard.vue b/src/views/Friend/components/FriendCard.vue new file mode 100644 index 00000000..a1a37eb1 --- /dev/null +++ b/src/views/Friend/components/FriendCard.vue @@ -0,0 +1,234 @@ + + + + + + {{ avatarFallback }} + + + + {{ friend.name }} + + + + + {{ friend.ref?.statusDescription || ' ' }} + + + + + + + + + + + diff --git a/src/views/Sidebar/Sidebar.vue b/src/views/Sidebar/Sidebar.vue index 863d252e..8d83cc54 100644 --- a/src/views/Sidebar/Sidebar.vue +++ b/src/views/Sidebar/Sidebar.vue @@ -101,7 +101,7 @@ const { friends, isRefreshFriendsLoading, onlineFriendCount } = storeToRefs(useFriendStore()); const { refreshFriendsList, confirmDeleteFriend } = useFriendStore(); - const { quickSearchRemoteMethod, quickSearchChange, directAccessPaste } = useSearchStore(); + const { quickSearchRemoteMethod, quickSearchChange } = useSearchStore(); const { quickSearchItems } = storeToRefs(useSearchStore()); const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore()); const { logout } = useAuthStore(); diff --git a/src/components/FriendItem.vue b/src/views/Sidebar/components/FriendItem.vue similarity index 98% rename from src/components/FriendItem.vue rename to src/views/Sidebar/components/FriendItem.vue index 72d9b395..e2fd11ce 100644 --- a/src/components/FriendItem.vue +++ b/src/views/Sidebar/components/FriendItem.vue @@ -63,8 +63,8 @@ import { storeToRefs } from 'pinia'; import { useI18n } from 'vue-i18n'; - import { useAppearanceSettingsStore, useFriendStore } from '../stores'; - import { userImage, userStatusClass } from '../shared/utils'; + import { useAppearanceSettingsStore, useFriendStore } from '../../../stores'; + import { userImage, userStatusClass } from '../../../shared/utils'; const props = defineProps({ friend: { type: Object, required: true }, diff --git a/src/views/Sidebar/components/FriendsSidebar.vue b/src/views/Sidebar/components/FriendsSidebar.vue index 8e72cf1b..a898f414 100644 --- a/src/views/Sidebar/components/FriendsSidebar.vue +++ b/src/views/Sidebar/components/FriendsSidebar.vue @@ -183,16 +183,19 @@ useLocationStore, useUserStore } from '../../../stores'; - import { getFriendsSortFunction, isRealInstance, userImage, userStatusClass } from '../../../shared/utils'; + import { isRealInstance, userImage, userStatusClass } from '../../../shared/utils'; + import { getFriendsLocations } from '../../../shared/utils/location.js'; - import FriendItem from '../../../components/FriendItem.vue'; + import FriendItem from './FriendItem.vue'; import configRepository from '../../../service/config'; const emit = defineEmits(['confirm-delete-friend']); const { t } = useI18n(); - const { vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore()); - const { isSidebarGroupByInstance, isHideFriendsInSameInstance, isSidebarDivideByFriendGroup, sidebarSortMethods } = + const friendStore = useFriendStore(); + const { vipFriends, onlineFriends, activeFriends, offlineFriends, friendsInSameInstance } = + storeToRefs(friendStore); + const { isSidebarGroupByInstance, isHideFriendsInSameInstance, isSidebarDivideByFriendGroup } = storeToRefs(useAppearanceSettingsStore()); const { gameLogDisabled } = storeToRefs(useAdvancedSettingsStore()); const { showUserDialog } = useUserStore(); @@ -210,40 +213,6 @@ loadFriendsGroupStates(); - const friendsInSameInstance = computed(() => { - const friendsList = {}; - - const allFriends = [...vipFriends.value, ...onlineFriends.value]; - allFriends.forEach((friend) => { - if (!friend.ref?.$location) { - return; - } - - let locationTag = friend.ref.$location.tag; - if (!friend.ref.$location.isRealInstance && lastLocation.value.friendList.has(friend.id)) { - locationTag = lastLocation.value.location; - } - const isReal = isRealInstance(locationTag); - if (!isReal) { - return; - } - - if (!friendsList[locationTag]) { - friendsList[locationTag] = []; - } - friendsList[locationTag].push(friend); - }); - - const sortedFriendsList = []; - for (const group of Object.values(friendsList)) { - if (group.length > 1) { - sortedFriendsList.push(group.sort(getFriendsSortFunction(sidebarSortMethods.value))); - } - } - - return sortedFriendsList.sort((a, b) => b.length - a.length); - }); - const sameInstanceFriendId = computed(() => { const sameInstanceFriendId = new Set(); for (const item of friendsInSameInstance.value) { @@ -332,29 +301,6 @@ configRepository.setBool('VRCX_sidebarGroupByInstanceCollapsed', isSidebarGroupByInstanceCollapsed.value); } - function getFriendsLocations(friendsArr) { - // prevent the instance title display as "Traveling". - if (!friendsArr?.length) { - return ''; - } - for (const friend of friendsArr) { - if (isRealInstance(friend.ref?.location)) { - return friend.ref.location; - } - } - for (const friend of friendsArr) { - if (isRealInstance(friend.ref?.travelingToLocation)) { - return friend.ref.travelingToLocation; - } - } - for (const friend of friendsArr) { - if (lastLocation.value.friendList.has(friend.id)) { - return lastLocation.value.location; - } - } - return friendsArr[0].ref?.location; - } - function confirmDeleteFriend(friend) { emit('confirm-delete-friend', friend); }