mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-13 23:54:48 +01:00
236 lines
6.0 KiB
TypeScript
236 lines
6.0 KiB
TypeScript
import { invoke } from '@tauri-apps/api'
|
|
import React from 'react'
|
|
import DownloadHandler from '../utils/download'
|
|
import { getModDownload, ModData } from '../utils/gamebanana'
|
|
import { getModsFolder } from '../utils/mods'
|
|
import { unzip } from '../utils/zipUtils'
|
|
import ProgressBar from './components/common/MainProgressBar'
|
|
import { ModHeader } from './components/mods/ModHeader'
|
|
import { ModList } from './components/mods/ModList'
|
|
import TopBar from './components/TopBar'
|
|
|
|
import './Mods.css'
|
|
import Back from '../resources/icons/back.svg'
|
|
import Menu from './components/menu/Menu'
|
|
import BigButton from './components/common/BigButton'
|
|
import Tr from '../utils/language'
|
|
import { ModPages } from './components/mods/ModPages'
|
|
import TextInput from './components/common/TextInput'
|
|
|
|
interface IProps {
|
|
downloadHandler: DownloadHandler
|
|
}
|
|
|
|
interface IState {
|
|
isDownloading: boolean
|
|
category: string
|
|
downloadList: { name: string; url: string; mod: ModData }[] | null
|
|
page: number
|
|
search: string
|
|
}
|
|
|
|
const pages = [
|
|
{
|
|
name: -1,
|
|
title: '<',
|
|
},
|
|
{
|
|
name: 1,
|
|
title: '>',
|
|
},
|
|
]
|
|
|
|
const headers = [
|
|
{
|
|
name: 'ripe',
|
|
title: 'Hot',
|
|
},
|
|
{
|
|
name: 'new',
|
|
title: 'New',
|
|
},
|
|
{
|
|
name: 'installed',
|
|
title: 'Installed',
|
|
},
|
|
]
|
|
|
|
/**
|
|
* Mods currently install into folder labelled with their GB ID
|
|
*
|
|
* @TODO Categorizaiton/sorting (by likes, views, etc)
|
|
*/
|
|
export class Mods extends React.Component<IProps, IState> {
|
|
timeout: number
|
|
constructor(props: IProps) {
|
|
super(props)
|
|
this.timeout = 0
|
|
|
|
this.state = {
|
|
isDownloading: false,
|
|
category: '',
|
|
downloadList: null,
|
|
page: 1,
|
|
search: '',
|
|
}
|
|
|
|
this.setCategory = this.setCategory.bind(this)
|
|
this.addDownload = this.addDownload.bind(this)
|
|
this.setPage = this.setPage.bind(this)
|
|
}
|
|
|
|
async addDownload(mod: ModData) {
|
|
const dlLinks = await getModDownload(String(mod.id))
|
|
|
|
// Not gonna bother allowing sorting for now
|
|
const firstLink = dlLinks[0].downloadUrl
|
|
const fileExt = firstLink.split('.').pop()
|
|
|
|
const modName = `${mod.id}.${fileExt}`
|
|
|
|
if (dlLinks.length === 0) return
|
|
|
|
// If there is one download we don't care to choose
|
|
if (dlLinks.length === 1) {
|
|
this.downloadMod(firstLink, modName, mod)
|
|
return
|
|
}
|
|
|
|
this.setState({
|
|
downloadList: dlLinks.map((link) => ({
|
|
name: link.filename,
|
|
url: link.downloadUrl,
|
|
mod: mod,
|
|
})),
|
|
})
|
|
}
|
|
|
|
async downloadMod(link: string, modName: string, mod: ModData) {
|
|
const modFolder = await getModsFolder()
|
|
const path = `${modFolder}/${modName}`
|
|
|
|
if (!modFolder) return
|
|
|
|
this.props.downloadHandler.addDownload(link, path, async () => {
|
|
const unzipRes = await unzip(path, modFolder, false, true)
|
|
|
|
// Write a modinfo.json file
|
|
invoke('write_file', {
|
|
path: `${unzipRes.new_folder}/modinfo.json`,
|
|
contents: JSON.stringify(mod),
|
|
})
|
|
})
|
|
}
|
|
|
|
async setCategory(value: string) {
|
|
this.setState(
|
|
{
|
|
category: value,
|
|
},
|
|
this.forceUpdate
|
|
)
|
|
}
|
|
|
|
async setPage(value: number) {
|
|
const current = this.state.page
|
|
if (current + value == 0) return
|
|
this.setState(
|
|
{
|
|
page: current + value,
|
|
},
|
|
this.forceUpdate
|
|
)
|
|
}
|
|
|
|
async setSearch(text: string) {
|
|
if (this.timeout) clearTimeout(this.timeout)
|
|
this.timeout = window.setTimeout(() => {
|
|
this.setState(
|
|
{
|
|
search: text,
|
|
},
|
|
this.forceUpdate
|
|
)
|
|
}, 500)
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div className="Mods">
|
|
<TopBar>
|
|
<div
|
|
id="backbtn"
|
|
className="TopButton"
|
|
onClick={() => {
|
|
// Create and dispatch a custom "changePage" event
|
|
const event = new CustomEvent('changePage', { detail: 'main' })
|
|
window.dispatchEvent(event)
|
|
}}
|
|
>
|
|
<img src={Back} alt="back" />
|
|
</div>
|
|
</TopBar>
|
|
|
|
{this.state.downloadList && (
|
|
<Menu className="ModMenu" heading="Links" closeFn={() => this.setState({ downloadList: null })}>
|
|
<div className="ModDownloadList">
|
|
{this.state.downloadList.map((o) => {
|
|
return (
|
|
<div className="ModDownloadItem" key={o.name}>
|
|
<div className="ModDownloadName">{o.name}</div>
|
|
<BigButton
|
|
id={o.url}
|
|
onClick={() => {
|
|
const fileExt = o.url.split('.').pop()
|
|
const modName = `${o.mod.id}.${fileExt}`
|
|
|
|
this.downloadMod(o.url, modName, o.mod)
|
|
this.setState({
|
|
downloadList: null,
|
|
})
|
|
}}
|
|
>
|
|
<Tr text="components.download" />
|
|
</BigButton>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</Menu>
|
|
)}
|
|
|
|
<div className="TopDownloads">
|
|
<ProgressBar downloadManager={this.props.downloadHandler} withStats={false} />
|
|
</div>
|
|
|
|
<ModHeader onChange={this.setCategory} headers={headers} defaultHeader={'ripe'} />
|
|
|
|
{this.state.category != 'installed' && (
|
|
<>
|
|
<div className="ModPagesPage">
|
|
<TextInput
|
|
id="search"
|
|
key="search"
|
|
placeholder={this.state.page.toString()}
|
|
onChange={(text: string) => {
|
|
this.setSearch(text)
|
|
}}
|
|
initalValue={''}
|
|
/>
|
|
</div>
|
|
<ModPages onClick={this.setPage} headers={pages} defaultHeader={this.state.page} />
|
|
</>
|
|
)}
|
|
|
|
<ModList
|
|
key={`${this.state.category}_${this.state.page}_${this.state.search}`}
|
|
mode={this.state.category}
|
|
addDownload={this.addDownload}
|
|
page={this.state.page}
|
|
search={this.state.search}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
}
|