Skip to content

Commit 66aca69

Browse files
authored
feat: update homebase name
* feat: add new page | Homebase Name * refactor: extract accounts/tags selector component * refactor: move content * feat: update homebase name
1 parent ce3d456 commit 66aca69

File tree

19 files changed

+698
-82
lines changed

19 files changed

+698
-82
lines changed

src/components/menu/sidebar.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ export function SidebarMenu({
9898
Save Quests
9999
</Link>
100100
</li>
101+
<li className="item-stw-operations">
102+
<Link
103+
to="/stw-operations/homebase-name"
104+
className={currentClassNameHover}
105+
activeProps={{
106+
className: cn(activeClassName),
107+
}}
108+
onClick={goToPage}
109+
>
110+
Homebase Name
111+
</Link>
112+
</li>
101113
</ul>
102114
</div>
103115
<Title className="pb-0">Account Management</Title>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import type { SelectOption } from '../../../components/ui/third-party/extended/input-tags'
2+
3+
import { defaultColor } from '../../../config/constants/colors'
4+
5+
import { useGetAccounts } from '../../../hooks/accounts'
6+
import { useGetGroups } from '../../../hooks/groups'
7+
import { useGetTags } from '../../../hooks/tags'
8+
9+
import { parseDisplayName } from '../../../lib/utils'
10+
11+
export function useAccountSelectorData({
12+
selectedAccounts,
13+
selectedTags,
14+
}: {
15+
selectedAccounts: Array<string>
16+
selectedTags: Array<string>
17+
}) {
18+
const { accountList, accountsArray } = useGetAccounts()
19+
const { groupsArray } = useGetGroups()
20+
const { tagsArray } = useGetTags()
21+
22+
const areThereAccounts = accountsArray.length > 0
23+
const accounts: Array<SelectOption> = accountsArray.map((account) => {
24+
const label = parseDisplayName(account)
25+
26+
return {
27+
label,
28+
color: defaultColor,
29+
value: account.accountId,
30+
}
31+
})
32+
const tags: Array<SelectOption> = tagsArray.map(([name, color]) => ({
33+
color: color ?? defaultColor,
34+
label: name,
35+
value: name,
36+
}))
37+
38+
const parsedSelectedAccounts: Array<SelectOption> = selectedAccounts.map(
39+
(accountId) => {
40+
const selected = accounts.find((item) => item.value === accountId)!
41+
42+
return {
43+
color: defaultColor,
44+
label: selected.label,
45+
value: selected.value,
46+
}
47+
}
48+
)
49+
const parsedSelectedTags: Array<SelectOption> = selectedTags.map(
50+
(tagName) => {
51+
const selected = tags.find((item) => item.value === tagName)!
52+
53+
return {
54+
color: selected.color,
55+
label: selected.label,
56+
value: selected.value,
57+
}
58+
}
59+
)
60+
61+
const isSelectedEmpty =
62+
parsedSelectedAccounts.length === 0 && parsedSelectedTags.length === 0
63+
64+
const getAccounts = () => {
65+
const currentTags = parsedSelectedTags.map(({ value }) => value)
66+
const accountIds = [
67+
...new Set([
68+
...parsedSelectedAccounts.map(({ value }) => value),
69+
...groupsArray
70+
.filter(([, itemTags]) =>
71+
currentTags.some((currentTag) => itemTags.includes(currentTag))
72+
)
73+
.map(([key]) => key),
74+
]),
75+
]
76+
return accountIds.map((accountId) => accountList[accountId])
77+
}
78+
79+
return {
80+
accounts,
81+
areThereAccounts,
82+
isSelectedEmpty,
83+
parsedSelectedAccounts,
84+
parsedSelectedTags,
85+
tags,
86+
87+
getAccounts,
88+
}
89+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { SelectOption } from '../../ui/third-party/extended/input-tags'
2+
3+
import { SeparatorWithTitle } from '../../ui/extended/separator'
4+
import { InputTags } from '../../ui/third-party/extended/input-tags'
5+
6+
export function AccountSelectors({
7+
accounts,
8+
tags,
9+
onUpdateAccounts,
10+
onUpdateTags,
11+
}: {
12+
accounts: {
13+
options: Array<SelectOption>
14+
value: Array<SelectOption>
15+
}
16+
tags: {
17+
options: Array<SelectOption>
18+
value: Array<SelectOption>
19+
}
20+
onUpdateAccounts?: (value: Array<SelectOption>) => void
21+
onUpdateTags?: (value: Array<SelectOption>) => void
22+
}) {
23+
return (
24+
<div className="grid gap-4">
25+
<InputTags
26+
placeholder="Select some accounts..."
27+
options={accounts.options}
28+
value={accounts.value}
29+
onChange={onUpdateAccounts ?? (() => {})}
30+
/>
31+
<SeparatorWithTitle>Or</SeparatorWithTitle>
32+
<InputTags
33+
placeholder="Select some tags..."
34+
options={tags.options}
35+
value={tags.value}
36+
onChange={onUpdateTags ?? (() => {})}
37+
/>
38+
</div>
39+
)
40+
}

src/config/constants/main-process.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ export enum ElectronAPIEventKeys {
8484
SetSaveQuests = 'save-quests:set',
8585
SaveQuestsNotification = 'save-quests:notification',
8686

87+
SetHombaseName = 'homebase-name:set',
88+
HomebaseNameNotification = 'homebase-name:notification',
89+
8790
/**
8891
* Party
8992
*/
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { SelectOption } from '../../components/ui/third-party/extended/input-tags'
2+
3+
import { useShallow } from 'zustand/react/shallow'
4+
5+
import { useHomebaseNameStore } from '../../state/stw-operations/homebase-name'
6+
7+
export function useGetHomebaseNameData() {
8+
const { accounts, tags } = useHomebaseNameStore(
9+
useShallow((state) => ({
10+
accounts: state.accounts,
11+
tags: state.tags,
12+
}))
13+
)
14+
15+
return {
16+
selectedAccounts: accounts,
17+
selectedTags: tags,
18+
}
19+
}
20+
21+
export function useGetHomebaseNameActions() {
22+
const { updateAccounts, updateTags } = useHomebaseNameStore(
23+
useShallow((state) => ({
24+
updateAccounts: state.updateAccounts,
25+
updateTags: state.updateTags,
26+
}))
27+
)
28+
29+
const rawHomebaseNameUpdateAccounts = (value: Array<string>) => {
30+
updateAccounts(value)
31+
}
32+
const homebaseNameUpdateAccounts = (value: Array<SelectOption>) => {
33+
updateAccounts(value.map((item) => item.value))
34+
}
35+
36+
const rawHomebaseNameUpdateTags = (value: Array<string>) => {
37+
updateTags(value)
38+
}
39+
const homebaseNameUpdateTags = (value: Array<SelectOption>) => {
40+
updateTags(value.map((item) => item.value))
41+
}
42+
43+
return {
44+
rawHomebaseNameUpdateAccounts,
45+
rawHomebaseNameUpdateTags,
46+
homebaseNameUpdateAccounts,
47+
homebaseNameUpdateTags,
48+
}
49+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { AccountData } from '../../../types/accounts'
2+
3+
import { BrowserWindow } from 'electron'
4+
5+
import { ElectronAPIEventKeys } from '../../../config/constants/main-process'
6+
7+
import { NotificationHomebaseNameResponse } from '../../../kernel/preload-actions/mcp'
8+
9+
import { Authentication } from '../authentication'
10+
11+
import { setHomebaseName } from '../../../services/endpoints/mcp'
12+
13+
export class MCPHomebaseName {
14+
static async update(
15+
currentWindow: BrowserWindow,
16+
accounts: Array<AccountData>,
17+
homebaseName: string
18+
) {
19+
try {
20+
const response = await Promise.allSettled(
21+
accounts.map(async (account) => {
22+
const result: NotificationHomebaseNameResponse = {
23+
errorMessage: undefined,
24+
}
25+
26+
try {
27+
const accessToken =
28+
await Authentication.verifyAccessToken(account)
29+
30+
if (!accessToken) {
31+
result.errorMessage = 'Unknown Error'
32+
33+
return result
34+
}
35+
36+
const { accountId } = account
37+
38+
const mcpResponse = await setHomebaseName({
39+
accessToken,
40+
accountId,
41+
homebaseName,
42+
})
43+
44+
if (!mcpResponse.data.profileChanges?.[0]) {
45+
result.errorMessage = 'Unknown Error'
46+
}
47+
48+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
49+
} catch (error: any) {
50+
if (error?.response?.data.errorMessage) {
51+
const errorMessage =
52+
error.response?.data?.errorMessage ?? 'Unknown Error'
53+
54+
result.errorMessage = errorMessage
55+
} else {
56+
result.errorMessage = 'Unknown Error'
57+
}
58+
}
59+
60+
return result
61+
})
62+
)
63+
const filteredResponse = response
64+
.filter((result) => result.status === 'fulfilled')
65+
.map((result) => result.value)
66+
const commonError = filteredResponse.find(
67+
(item) => item.errorMessage
68+
)
69+
70+
currentWindow.webContents.send(
71+
ElectronAPIEventKeys.HomebaseNameNotification,
72+
(commonError
73+
? {
74+
errorMessage: commonError.errorMessage,
75+
}
76+
: {}) as NotificationHomebaseNameResponse
77+
)
78+
79+
return
80+
} catch (error) {
81+
//
82+
}
83+
84+
currentWindow.webContents.send(
85+
ElectronAPIEventKeys.HomebaseNameNotification,
86+
{
87+
errorMessage: 'Unknown Error',
88+
} as NotificationHomebaseNameResponse
89+
)
90+
}
91+
}

src/kernel/core/mcp/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from './client-quest-login'
1+
export { MCPClientQuestLogin } from './client-quest-login'
2+
export { MCPHomebaseName } from './homebase-name'

src/kernel/main.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { AntiCheatProvider } from './core/anti-cheat-provider'
1919
import { Authentication } from './core/authentication'
2020
import { ClaimRewards } from './core/claim-rewards'
2121
import { FortniteLauncher } from './core/launcher'
22-
import { MCPClientQuestLogin } from './core/mcp'
22+
import { MCPClientQuestLogin, MCPHomebaseName } from './core/mcp'
2323
import { Manifest } from './core/manifest'
2424
import { Party } from './core/party'
2525
import { AccountsManager } from './startup/accounts'
@@ -234,6 +234,13 @@ app.on('ready', async () => {
234234
}
235235
)
236236

237+
ipcMain.on(
238+
ElectronAPIEventKeys.SetHombaseName,
239+
async (_, accounts: Array<AccountData>, homebaseName: string) => {
240+
await MCPHomebaseName.update(currentWindow, accounts, homebaseName)
241+
}
242+
)
243+
237244
/**
238245
* Party
239246
*/

src/kernel/preload-actions/mcp/claim-rewards.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { IpcRendererEvent } from 'electron'
2-
// import type { AccountData } from '../../../types/accounts'
32
import type { RewardsNotification } from '../../../types/notifications'
43

54
import { ipcRenderer } from 'electron'
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { IpcRendererEvent } from 'electron'
2+
import type { AccountData } from '../../../types/accounts'
3+
4+
import { ipcRenderer } from 'electron'
5+
6+
import { ElectronAPIEventKeys } from '../../../config/constants/main-process'
7+
8+
export type NotificationHomebaseNameResponse = {
9+
errorMessage?: string
10+
}
11+
12+
export function setHomebaseName(
13+
accounts: Array<AccountData>,
14+
homebaseName: string
15+
) {
16+
ipcRenderer.send(
17+
ElectronAPIEventKeys.SetHombaseName,
18+
accounts,
19+
homebaseName
20+
)
21+
}
22+
23+
export function notificationHomebaseName(
24+
callback: (response: NotificationHomebaseNameResponse) => Promise<void>
25+
) {
26+
const customCallback = (
27+
_: IpcRendererEvent,
28+
response: NotificationHomebaseNameResponse
29+
) => {
30+
callback(response).catch(() => {})
31+
}
32+
const rendererInstance = ipcRenderer.on(
33+
ElectronAPIEventKeys.HomebaseNameNotification,
34+
customCallback
35+
)
36+
37+
return {
38+
removeListener: () =>
39+
rendererInstance.removeListener(
40+
ElectronAPIEventKeys.HomebaseNameNotification,
41+
customCallback
42+
),
43+
}
44+
}

0 commit comments

Comments
 (0)