Skip to content

Commit 4f9c77b

Browse files
Merge pull request #394 from MaaAssistantArknights/dev
Release to Production
2 parents ba43aee + 1754940 commit 4f9c77b

File tree

9 files changed

+212
-109
lines changed

9 files changed

+212
-109
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 2025-04-10
2+
3+
- 修改作业时会在新标签页打开 [@guansss](https://github.com/guansss)
4+
- 对搜索面板进行了正面优化(确信) [@guansss](https://github.com/guansss)
5+
- 广告更新
6+
17
## 2025-04-04
28

39
- 优化了首页列表的性能 [@Aliothmoon](https://github.com/Aliothmoon)

public/ad_leidian.jpg

404 KB
Loading

public/ads_mumu.jpg

-283 KB
Binary file not shown.

src/apis/operation-set.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useAtomValue } from 'jotai'
12
import { noop } from 'lodash-es'
23
import {
34
CopilotSetPageRes,
@@ -12,6 +13,7 @@ import { OperationSetApi } from 'utils/maa-copilot-client'
1213
import { useSWRRefresh } from 'utils/swr'
1314

1415
import { parseShortCode } from '../models/shortCode'
16+
import { authAtom } from '../store/auth'
1517

1618
export type OrderBy = 'views' | 'hot' | 'id'
1719

@@ -29,6 +31,7 @@ export function useOperationSets({
2931
disabled,
3032
suspense,
3133
}: UseOperationSetsParams) {
34+
const auth = useAtomValue(authAtom)
3235
const {
3336
data: pages,
3437
error,
@@ -49,7 +52,7 @@ export function useOperationSets({
4952
limit: 50,
5053
page: pageIndex + 1,
5154
keyword,
52-
creatorId,
55+
creatorId: creatorId === 'me' ? auth.userId : creatorId,
5356
} satisfies CopilotSetQuery,
5457
]
5558
},

src/components/LevelSelect.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Button, Classes, MenuDivider, MenuItem } from '@blueprintjs/core'
2+
import { getCreateNewItem } from '@blueprintjs/select'
23

34
import clsx from 'clsx'
45
import Fuse from 'fuse.js'
5-
import { FC, useMemo } from 'react'
6+
import { FC, useEffect, useMemo, useState } from 'react'
67

78
import { useLevels } from '../apis/level'
8-
import { createCustomLevel, isHardMode } from '../models/level'
9+
import { createCustomLevel, isCustomLevel, isHardMode } from '../models/level'
910
import { Level } from '../models/operation'
1011
import { useDebouncedQuery } from '../utils/useDebouncedQuery'
1112
import { Select } from './Select'
@@ -40,7 +41,17 @@ export const LevelSelect: FC<LevelSelectProps> = ({
4041
)
4142

4243
const { query, debouncedQuery, updateQuery, onOptionMouseDown } =
43-
useDebouncedQuery()
44+
useDebouncedQuery({
45+
onDebouncedQueryChange: (value) => {
46+
if (value !== debouncedQuery) {
47+
// 清空 activeItem,之后会自动设置为第一项
48+
setActiveItem(null)
49+
}
50+
},
51+
})
52+
const [activeItem, setActiveItem] = useState<Level | 'createNewItem' | null>(
53+
null,
54+
)
4455

4556
const selectedLevel = useMemo(() => {
4657
const level = levels.find((el) => el.stageId === value)
@@ -103,10 +114,26 @@ export const LevelSelect: FC<LevelSelectProps> = ({
103114
: levels
104115
}, [debouncedQuery, selectedLevel, levels, fuse])
105116

117+
useEffect(() => {
118+
if (!selectedLevel) {
119+
setActiveItem(null)
120+
} else if (isCustomLevel(selectedLevel)) {
121+
setActiveItem('createNewItem')
122+
} else {
123+
setActiveItem(selectedLevel)
124+
}
125+
}, [selectedLevel])
126+
106127
return (
107128
<Select<Level>
108129
items={levels}
109130
itemListPredicate={() => filteredLevels}
131+
activeItem={
132+
activeItem === 'createNewItem' ? getCreateNewItem() : activeItem
133+
}
134+
onActiveItemChange={(item, isCreateNewItem) => {
135+
setActiveItem(isCreateNewItem ? 'createNewItem' : item)
136+
}}
110137
query={query}
111138
onQueryChange={(query) => updateQuery(query, false)}
112139
onReset={() => onChange('')}
@@ -132,19 +159,22 @@ export const LevelSelect: FC<LevelSelectProps> = ({
132159
}
133160
selectedItem={selectedLevel}
134161
onItemSelect={(level) => {
135-
// 重置 query 以显示同类关卡
136-
updateQuery('', true)
162+
if (!isCustomLevel(level)) {
163+
// 重置 query 以显示同类关卡
164+
updateQuery('', true)
165+
}
137166
onChange(level.stageId)
138167
}}
139168
createNewItemFromQuery={(query) => createCustomLevel(query)}
140169
createNewItemRenderer={(query, active, handleClick) => (
141170
<MenuItem
142171
key="create-new-item"
143172
roleStructure="listoption"
144-
text={`使用自定义关卡名 "${query}"`}
173+
className={clsx(active && Classes.ACTIVE)}
174+
text={`直接搜索关卡 "${query}"`}
145175
icon="text-highlight"
146176
onClick={handleClick}
147-
active={active}
177+
selected={selectedLevel && isCustomLevel(selectedLevel)}
148178
/>
149179
)}
150180
inputProps={{

src/components/Select.tsx

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
11
import { Button, ButtonProps, Label } from '@blueprintjs/core'
2-
import { Select2, Select2Props } from '@blueprintjs/select'
2+
import {
3+
QueryList,
4+
Select2,
5+
Select2Props,
6+
isCreateNewItem,
7+
} from '@blueprintjs/select'
38

49
import clsx from 'clsx'
510

611
interface SelectProps<T> extends Select2Props<T> {
712
selectedItem?: T
813
resetButtonProps?: ButtonProps
14+
canReset?: boolean
915
onReset?: () => void
1016
}
1117

1218
export const Select = <T,>({
1319
className,
1420
selectedItem,
21+
canReset,
1522
onReset,
1623
resetButtonProps,
1724
inputProps,
1825
...props
1926
}: SelectProps<T>) => {
20-
const canReset = selectedItem !== undefined
27+
canReset ??= selectedItem !== undefined
2128

2229
return (
2330
<Label className={clsx('!flex items-center !mb-0', className)}>
@@ -32,6 +39,8 @@ export const Select = <T,>({
3239
),
3340
}}
3441
{...props}
42+
// 传给 QueryList,给下面的补丁用
43+
{...{ _parentType: 'Select' }}
3544
/>
3645
{canReset && (
3746
<Button
@@ -50,3 +59,54 @@ export const Select = <T,>({
5059
</Label>
5160
)
5261
}
62+
63+
// 修复 BP 的远古 bug:https://github.com/palantir/blueprint/issues/3751
64+
// 补丁只对 Select 组件启用,因为不知道对 Suggest 和 MultiSelect 是否有效,先不管了
65+
66+
const originalSetQuery =
67+
(QueryList.prototype.setQuery as any)._original ??
68+
QueryList.prototype.setQuery
69+
QueryList.prototype.setQuery = function (...args) {
70+
;(this as any)._isCallingSetQuery = true
71+
originalSetQuery.apply(this, args)
72+
;(this as any)._isCallingSetQuery = false
73+
}
74+
;(QueryList.prototype.setQuery as any)._original = originalSetQuery
75+
76+
const originalGetActiveIndex =
77+
(QueryList.prototype['getActiveIndex'] as any)._original ??
78+
QueryList.prototype['getActiveIndex']
79+
QueryList.prototype['getActiveIndex'] = function (items) {
80+
if (
81+
this.props['_parentType'] === 'Select' &&
82+
(this as any)._isCallingSetQuery
83+
) {
84+
const activeItem =
85+
this.props.activeItem === undefined
86+
? this.state.activeItem
87+
: this.props.activeItem
88+
89+
if (isCreateNewItem(activeItem)) {
90+
// bug 1:如果 activeItem 是 createNewItem,QueryList 会直接将 activeItem 刷新为第一个 item,
91+
// 为了阻止这种行为,这里返回 0 以绕过 activeIndex < 0 的判断:https://github.com/palantir/blueprint/blob/e365c08b2f133ad102de8cdf687b9609e824d96c/packages/select/src/components/query-list/queryList.tsx#L328
92+
return 0
93+
}
94+
95+
return originalGetActiveIndex.call(
96+
{
97+
props: this.props,
98+
state: {
99+
...this.state,
100+
101+
// bug 2:QueryList 在 setState({ activeItem: props.activeItem }) 之后并未等待 state 更新就直接读取 state.activeItem,
102+
// 导致 activeItem 仍然是旧值,所以这里直接使用 props.activeItem 来覆盖
103+
activeItem,
104+
},
105+
},
106+
items,
107+
)
108+
}
109+
return originalGetActiveIndex.call(this, items)
110+
}
111+
;(QueryList.prototype['getActiveIndex'] as any)._original =
112+
originalGetActiveIndex

src/components/UserFilter.tsx

Lines changed: 75 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Button, Classes, IconSize, MenuItem, Spinner } from '@blueprintjs/core'
2-
import { Classes as Popover2Classes } from '@blueprintjs/popover2'
32

43
import clsx from 'clsx'
54
import { useAtomValue } from 'jotai'
@@ -51,80 +50,81 @@ export const UserFilter: FC<UserFilterProps> = ({
5150
}, [auth.token, user, onChange])
5251

5352
return (
54-
<Select<MaaUserInfo>
55-
className={clsx('items-stretch', className)}
56-
items={users}
57-
itemListPredicate={() => (error ? [] : users)} // 有 error 时用 noResults 显示错误信息
58-
query={query}
59-
onQueryChange={(query) => updateQuery(query, false)}
60-
onReset={() => onChange(undefined)}
61-
itemsEqual={(a, b) => a.id === b.id}
62-
itemRenderer={(item, { handleClick, handleFocus, modifiers }) => (
63-
<MenuItem
64-
roleStructure="listoption"
65-
className={clsx(modifiers.active && Classes.ACTIVE)}
66-
key={item.id}
67-
text={item.userName}
68-
onClick={handleClick}
69-
onFocus={handleFocus}
70-
onMouseDown={onOptionMouseDown}
71-
selected={item === user}
72-
/>
73-
)}
74-
selectedItem={user}
75-
onItemSelect={(user) => onChange(user)}
76-
noResults={
77-
<MenuItem
78-
disabled
79-
text={
80-
isLoading
81-
? '正在搜索...'
82-
: error
83-
? '搜索失败:' + formatError(error)
84-
: query && debouncedQuery
85-
? '查无此人 (゚Д゚≡゚д゚)!?'
86-
: '输入用户名以搜索'
87-
}
88-
/>
89-
}
90-
inputProps={{
91-
placeholder: '用户名称',
92-
leftElement: isValidating ? (
93-
<Spinner className="m-[7px] mr-[9px]" size={IconSize.STANDARD} />
94-
) : undefined,
95-
rightElement: auth.token ? (
96-
<Button
97-
minimal
98-
className={clsx(
99-
!isMyself(user) &&
100-
'opacity-75 ' + Popover2Classes.POPOVER2_DISMISS,
101-
)}
102-
active={isMyself(user)}
103-
intent={isMyself(user) ? 'primary' : 'none'}
104-
onClick={() => {
105-
if (isMyself(user)) {
106-
onChange(undefined)
107-
} else {
108-
onChange(MYSELF)
109-
}
110-
}}
111-
>
112-
我自己
113-
</Button>
114-
) : undefined,
115-
}}
116-
popoverProps={{
117-
minimal: true,
118-
}}
119-
>
120-
<Button
121-
minimal
122-
className="!pl-3 !pr-2"
123-
icon="person"
124-
rightIcon="chevron-down"
53+
<>
54+
<Select<MaaUserInfo>
55+
className={clsx('items-stretch', className)}
56+
items={users}
57+
itemListPredicate={() => (error ? [] : users)} // 有 error 时用 noResults 显示错误信息
58+
query={query}
59+
onQueryChange={(query) => updateQuery(query, false)}
60+
onReset={() => onChange(undefined)}
61+
itemsEqual={(a, b) => a.id === b.id}
62+
itemRenderer={(item, { handleClick, handleFocus, modifiers }) => (
63+
<MenuItem
64+
roleStructure="listoption"
65+
className={clsx(modifiers.active && Classes.ACTIVE)}
66+
key={item.id}
67+
text={item.userName}
68+
onClick={handleClick}
69+
onFocus={handleFocus}
70+
onMouseDown={onOptionMouseDown}
71+
selected={item === user}
72+
/>
73+
)}
74+
canReset={user && !isMyself(user)}
75+
onItemSelect={(user) => onChange(user)}
76+
noResults={
77+
<MenuItem
78+
disabled
79+
text={
80+
isLoading
81+
? '正在搜索...'
82+
: error
83+
? '搜索失败:' + formatError(error)
84+
: query && debouncedQuery
85+
? '查无此人 (゚Д゚≡゚д゚)!?'
86+
: '输入用户名以搜索'
87+
}
88+
/>
89+
}
90+
inputProps={{
91+
placeholder: '用户名称',
92+
leftElement: isValidating ? (
93+
<Spinner className="m-[7px] mr-[9px]" size={IconSize.STANDARD} />
94+
) : undefined,
95+
}}
96+
popoverProps={{
97+
minimal: true,
98+
}}
12599
>
126-
{user ? user.userName : '作者'}
127-
</Button>
128-
</Select>
100+
<Button
101+
minimal
102+
className="!pl-3 !pr-2"
103+
icon="person"
104+
rightIcon="chevron-down"
105+
>
106+
{user && !isMyself(user) ? user.userName : '作者'}
107+
</Button>
108+
</Select>
109+
{!!auth.token && (
110+
<Button
111+
minimal
112+
icon="user"
113+
className="!px-3"
114+
title="查看我自己的作业"
115+
active={isMyself(user)}
116+
intent={isMyself(user) ? 'primary' : 'none'}
117+
onClick={() => {
118+
if (isMyself(user)) {
119+
onChange(undefined)
120+
} else {
121+
onChange(MYSELF)
122+
}
123+
}}
124+
>
125+
看看我的
126+
</Button>
127+
)}
128+
</>
129129
)
130130
}

0 commit comments

Comments
 (0)