supporters full integrated exept sponsors item

This commit is contained in:
MathieuG-P
2022-09-09 02:11:15 +02:00
parent 2600785a10
commit bbff1c6934
16 changed files with 283 additions and 42 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+1 -3
View File
@@ -1,3 +1 @@
[
]
[]
+1
View File
@@ -4,3 +4,4 @@ import './bs-launcher-ipcs';
import './bs-version-ipcs';
import './bs-uninstall-ipcs';
import './map-ipcs';
import './supporters-ipcs';
+15
View File
@@ -0,0 +1,15 @@
import { ipcMain } from "electron";
import { SupportersService } from "../services/supporters.service";
import { IpcRequest } from "shared/models/ipc";
import { UtilsService } from '../services/utils.service';
ipcMain.on("get-supporters", (event, request: IpcRequest<void>) => {
const utils = UtilsService.getInstance();
const supportersService = SupportersService.getInstance();
supportersService.getSupporters().then(supporters => {
utils.ipcSend(request.responceChannel, {success: true, data: supporters});
}).catch(() => {
utils.ipcSend(request.responceChannel, {success: false});
});
});
+71
View File
@@ -0,0 +1,71 @@
import { writeFileSync } from "fs";
import { get } from "https";
import isOnline from "is-online";
import path from "path";
import { Supporter } from "shared/models/supporters/supporter.interface";
import { UtilsService } from "./utils.service";
export class SupportersService {
private readonly PATREONS_URL = "https://raw.githubusercontent.com/Zagrios/bs-manager/master/assets/jsons/patreons.json"
private readonly PATREONS_FILE = "patreons.json";
private static instance: SupportersService;
private cache: Supporter[];
private readonly utilsService: UtilsService;
public static getInstance(): SupportersService{
if(!SupportersService.instance){ SupportersService.instance = new SupportersService(); }
return SupportersService.instance;
}
private constructor(){
this.utilsService = UtilsService.getInstance();
}
private async updateLocalSupporters(supporters: Supporter[]): Promise<void>{
const patreonsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.PATREONS_FILE);
writeFileSync(patreonsPath, JSON.stringify(supporters, null, "\t"), {encoding: 'utf-8', flag: 'w'});
}
private async getRemoteSupporters(): Promise<Supporter[]>{
return new Promise<Supporter[]>((resolve, reject) => {
let body = ''
get(this.PATREONS_URL, (res) => {
res.on('data', chunk => body += chunk);
res.on('end', () => resolve(JSON.parse(body)));
res.on('error', () => reject(null))
})
})
}
private async getLocalSupporters(): Promise<Supporter[]>{
const patreonsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.PATREONS_FILE);
const rawPatreons = (await this.utilsService.readFileAsync(patreonsPath)).toString();
return JSON.parse(rawPatreons);
}
private async loadSupporters(): Promise<Supporter[]>{
if(!!this.cache && this.cache.length){ return this.cache; }
const isOnlineRes = await isOnline({timeout: 1500});
const [localVersions, remoteVersions] = await Promise.all([
this.getLocalSupporters(), (isOnlineRes && this.getRemoteSupporters())
]);
const supporters = remoteVersions?.length ? remoteVersions : localVersions;
if(!!remoteVersions && remoteVersions.length){ this.updateLocalSupporters(remoteVersions); }
this.cache = supporters;
return this.cache;
}
public getSupporters(): Promise<Supporter[]>{
return this.loadSupporters();
}
}
@@ -0,0 +1,19 @@
import { CSSProperties } from "react";
import { Supporter } from "shared/models/supporters";
import { motion } from "framer-motion";
import txtBg from "../../../../../assets/images/gifs/txt-bg.gif"
interface Props { supporter: Supporter, delay?: number }
export function SupporterItem({supporter, delay}: Props) {
const additionnalStyles: CSSProperties = (() => {
if(supporter.type === "gold"){ return {color: "#ffe270", textShadow: "0px 0px 10px #ffdd59", backgroundImage: `url(${txtBg})`, backgroundSize: "70% 15px", backgroundRepeat: "no-repeat", backgroundPosition: "center"}; }
if(supporter.type === "diamond"){ return {color: "#e574fc", textShadow: "0px 0px 15px #e056fd", backgroundImage: `url(${txtBg})`, backgroundSize: "70% 19px", backgroundRepeat: "no-repeat", backgroundPosition: "center"}; }
return {};
})()
return (
<motion.span className="text-2xl font-bold px-3 pb-1" style={additionnalStyles} initial={{y: "100%", opacity: 0}} animate={{y: 0, opacity: 1}} transition={{delay: delay}}>{supporter.username}</motion.span>
)
}
@@ -0,0 +1,22 @@
import { Supporter } from "shared/models/supporters";
import { SupporterItem } from "./supporter-item.component";
interface Props {className?: string, title: string, supporters: Supporter[]}
export function SupportersBlock({className, title, supporters}: Props) {
const someDelay = () => {
const [min, max] = [0, .55]
const baseDelay = .15;
return baseDelay + Math.random() * (max - min) + min;
}
return (
<div className={`mb-10 flex flex-col items-center ${className}`}>
<h2 className="uppercase text-3xl font-bold">{title}</h2>
<div className="flex justify-center flex-wrap py-5 px-20">
{supporters.map(s => <SupporterItem supporter={s} delay={someDelay()}/>)}
</div>
</div>
)
}
@@ -0,0 +1,49 @@
import { motion, AnimatePresence } from "framer-motion"
import { useEffect, useState } from "react";
import { BsmButton } from "renderer/components/shared/bsm-button.component"
import { Supporter } from "shared/models/supporters";
import { SupportersService } from "renderer/services/supporters.service";
import { SupportersBlock } from "./supporters-block.component";
import ManheraChanGif from "../../../../../assets/images/gifs/menhera-chan.gif"
import ManheraSadGif from "../../../../../assets/images/gifs/menhera-sad.gif"
interface Props {isVisible: boolean, setVisible: (b: boolean) => void}
export function SupportersView({isVisible, setVisible}: Props) {
const supportersService = SupportersService.getInstance();
const [supporters, setSupporters] = useState([] as Supporter[]);
const [sponsors, setSponsors] = useState([] as Supporter[]);
useEffect(() => {
if(isVisible){
supportersService.getSupporters().then(supporters => {
setSponsors(supporters.filter(s => s.type === "sponsor"));
setSupporters(supporters.filter(s => !s.type || s.type !== "sponsor"));
});
}
}, [isVisible])
return (
<AnimatePresence>
({isVisible &&
<motion.div className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-90 z-40 text-gray-200" transition={{duration: .3}} initial={{opacity: 0, y: "-100%"}} animate={{opacity: 1, y: "0%"}} exit={{opacity: 0, y: "-100%"}}>
<BsmButton className="absolute right-10 top-10 !bg-transparent w-7 h-7" icon="cross" withBar={false} onClick={() => setVisible(false)}></BsmButton>
{(!!sponsors.length || !!supporters.length) && <img className="absolute bottom-5 right-5 rotate-45 w-32 h-32" src={ManheraChanGif}/>}
<div className="w-full h-full overflow-y-scroll">
{!!sponsors.length && <SupportersBlock className="mt-12" title="sponsors" supporters={sponsors}/>}
{!!supporters.length && <SupportersBlock className="mt-12" title="supporters" supporters={supporters}/>}
{(!sponsors.length && !supporters.length) && (
<>
<h2 className="w-full text-center text-3xl font-bold mt-10 mb-24">No supporter yet</h2>
<img className="m-auto" src={ManheraSadGif} />
</>
)}
</div>
</motion.div>
})
</AnimatePresence>
)
}
+48 -39
View File
@@ -16,6 +16,7 @@ import { ModalExitCode, ModalService, ModalType } from "renderer/services/modale
import { NotificationService } from "renderer/services/notification.service";
import { ProgressBarService } from "renderer/services/progress-bar.service";
import { ThemeService } from "renderer/services/theme.service";
import { SupportersView } from "renderer/components/settings/supporters-view/supporters-view.component";
export function SettingsPage() {
@@ -45,6 +46,7 @@ export function SettingsPage() {
const[themeIdSelected, setThemeIdSelected]= useState(themeItem.find(e => e.value === themeService.getTheme()).id);
const[languageSelected, setLanguageSelected]= useState(languagesItems.find(e => e.value === i18nService.currentLanguage).id);
const [installationFolder, setInstallationFolder] = useState(null);
const [showSupporters, setShowSupporters] = useState(true);
useEffect(() => {
loadInstallationFolder();
@@ -110,49 +112,56 @@ export function SettingsPage() {
ipcService.sendLazy("new-window", {args: "https://www.patreon.com/bsmanager?fan_landing=true"})
}
return (
<div className="w-full h-full flex justify-center overflow-y-scroll scrollbar-thin scrollbar-thumb-neutral-900 text-gray-800 dark:text-gray-200">
const toogleShowSupporters = () => {
setShowSupporters(show => !show);
}
<div className="max-w-2xl w-full mt-10">
return (
<div className="w-full h-full flex justify-center overflow-y-scroll scrollbar-thin scrollbar-thumb-neutral-900 text-gray-800 dark:text-gray-200">
<SettingContainer title="pages.settings.steam.title" description="pages.settings.steam.description">
<BsmButton onClick={deleteSteamSession} className="w-fit px-3 py-[2px] text-white rounded-md" withBar={false} text="pages.settings.steam.logout" typeColor="error" disabled={!sessionExist}/>
</SettingContainer>
<div className="max-w-2xl w-full mt-10">
<SettingContainer title="pages.settings.steam.title" description="pages.settings.steam.description">
<BsmButton onClick={deleteSteamSession} className="w-fit px-3 py-[2px] text-white rounded-md" withBar={false} text="pages.settings.steam.logout" typeColor="error" disabled={!sessionExist}/>
</SettingContainer>
<SettingContainer title="pages.settings.appearance.title" description="pages.settings.appearance.description">
<div className="relative w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 flex justify-center rounded-md py-1">
<SettingColorChooser color={firstColor} onChange={setFirstColorSetting}/>
<SettingColorChooser color={secondColor} onChange={setSecondColorSetting}/>
<div className="absolute right-2 top-0 h-full flex items-center">
<BsmButton onClick={resetColors} className="px-2 font-bold italic text-sm rounded-md" text="pages.settings.appearance.reset" withBar={false}/>
</div>
</div>
<SettingContainer minorTitle="pages.settings.appearance.sub-title" className="mt-3">
<SettingRadioArray items={themeItem} selectedItem={themeIdSelected} onItemSelected={handleChangeTheme}/>
</SettingContainer>
</SettingContainer>
<SettingContainer title="pages.settings.installation-folder.title" description="pages.settings.installation-folder.description">
<div className="relative flex items-center justify-between w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 rounded-md pl-2 py-1">
<span className="block text-ellipsis overflow-hidden min-w-0" title={installationFolder}>{installationFolder}</span>
<BsmButton onClick={setDefaultInstallationFolder} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.installation-folder.choose-folder" withBar={false}/>
</div>
</SettingContainer>
<SettingContainer title="pages.settings.language.title" description="pages.settings.language.description">
<SettingRadioArray items={languagesItems} selectedItem={languageSelected} onItemSelected={handleChangeLanguage}/>
</SettingContainer>
<SettingContainer title="pages.settings.patreon.title" description="pages.settings.patreon.description">
<div className="flex">
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 whitespace-nowrap mr-2 !text-white" iconClassName="mr-1" text="pages.settings.patreon.buttons.support" icon="patreon" color="#EC6350" withBar={false} onClick={openPatreonPage}></BsmButton>
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="pages.settings.patreon.buttons.supporters" color="#6c5ce7" onClick={toogleShowSupporters}></BsmButton>
</div>
</SettingContainer>
<div className="h-10"/>
<SettingContainer title="pages.settings.appearance.title" description="pages.settings.appearance.description">
<div className="relative w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 flex justify-center rounded-md py-1">
<SettingColorChooser color={firstColor} onChange={setFirstColorSetting}/>
<SettingColorChooser color={secondColor} onChange={setSecondColorSetting}/>
<div className="absolute right-2 top-0 h-full flex items-center">
<BsmButton onClick={resetColors} className="px-2 font-bold italic text-sm rounded-md" text="pages.settings.appearance.reset" withBar={false}/>
</div>
</div>
<SettingContainer minorTitle="pages.settings.appearance.sub-title" className="mt-3">
<SettingRadioArray items={themeItem} selectedItem={themeIdSelected} onItemSelected={handleChangeTheme}/>
</SettingContainer>
</SettingContainer>
<SettingContainer title="pages.settings.installation-folder.title" description="pages.settings.installation-folder.description">
<div className="relative flex items-center justify-between w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 rounded-md pl-2 py-1">
<span className="block text-ellipsis overflow-hidden min-w-0" title={installationFolder}>{installationFolder}</span>
<BsmButton onClick={setDefaultInstallationFolder} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.installation-folder.choose-folder" withBar={false}/>
<SupportersView isVisible={showSupporters} setVisible={setShowSupporters}/>
</div>
</SettingContainer>
<SettingContainer title="pages.settings.language.title" description="pages.settings.language.description">
<SettingRadioArray items={languagesItems} selectedItem={languageSelected} onItemSelected={handleChangeLanguage}/>
</SettingContainer>
<SettingContainer title="pages.settings.patreon.title" description="pages.settings.patreon.description">
<div className="flex">
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 whitespace-nowrap mr-2 !text-white" iconClassName="mr-1" text="pages.settings.patreon.buttons.support" icon="patreon" color="#EC6350" withBar={false} onClick={openPatreonPage}></BsmButton>
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="pages.settings.patreon.buttons.supporters" color="#6c5ce7"></BsmButton>
</div>
</SettingContainer>
<div className="h-10"/>
</div>
</div>
)
)
}
@@ -0,0 +1,27 @@
import { Supporter } from "shared/models/supporters";
import { IpcService } from "./ipc.service";
export class SupportersService {
private static instance: SupportersService;
private ipcService: IpcService;
private constructor(){
this.ipcService = IpcService.getInstance();
}
public static getInstance(): SupportersService{
if(!SupportersService.instance){ SupportersService.instance = new SupportersService(); }
return SupportersService.instance;
}
public getSupporters(): Promise<Supporter[]>{
return this.ipcService.send<Supporter[]>("get-supporters").then(res => {
if(!res.success){ return null; }
return res.data;
});
}
}
+2
View File
@@ -0,0 +1,2 @@
export { SupporterType } from "./supporter.type";
export { Supporter } from "./supporter.interface";
@@ -0,0 +1,8 @@
import { SupporterType } from "./supporter.type";
export interface Supporter {
username: string,
type?: SupporterType,
link?: string,
image?: string,
}
@@ -0,0 +1,19 @@
import { SupporterType } from "./supporter.type";
import { SupporterInterface } from "./supporter.interface";
export class SupporterModel{
constructor(private args: SupporterInterface){}
public get username(): string{ return this.args.username; }
public get type(): SupporterType{ return this.args.type; }
public get link(): string{
if(this.type === "basic"){ return null; }
return this.args.link;
}
public get image(): string{
if(this.type !== "sponsor"){ return null; }
return this.args.image;
}
}
@@ -0,0 +1 @@
export type SupporterType = "gold"|"diamond"|"sponsor"