Skip to content

Commit 40c57d8

Browse files
authored
Merge pull request #376 from MaaAssistantArknights/dev
Release
2 parents 66b0b2d + 63628fd commit 40c57d8

File tree

11 files changed

+248
-54
lines changed

11 files changed

+248
-54
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Auto Upsert Release PR
2+
3+
on:
4+
# trigger on push to dev branch
5+
push:
6+
branches:
7+
- dev
8+
# trigger on manual workflow_dispatch
9+
workflow_dispatch:
10+
11+
concurrency:
12+
group: pr-upsert-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: write
17+
pull-requests: write
18+
19+
jobs:
20+
pr-upsert:
21+
name: Upsert PR
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v3
25+
with:
26+
ref: "dev"
27+
# fetch all history so that git log can get all commit messages
28+
fetch-depth: 0
29+
30+
# create a PR from dev to main, with title in form: Release <semver>
31+
# where, <semver> is the next version number to be released, based on the last release in git tag
32+
- name: Upsert PR Content
33+
uses: actions/github-script@v6
34+
with:
35+
github-token: ${{ github.token }}
36+
script: |
37+
const prTitle = "Release to Production"
38+
let body = `> *This PR is automatically created by actions defined in this repository. To see the run log of this action, please click [here](/${{ github.repository }}/actions/runs/${{ github.run_id }})*`
39+
const existedPR = await github.rest.pulls.list({
40+
owner: context.repo.owner,
41+
repo: context.repo.repo,
42+
state: 'open',
43+
head: `${context.repo.owner}:dev`,
44+
base: 'main'
45+
})
46+
if (existedPR.data.length > 0) {
47+
core.info(`PR already exists: ${existedPR.data[0].html_url}. Updating body...`)
48+
await github.rest.pulls.update({
49+
owner: context.repo.owner,
50+
repo: context.repo.repo,
51+
pull_number: existedPR.data[0].number,
52+
body: body
53+
})
54+
core.info(`PR updated: ${existedPR.data[0].html_url}`)
55+
return
56+
}
57+
const pr = await github.rest.pulls.create({
58+
owner: context.repo.owner,
59+
repo: context.repo.repo,
60+
title: prTitle,
61+
body: body,
62+
head: context.ref,
63+
base: 'main',
64+
draft: true
65+
})

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 2025-02-17
2+
3+
- 修复了编辑作业时无法修改干员名称的问题 [@Gemini2035](https://github.com/Gemini2035)
4+
- 修复了编辑作业时无法修改动作类型的问题 [@Gemini2035](https://github.com/Gemini2035)
5+
- 添加了是否保存干员过滤器的选项 [@guansss](https://github.com/guansss)
6+
- 使用自定义关卡时支持设置难度 [@guansss](https://github.com/guansss)
7+
- 编辑作业时提醒缺失的干员 [@guansss](https://github.com/guansss)
8+
19
## 2025-02-06
210

311
- 支持作业集内排序 [@hmydgz](https://github.com/hmydgz)
46.9 KB
Loading
50.7 KB
Loading

src/components/Operations.tsx

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,17 @@ import {
77
} from '@blueprintjs/core'
88

99
import { UseOperationsParams } from 'apis/operation'
10-
import { useAtom, useAtomValue } from 'jotai'
10+
import { useAtom } from 'jotai'
1111
import { debounce } from 'lodash-es'
1212
import { ComponentType, useMemo, useState } from 'react'
1313

1414
import { CardTitle } from 'components/CardTitle'
1515
import { OperationList } from 'components/OperationList'
1616
import { OperationSetList } from 'components/OperationSetList'
1717
import { neoLayoutAtom } from 'store/pref'
18-
import {
19-
selectedOperatorQueryAtom,
20-
selectedOperatorsAtom,
21-
} from 'store/selectedOperators'
2218

2319
import { authAtom } from '../store/auth'
24-
import { OperatorSelect } from './OperatorSelect'
20+
import { OperatorFilter } from './OperatorFilter'
2521
import { withSuspensable } from './Suspensable'
2622

2723
export const Operations: ComponentType = withSuspensable(() => {
@@ -31,14 +27,13 @@ export const Operations: ComponentType = withSuspensable(() => {
3127
limit: 10,
3228
orderBy: 'hot',
3329
})
34-
const [selectedOperators, setSelectedOperators] = useAtom(
35-
selectedOperatorsAtom,
36-
)
37-
const selectedOperatorQuery = useAtomValue(selectedOperatorQueryAtom)
3830
const debouncedSetQueryParams = useMemo(
3931
() => debounce(setQueryParams, 500),
4032
[],
4133
)
34+
35+
const [selectedOperators, setSelectedOperators] = useState<string[]>([])
36+
4237
const [authState] = useAtom(authAtom)
4338
const [neoLayout, setNeoLayout] = useAtom(neoLayoutAtom)
4439
const [listMode, setListMode] = useState<'operation' | 'operationSet'>(
@@ -104,14 +99,7 @@ export const Operations: ComponentType = withSuspensable(() => {
10499
{listMode === 'operation' && (
105100
<div className="flex flex-wrap items-end">
106101
<div className="flex mr-4">
107-
<FormGroup
108-
helperText={
109-
selectedOperators.length
110-
? '点击干员标签以标记为排除该干员'
111-
: undefined
112-
}
113-
className="max-w-md"
114-
>
102+
<div className="max-w-md">
115103
<InputGroup
116104
className="[&>input]:!rounded-md"
117105
placeholder="标题、描述、神秘代码"
@@ -145,12 +133,12 @@ export const Operations: ComponentType = withSuspensable(() => {
145133
}
146134
onBlur={() => debouncedSetQueryParams.flush()}
147135
/>
148-
<OperatorSelect
136+
<OperatorFilter
149137
className="mt-2"
150-
operators={selectedOperatorQuery.split(',')}
138+
operators={selectedOperators}
151139
onChange={setSelectedOperators}
152140
/>
153-
</FormGroup>
141+
</div>
154142
</div>
155143
<div className="flex flex-col">
156144
{filterNode}
@@ -221,7 +209,7 @@ export const Operations: ComponentType = withSuspensable(() => {
221209
{listMode === 'operation' && (
222210
<OperationList
223211
{...queryParams}
224-
operator={selectedOperatorQuery}
212+
operator={selectedOperators.join(',')}
225213
// 按热度排序时列表前几页的变化不会太频繁,可以不刷新第一页,节省点流量
226214
revalidateFirstPage={queryParams.orderBy !== 'hot'}
227215
/>

src/components/OperatorFilter.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Checkbox } from '@blueprintjs/core'
2+
3+
import { useAtom } from 'jotai'
4+
import { FC, useEffect } from 'react'
5+
6+
import {
7+
selectedOperatorsAtom,
8+
shouldSaveSelectedOperatorsAtom,
9+
} from '../store/selectedOperators'
10+
import { OperatorSelect } from './OperatorSelect'
11+
12+
interface OperatorSelectProps {
13+
className?: string
14+
operators: string[]
15+
onChange: (operators: string[]) => void
16+
}
17+
18+
export const OperatorFilter: FC<OperatorSelectProps> = ({
19+
className,
20+
operators,
21+
onChange,
22+
}) => {
23+
const [shouldSaveSelectedOperators, setShouldSaveSelectedOperators] = useAtom(
24+
shouldSaveSelectedOperatorsAtom,
25+
)
26+
const [savedSelectedOperators, setSavedSelectedOperators] = useAtom(
27+
selectedOperatorsAtom,
28+
)
29+
30+
useEffect(() => {
31+
// 用户在另一个标签页中修改选择时,savedSelectedOperators 会同步到当前标签页,需要手动更新给 operators
32+
if (shouldSaveSelectedOperators) {
33+
onChange(savedSelectedOperators)
34+
}
35+
}, [shouldSaveSelectedOperators, savedSelectedOperators, onChange])
36+
37+
const handleSelectOperator = (operators: string[]) => {
38+
onChange(operators)
39+
40+
if (shouldSaveSelectedOperators) {
41+
setSavedSelectedOperators(operators)
42+
}
43+
}
44+
45+
const handleShouldSaveSelectedOperators = (shouldSave: boolean) => {
46+
setShouldSaveSelectedOperators(shouldSave)
47+
48+
if (shouldSave) {
49+
setSavedSelectedOperators(operators)
50+
}
51+
}
52+
53+
return (
54+
<div className={className}>
55+
<OperatorSelect operators={operators} onChange={handleSelectOperator} />
56+
{operators.length > 0 && (
57+
<div className="flex justify-between items-baseline text-zinc-500">
58+
<div>点击干员标签以标记为排除该干员</div>
59+
<Checkbox
60+
className="[&>input:checked~.bp4-control-indicator]:bg-zinc-500"
61+
checked={shouldSaveSelectedOperators}
62+
onChange={(e) =>
63+
handleShouldSaveSelectedOperators(e.currentTarget.checked)
64+
}
65+
>
66+
<span className="-ml-2">记住选择</span>
67+
</Checkbox>
68+
</div>
69+
)}
70+
</div>
71+
)
72+
}

src/components/editor/OperationEditor.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,18 @@ const DifficultyPicker: FC<{
196196

197197
const stageName = useWatch({ control, name: 'stageName' })
198198
const { data: levels } = useLevels()
199-
const invalid = useMemo(
200-
() => !hasHardMode(levels, stageName),
201-
[levels, stageName],
202-
)
199+
const invalid = useMemo(() => {
200+
// if the stageName is a custom level, we always allow setting difficulty
201+
if (!findLevelByStageName(levels, stageName)) {
202+
return false
203+
}
204+
205+
if (hasHardMode(levels, stageName)) {
206+
return false
207+
}
208+
209+
return true
210+
}, [levels, stageName])
203211

204212
useEffect(() => {
205213
if (invalid && value !== OpDifficulty.UNKNOWN) {

src/components/editor/action/EditorActionAdd.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,15 @@ export const EditorActionAdd = ({
100100
[type],
101101
)
102102

103+
useEffect(() => {
104+
if (editingAction?.type) {
105+
setValue('type', editingAction.type)
106+
}
107+
// eslint-disable-next-line react-hooks/exhaustive-deps
108+
}, [editingAction?._id, setValue])
109+
103110
useEffect(() => {
104111
if (editingAction) {
105-
if ('type' in editingAction) {
106-
setValue('type', editingAction.type)
107-
}
108112
// 修复切换type的时候,数据丢失的问题
109113
// 原因:因为切换type的时候会触发页面绘制,导致form和对应的item组件丢失绑定,
110114
// 当重置时没办法正常清空item组件内部的值。

src/components/editor/operator/EditorPerformer.tsx

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NonIdealState } from '@blueprintjs/core'
1+
import { Callout, Icon, NonIdealState } from '@blueprintjs/core'
22
import {
33
Active,
44
DndContext,
@@ -17,12 +17,17 @@ import {
1717
verticalListSortingStrategy,
1818
} from '@dnd-kit/sortable'
1919

20-
import { uniqueId } from 'lodash-es'
21-
import { FC, useEffect, useState } from 'react'
22-
import { Control, UseFieldArrayMove, useFieldArray } from 'react-hook-form'
20+
import { compact, uniq, uniqueId } from 'lodash-es'
21+
import { FC, useEffect, useMemo, useState } from 'react'
22+
import {
23+
Control,
24+
UseFieldArrayMove,
25+
useFieldArray,
26+
useWatch,
27+
} from 'react-hook-form'
2328
import { SetRequired } from 'type-fest'
2429

25-
import type { CopilotDocV1 } from 'models/copilot.schema'
30+
import { CopilotDocV1 } from 'models/copilot.schema'
2631

2732
import { FactItem } from '../../FactItem'
2833
import { Droppable, Sortable } from '../../dnd'
@@ -52,6 +57,7 @@ const getId = (performer: Operator | Group) => {
5257
export const EditorPerformer: FC<EditorPerformerProps> = ({ control }) => {
5358
const [editMode, setEditMode] = useState<PerformerType>('operator')
5459
const sensors = useSensors(useSensor(PointerSensor))
60+
const actions = useWatch({ control, name: 'actions' })
5561

5662
const {
5763
fields: _operators,
@@ -79,6 +85,25 @@ export const EditorPerformer: FC<EditorPerformerProps> = ({ control }) => {
7985
const operators: Operator[] = _operators
8086
const groups: Group[] = _groups
8187

88+
const additionalOperatorsFromActions = useMemo(() => {
89+
if (!actions) return []
90+
91+
const additionalOperators = actions.map((action) => {
92+
if (
93+
'name' in action &&
94+
!operators.some(({ name }) => name === action.name) &&
95+
!groups.some(({ opers }) =>
96+
opers?.some(({ name }) => name === action.name),
97+
)
98+
) {
99+
return action.name
100+
}
101+
return undefined
102+
})
103+
104+
return uniq(compact(additionalOperators))
105+
}, [actions, operators, groups])
106+
82107
const [draggingOperator, setDraggingOperator] = useState<Operator>()
83108
const [draggingGroup, setDraggingGroup] = useState<Group>()
84109
const [editingOperator, setEditingOperator] = useState<Operator>()
@@ -289,7 +314,7 @@ export const EditorPerformer: FC<EditorPerformerProps> = ({ control }) => {
289314
addOperator()
290315
} else {
291316
updateOperator(
292-
operators.findIndex(({ name }) => name === operator.name),
317+
operators.findIndex(({ _id }) => _id === operator._id),
293318
operator,
294319
)
295320
}
@@ -354,14 +379,16 @@ export const EditorPerformer: FC<EditorPerformerProps> = ({ control }) => {
354379
<>
355380
<div className="flex flex-wrap md:flex-nowrap">
356381
<div className="w-full md:w-1/3 md:mr-8 flex flex-col pb-8">
357-
<EditorSheetTrigger
358-
submitOperator={submitOperator}
359-
submitGroup={submitGroup}
360-
existedOperators={operators}
361-
existedGroups={groups}
362-
removeOperator={removeOperator}
363-
removeGroup={removeGroup}
364-
/>
382+
<div className="mb-2">
383+
<EditorSheetTrigger
384+
submitOperator={submitOperator}
385+
submitGroup={submitGroup}
386+
existedOperators={operators}
387+
existedGroups={groups}
388+
removeOperator={removeOperator}
389+
removeGroup={removeGroup}
390+
/>
391+
</div>
365392
<EditorPerformerAdd
366393
mode={editMode}
367394
operator={editingOperator}
@@ -377,7 +404,17 @@ export const EditorPerformer: FC<EditorPerformerProps> = ({ control }) => {
377404
/>
378405
</div>
379406
<div className="w-full md:w-2/3 pb-8">
380-
<div className="p-2 -mx-2 relative">
407+
{additionalOperatorsFromActions.length > 0 && (
408+
<Callout
409+
className="flex items-center py-2 mb-2"
410+
icon={null}
411+
intent="primary"
412+
>
413+
<Icon icon="info-sign" className="mr-1" />
414+
未加入干员:{additionalOperatorsFromActions.join(', ')}
415+
</Callout>
416+
)}
417+
<div className="mt-2 relative">
381418
<DndContext
382419
sensors={sensors}
383420
onDragStart={handleDragStart}

0 commit comments

Comments
 (0)