Skip to content

Release to Production #414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 36 additions & 14 deletions scripts/shared.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { access } from 'fs/promises'
import { uniq, uniqBy } from 'lodash-es'
import { capitalize, uniq, uniqBy } from 'lodash-es'
import fetch from 'node-fetch'
import { pinyin } from 'pinyin'
import simplebig from 'simplebig'

type Profession = { id: string; name: string }
type Profession = { id: string; name: string; name_en?: string }
type Professions = (Profession & { sub: Profession[] })[]

export async function fileExists(file: string) {
Expand Down Expand Up @@ -48,11 +48,14 @@ function transformOperatorName(name: string) {
}
}

const CHARACTER_TABLE_JSON_URL =
const CHARACTER_TABLE_JSON_URL_CN =
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData/master/zh_CN/gamedata/excel/character_table.json'
const UNIEQUIP_TABLE_JSON_URL =
const UNIEQUIP_TABLE_JSON_URL_CN =
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData/master/zh_CN/gamedata/excel/uniequip_table.json'

const CHARACTER_TABLE_JSON_URL_EN =
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData_YoStar/main/en_US/gamedata/excel/character_table.json'
const UNIEQUIP_TABLE_JSON_URL_EN =
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData_YoStar/main/en_US/gamedata/excel/uniequip_table.json'
const CHARACTER_BLOCKLIST = [
'char_512_aprot', // 暮落(集成战略):It's just not gonna be there.
'token_10012_rosmon_shield', // 迷迭香的战术装备:It's just not gonna be there.
Expand All @@ -74,37 +77,55 @@ async function json(url: string) {
}

export async function getOperators() {
const [charTable, uniequipTable] = await Promise.all([
json(CHARACTER_TABLE_JSON_URL),
json(UNIEQUIP_TABLE_JSON_URL),
])
const [charTableCN, uniequipTableCN, charTableEN, uniequipTableEN] =
await Promise.all([
json(CHARACTER_TABLE_JSON_URL_CN),
json(UNIEQUIP_TABLE_JSON_URL_CN),
json(CHARACTER_TABLE_JSON_URL_EN),
json(UNIEQUIP_TABLE_JSON_URL_EN),
])

const { subProfDict } = uniequipTable
const { subProfDict: subProfDictCN } = uniequipTableCN
const { subProfDict: subProfDictEN } = uniequipTableEN

const opIds = Object.keys(charTable)
const opIds = Object.keys(charTableCN)
const professions: Professions = []
const result = uniqBy(
opIds.flatMap((id) => {
const op = charTable[id]
const op = charTableCN[id]
const enName = charTableEN[id]?.name || op.appellation || op.name

if (['TRAP'].includes(op.profession)) return []

if (!['TOKEN'].includes(op.profession)) {
const prof = professions.find((p) => p.id === op.profession)
if (!prof) {
const enSubProfName =
subProfDictEN?.[op.subProfessionId]?.subProfessionName ||
capitalize(op.subProfessionId)

professions.push({
id: op.profession,
name: PROFESSION_NAMES[op.profession],
name_en:
op.profession.charAt(0) + op.profession.slice(1).toLowerCase(),
sub: [
{
id: op.subProfessionId,
name: subProfDict[op.subProfessionId].subProfessionName,
name: subProfDictCN[op.subProfessionId].subProfessionName,
name_en: enSubProfName,
},
],
})
} else if (!prof.sub.find((p) => p.id === op.subProfessionId)) {
const enSubProfName =
subProfDictEN?.[op.subProfessionId]?.subProfessionName ||
capitalize(op.subProfessionId)

prof.sub.push({
id: op.subProfessionId,
name: subProfDict[op.subProfessionId].subProfessionName,
name: subProfDictCN[op.subProfessionId].subProfessionName,
name_en: enSubProfName,
})
}
}
Expand All @@ -113,6 +134,7 @@ export async function getOperators() {
id: id,
prof: op.profession,
subProf: op.subProfessionId,
name_en: enName,
...transformOperatorName(op.name),
rarity:
op.subProfessionId === 'notchar1'
Expand Down
2 changes: 1 addition & 1 deletion src/apis/announcement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const isMock = process.env.NODE_ENV === 'development'

const announcementURL = isMock
? mockFile
: 'https://ota.maa.plus/MaaAssistantArknights/api/announcements/copilot.md'
: 'https://api.maa.plus/MaaAssistantArknights/api/announcements/copilot.md'

export const announcementBaseURL = isMock
? location.href
Expand Down
12 changes: 9 additions & 3 deletions src/components/OperationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Button, Card, Elevation, H4, H5, Icon, Tag } from '@blueprintjs/core'
import { Tooltip2 } from '@blueprintjs/popover2'

import clsx from 'clsx'
import { useAtomValue } from 'jotai'
import { CopilotInfoStatusEnum } from 'maa-copilot-client'
import { copyShortCode, handleLazyDownloadJSON } from 'services/operation'

Expand All @@ -11,8 +12,9 @@ import { OperationRating } from 'components/viewer/OperationRating'
import { OpDifficulty, Operation } from 'models/operation'

import { useLevels } from '../apis/level'
import { useTranslation } from '../i18n/i18n'
import { languageAtom, useTranslation } from '../i18n/i18n'
import { createCustomLevel, findLevelByStageName } from '../models/level'
import { getLocalizedOperatorName } from '../models/operator'
import { Paragraphs } from './Paragraphs'
import { ReLinkDiv } from './ReLinkDiv'
import { UserName } from './UserName'
Expand Down Expand Up @@ -245,13 +247,14 @@ export const OperationCard = ({ operation }: { operation: Operation }) => {

const OperatorTags = ({ operation }: { operation: Operation }) => {
const t = useTranslation()
const language = useAtomValue(languageAtom)
const { opers, groups } = operation.parsedContent

return opers?.length || groups?.length ? (
<div>
{opers?.map(({ name, skill }, index) => (
<Tag key={index} className="mr-2 last:mr-0 mb-1 last:mb-0">
{`${name} ${skill ?? 1}`}
{`${getLocalizedOperatorName(name, language)} ${skill ?? 1}`}
</Tag>
))}
{groups?.map(({ name, opers }, index) => (
Expand All @@ -261,7 +264,10 @@ const OperatorTags = ({ operation }: { operation: Operation }) => {
placement="top"
content={
opers
?.map(({ name, skill }) => `${name} ${skill ?? 1}`)
?.map(
({ name, skill }) =>
`${getLocalizedOperatorName(name, language)} ${skill ?? 1}`,
)
.join(', ') || t.components.OperationCard.no_operators
}
>
Expand Down
15 changes: 9 additions & 6 deletions src/components/OperatorFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
} from '@blueprintjs/core'

import clsx from 'clsx'
import { getDefaultStore, useAtom } from 'jotai'
import { getDefaultStore, useAtom, useAtomValue } from 'jotai'
import { compact } from 'lodash-es'
import { FC, useEffect, useMemo, useState } from 'react'

import { useTranslation } from '../i18n/i18n'
import { languageAtom, useTranslation } from '../i18n/i18n'
import { OPERATORS } from '../models/operator'
import {
DEFAULT_OPERATOR_FILTER,
Expand Down Expand Up @@ -53,6 +53,7 @@ export const OperatorFilter: FC<OperatorFilterProps> = ({
onChange,
}) => {
const t = useTranslation()
const language = useAtomValue(languageAtom)
const [savedFilter, setSavedFilter] = useAtom(operatorFilterAtom)
const [dialogOpen, setDialogOpen] = useState(false)
const [editingFilter, setEditingFilter] = useState<typeof savedFilter>(filter)
Expand Down Expand Up @@ -137,27 +138,29 @@ export const OperatorFilter: FC<OperatorFilterProps> = ({
!filter.enabled && 'opacity-30',
)}
>
{includedOperators.map(({ id, name, rarity }) => (
{includedOperators.map(({ id, name, name_en, rarity }) => (
<Tag minimal key={id} className="py-0 pl-0" intent="primary">
<div className="flex items-center gap-1 text-sm">
<OperatorAvatar
className="w-8 h-8"
id={id}
rarity={rarity}
/>
&nbsp;{name}&nbsp;
&nbsp;{language === 'en' ? name_en : name}
&nbsp;
</div>
</Tag>
))}
{excludedOperators.map(({ id, name, rarity }) => (
{excludedOperators.map(({ id, name, name_en, rarity }) => (
<Tag minimal key={id} className="py-0 pl-0" intent="danger">
<div className="flex items-center gap-1 text-sm line-through">
<OperatorAvatar
className="w-8 h-8"
id={id}
rarity={rarity}
/>
&nbsp;{name}&nbsp; {/* 两边加空格让删除线更显眼一些 */}
&nbsp;{language === 'en' ? name_en : name}
&nbsp; {/* 两边加空格让删除线更显眼一些 */}
</div>
</Tag>
))}
Expand Down
10 changes: 6 additions & 4 deletions src/components/OperatorSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { MultiSelect2 } from '@blueprintjs/select'

import clsx from 'clsx'
import Fuse from 'fuse.js'
import { useAtomValue } from 'jotai'
import { compact } from 'lodash-es'
import { FC, useMemo } from 'react'

import { useTranslation } from '../i18n/i18n'
import { languageAtom, useTranslation } from '../i18n/i18n'
import { OPERATORS } from '../models/operator'
import { useDebouncedQuery } from '../utils/useDebouncedQuery'
import { OperatorAvatar } from './editor/operator/EditorOperator'
Expand All @@ -26,13 +27,14 @@ export const OperatorSelect: FC<OperatorSelectProps> = ({
onChange,
}) => {
const t = useTranslation()
const language = useAtomValue(languageAtom)
const { query, trimmedDebouncedQuery, updateQuery, onOptionMouseDown } =
useDebouncedQuery()

const fuse = useMemo(
() =>
new Fuse(OPERATORS, {
keys: ['name', 'alias', 'alt_name'],
keys: ['name', 'name_en', 'alias', 'alt_name'],
threshold: 0.3,
}),
[],
Expand Down Expand Up @@ -82,7 +84,7 @@ export const OperatorSelect: FC<OperatorSelectProps> = ({
id={item.id}
rarity={item.rarity}
/>
{item.name}
{language === 'en' ? item.name_en : item.name}
</div>
}
onClick={handleClick}
Expand Down Expand Up @@ -129,7 +131,7 @@ export const OperatorSelect: FC<OperatorSelectProps> = ({
id={item.id}
rarity={item.rarity}
/>
{item.name}
{language === 'en' ? item.name_en : item.name}
</div>
)}
popoverProps={{
Expand Down
14 changes: 10 additions & 4 deletions src/components/editor/DetailedSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { Select2, Select2Props } from '@blueprintjs/select'
import { ReactNode } from 'react'
import { FCC } from 'types'

export type DetailedSelectItem =
| DetailedSelectChoice
| { type: 'header'; header: ReactNode }
export type DetailedSelectItem = DetailedSelectHeader | DetailedSelectChoice
export type DetailedSelectHeader = {
type: 'header'
header: ReactNode | (() => ReactNode)
}
export interface DetailedSelectChoice {
type: 'choice'
icon?: IconName
Expand Down Expand Up @@ -34,7 +36,11 @@ export const DetailedSelect: FCC<
if (action.type === 'header') {
return (
<li key={'header_' + action.header} className={Classes.MENU_HEADER}>
<H6>{action.header}</H6>
<H6>
{typeof action.header === 'function'
? action.header()
: action.header}
</H6>
</li>
)
}
Expand Down
12 changes: 6 additions & 6 deletions src/components/editor/action/EditorActionTypeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ export const EditorActionTypeSelect = (

const menuItems = useMemo<DetailedSelectItem[]>(
() =>
Object.entries(groupBy(ACTION_TYPES, 'group')).flatMap(
([group, items]) => [
{ type: 'header' as const, header: group },
...items,
],
),
Object.values(
groupBy(ACTION_TYPES, (item) => item.group.toString()),
).flatMap((items) => [
{ type: 'header' as const, header: items[0].group },
...items,
]),
[],
)
const selectedAction = findActionType(value)
Expand Down
19 changes: 15 additions & 4 deletions src/components/editor/operator/EditorOperator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Icon, IconSize, MenuItem } from '@blueprintjs/core'

import clsx from 'clsx'
import Fuse from 'fuse.js'
import { useAtomValue } from 'jotai'
import { useMemo } from 'react'
import { FieldValues, useController } from 'react-hook-form'

import { EditorFieldProps } from 'components/editor/EditorFieldProps'

import { useTranslation } from '../../../i18n/i18n'
import { languageAtom, useTranslation } from '../../../i18n/i18n'
import { CopilotDocV1 } from '../../../models/copilot.schema'
import { OPERATORS } from '../../../models/operator'
import { Suggest } from '../../Suggest'
Expand All @@ -27,6 +28,7 @@ const createArbitraryOperator = (name: string): OperatorInfo => ({
alias: '',
alt_name: '',
subProf: '',
name_en: '',
prof: '',
rarity: 0,
})
Expand All @@ -43,6 +45,7 @@ export const EditorOperatorName = <T extends FieldValues>({
operators?: CopilotDocV1.Operator[]
}) => {
const t = useTranslation()
const language = useAtomValue(languageAtom)

const entityName = useMemo(
() =>
Expand Down Expand Up @@ -90,7 +93,7 @@ export const EditorOperatorName = <T extends FieldValues>({
const fuse = useMemo(
() =>
new Fuse(items, {
keys: ['name', 'alias', 'alt_name'],
keys: ['name', 'name_en', 'alias', 'alt_name'],
threshold: 0.3,
}),
[items],
Expand All @@ -107,7 +110,11 @@ export const EditorOperatorName = <T extends FieldValues>({
itemRenderer={(item, { handleClick, handleFocus, modifiers }) => (
<MenuItem
key={'id' in item ? item.id : item.name}
text={item.name}
text={
isOperator(item) && language === 'en' && item.name_en
? item.name_en
: item.name
}
icon={
isOperator(item) ? (
<OperatorAvatar id={item.id} size="small" />
Expand All @@ -123,7 +130,11 @@ export const EditorOperatorName = <T extends FieldValues>({
)}
onItemSelect={(item) => onChange(item.name)}
selectedItem={createArbitraryOperator((value || '') as string)}
inputValueRenderer={(item) => item.name}
inputValueRenderer={(item) =>
isOperator(item) && language === 'en' && item.name_en
? item.name_en
: item.name
}
createNewItemFromQuery={(query) => createArbitraryOperator(query)}
createNewItemRenderer={(query, active, handleClick) => (
<MenuItem
Expand Down
Loading