diff --git a/.eslintrc.cjs b/.eslintrc.cjs index affc776..d7091a6 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -31,6 +31,7 @@ module.exports = { '@typescript-eslint/no-var-requires': 'off', 'prefer-const': 'off', 'no-async-promise-executor': 'off', + 'no-prototype-builtins': 'off', }, overrides: [ { diff --git a/README.md b/README.md index 9d01d04..2bca6b0 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Electron starter kit with multiple features ## Github workflow -> [Figma link](https://www.figma.com/file/qJrFt4YVAZX5UdbeKLx6xA/LADA?type=whiteboard&t=oozV2tgJvZuRd6S4-1) +> [Figma link](https://www.figma.com/board/BGt9EJBWBnjcPCvKgEeES3/electron-nestjs-react-vite-template?node-id=304-58&t=NB3gHvd2vgOlaHfb-1) ![image](https://github.com/2skydev/electron-nestjs-react-vite-template/assets/43225384/aa9301fe-a6d9-4075-b5bc-4126dbc03e1a) diff --git a/resources/icons/logo@256.ico b/resources/icons/logo@256.ico index b802f0c..3b31eeb 100644 Binary files a/resources/icons/logo@256.ico and b/resources/icons/logo@256.ico differ diff --git a/resources/icons/logo@256.png b/resources/icons/logo@256.png index 7c49bbd..72d8815 100644 Binary files a/resources/icons/logo@256.png and b/resources/icons/logo@256.png differ diff --git a/resources/icons/logo@512.png b/resources/icons/logo@512.png index a834ad0..3290c01 100644 Binary files a/resources/icons/logo@512.png and b/resources/icons/logo@512.png differ diff --git a/resources/locales/en_US.json b/resources/locales/en_US.json index 42ef0e5..a07af3e 100644 --- a/resources/locales/en_US.json +++ b/resources/locales/en_US.json @@ -1,306 +1,18 @@ { "label": "English", - "league": { - "laneEn": { - "top": "Top", - "jg": "Jungle", - "mid": "Middle", - "adc": "ADC", - "sup": "Support" - }, - "laneId": [ - "$t(league.laneEn.top)", - "$t(league.laneEn.jg)", - "$t(league.laneEn.mid)", - "$t(league.laneEn.adc)", - "$t(league.laneEn.sup)" - ], - "gameMode": { - "CLASSIC": "Summoner's Rift", - "ARAM": "ARAM", - "URF": "U.R.F." - }, - "pickType": { - "SimulPickStrategy": "Blind Pick", - "TournamentPickStrategy": "Tournament Draft", - "DraftModeSinglePickStrategy": "Draft Pick", - "AllRandomPickStrategy": "All Random" - }, - "rune": { - "Shard": "Shard", - "HealthScaling": "Health Scaling", - "Armor": "Armor", - "MagicRes": "Magic Res", - "AttackSpeed": "Attack Speed", - "CDRScaling": "Skill Acceleration", - "AdaptiveForce": "Adaptive Force" - }, - /** - * Champion Name Alias - * - * [key] champion id - * [value] champion name alias array - * - * ex) "Aatrox": ["testName1", "abc"] - * => When searching for champions, you can search for "Aatrox" as "testName1", "abc" - * - * Champion ID Reference Link: http://ddragon.leagueoflegends.com/cdn/13.19.1/data/en_US/champion.json - */ - "championNameAlias": {}, - "statsRankRange": { - "EMERALD+": "EMERALD+", - "DIAMOND+": "DIAMOND+", - "MASTER+": "MASTER+", - "BSGP": "BSGP" - } - }, "main": { "contextMenu": { - "showHome": "Show LADA Home", + "showHome": "Show Home Screen", "setAppZoom": "Set App Display Scale", - "quit": "Quit LADA", + "quit": "Quit App", "nowValue": "Now", "resetWindowPosition": "Reset Window Position" } }, "renderer": { - "pages": { - "stats": "Statistics", - "championTier": "Champion Tiers", - "championDetail": "Champion Detail", - "duoSynergy": "Duo Synergy", - "live": "Live", - "championSelect": "Champion Select", - "inGame": "In Game", - "utility": "Utility", - "customGameTeamManager": "5:5 Team Manager", - "setting": "Setting", - "generalSetting": "General", - "gameSetting": "Game", - "developerSetting": "Developer", - "notFound": "Page Not Found :(" - }, - "form": { - "save": { - "title": "There are unsaved changes!", - "ok": "Save changes", - "rollback": "Rollback", - "confirm": { - "title": "Are you sure you want to save?", - "ok": "Save", - "cancel": "Cancel" - } - } - }, - "stats": { - "gameCount": "Count", - "sampledCount": "Sampled", - "winRate": "Win Rate", - "pickRate": "Pick Rate", - "banRate": "Ban Rate", - "spell": "Spell", - "skillBuild": "Skill Build", - "startItem": "Starting Item", - "shoes": "Shoes", - "coreItem": "Core Item", - "recommendItemBuild": "Recommended Item Build", - "championTier": { - "title": "Champion Tier by Lane", - "loading": "Loading...", - "timezone": "KST Timezone", - "minPickRateInfo": "Only showing lanes with pick rate above 0.5%", - "tableColumns": { - "score": "PS Score", - "honeyScore": "Honey Score" - } - }, - "champion": { - "counter": { - "easy": "Easy Matchup", - "hard": "Hard Matchup" - }, - "notFound": { - "title": "No data to display", - "description": "Please select a different lane." - } - }, - "championSelect": { - "notFound": { - "title": "Cannot fetch champion select information", - "description": "Either not currently in a game or unable to fetch champion select information." - } - }, - "duoSynergy": { - "title": "Duo Synergy", - "searchPlaceholder": "Search by Champion Name", - "tableColumns": { - "ranking": "Ranking", - "synergyScore": "Synergy Score", - "winRate": "Duo Win Rate", - "pickRate": "Duo Pick Rate" - }, - "lane": { - "adc-sup": "$t(league.laneEn.adc) & $t(league.laneEn.sup)", - "mid-jg": "$t(league.laneEn.mid) & $t(league.laneEn.jg)", - "top-jg": "$t(league.laneEn.top) & $t(league.laneEn.jg)", - "jg-sup": "$t(league.laneEn.jg) & $t(league.laneEn.sup)" - } - }, - "inGame": { - "title": "In-Game Information", - "tags": { - "gameType": "Solo/Duo Ranked", - "avg": "Average", - "avgNotFound": "Average tier information not available", - "playing": "In Game" - }, - "myTeam": "My Team", - "enemyTeam": "Enemy Team", - "rankStats": "Rank Stats", - "score": "PS Score", - "runeBuild": "Rune Build", - "game": "Game", - "notFound": { - "title": "Cannot fetch in-game information", - "description": "Either not currently in a game or unable to fetch in-game information.", - "reload": "Reload" - } - } - }, - "setting": { - "general": { - "autoLaunch": { - "title": "Automatically Start Minimized on Computer Boot", - "description": "Automatically starts the app in minimized mode after the computer is turned on." - }, - "openWindowWhenLeagueClientLaunch": { - "title": "Automatically Open LADA Window", - "description": "Opens LADA window automatically when the League of Legends client starts.\nLADA should be running in minimized mode." - }, - "language": { - "title": "Language Settings", - "description": "Sets the language for LADA.\nChanging the language requires a restart of the app." - }, - "zoom": { - "title": "App Zoom Level", - "description": "Sets the zoom level of the app screen.\nDefault is 100%." - }, - "developerMode": { - "title": "Developer Mode Settings", - "description": "Enables or disables developer mode.\nWhen enabled, developer tools are activated." - }, - "restoreWindowPosition": { - "title": "Restore Window Position", - "description": "Opens to the previous location when the LADA window is lit.\nIt always opens in the center of the screen when disabled." - }, - "appVersion": { - "title": "App Version", - "description": "Displays the current version of the app.\nYou can check the changes through the following link.", - "releaseList": "App Release List", - "updateNote": "Update Notes", - "updateStatus": { - "currentVersion": "Current Version", - "checking-for-update": { - "description": "Checking for updates..." - }, - "update-available": { - "description": "Update available. Downloading..." - }, - "update-not-available": { - "description": "Up to date.", - "checkForUpdate": "Check for Update" - }, - "error": { - "description": "Error occurred while checking for updates.", - "checkForUpdate": "Check for Update" - }, - "download-progress": { - "description": "Downloading..." - }, - "update-downloaded": { - "description": "Update downloaded.\nThe app will apply the update upon restart.", - "quitAndInstall": "Install Now" - } - } - } - }, - "game": { - "statsProvider": { - "title": "Statistics Provider", - "description": "Selects the service providing statistical information.\nCan change services providing data such as champion tier.\n\nCurrently supporting only LOL.PS." - }, - "useCurrentPositionChampionData": { - "title": "Champion Information - Priority by Current Position", - "description": "Displays champion information based on the current assigned position.\nDisabling this will show the champion's main position by default." - }, - "autoAccept": { - "title": "Auto Accept Matchmaking", - "description": "Automatically accepts game matchmaking.\nWhen enabled, matches are accepted without needing to click the accept button.", - "autoAcceptDelaySeconds": { - "label": "Time to Wait for Auto Accept", - "seconds": "seconds" - } - }, - "autoRuneSetting": { - "title": "Auto Rune Settings", - "description": "Automatically sets rune pages during champion selection." - }, - "autoSummonerSpellSetting": { - "title": "Auto Summoner Spell Settings", - "description": "Automatically sets summoner spells during champion selection.", - "flash": "Flash" - } - }, - "developer": { - "dataRefreshTime": { - "title": "Data Refresh Date", - "description": "Below is the refresh date for all data.\nIt automatically refreshes every {{minute}} minutes.\n\nTo manually update the data, click the refresh button on the right.", - "refresh": "reload" - }, - "storePath": { - "title": "Store Path", - "description": "Path where settings and data are stored." - }, - "logs": { - "title": "Logs", - "description": "Logs generated by the app.\nIf necessary, you can delete logs using the button on the right.", - "logSize": "Log Size", - "clear": "Clear Logs" - } - } - }, - "needUpdateLaterNotification": { - "title": "Update Pending", - "description": "An update is pending while you're in champion select.\nThe update will proceed when you restart the app.\n\nYou can also manually update: [General Settings > App Version]" - }, "updateLoading": { "loading": "Downloading Update...", "autoRestart": "The app will restart shortly." - }, - "teamManager": { - "subTitle": "Summoner's Rift · 5v5 · {{pickType}}", - "team": "Team", - "empty": "Empty", - "randomTeam": "Randomize Teams", - "reset": "Reset", - "notFound": { - "title": "Create a Custom Game", - "description": "Start a game > Create custom game > Choose Summoner's Rift as the map." - } - }, - "updateNote": "Update Notes", - "autoAcceptOverlay": "Auto accept in {{seconds}} seconds", - "languageChangeInfoModal": { - "title": "App Restart Required", - "description": "The app needs to restart to apply the language changes.", - "ok": "Restart Now", - "later": "Later" - }, - "error": { - "title": "Seems like you wandered into unknown territory", - "description": "We'll investigate. It might be best for you to head back.", - "back": "Go Back", - "errorContent": "Error Content" } } } \ No newline at end of file diff --git a/resources/locales/ko_KR.json b/resources/locales/ko_KR.json index 0aa49ac..588c7f5 100644 --- a/resources/locales/ko_KR.json +++ b/resources/locales/ko_KR.json @@ -1,130 +1,8 @@ { "label": "한국어", - "league": { - "laneEn": { - "top": "탑", - "jg": "정글", - "mid": "미드", - "adc": "원딜", - "sup": "서폿" - }, - "laneId": [ - "$t(league.laneEn.top)", - "$t(league.laneEn.jg)", - "$t(league.laneEn.mid)", - "$t(league.laneEn.adc)", - "$t(league.laneEn.sup)" - ], - "gameMode": { - "CLASSIC": "소환사의 협곡", - "ARAM": "칼바람 나락", - "URF": "우르프" - }, - "pickType": { - "SimulPickStrategy": "비공개 선택", - "TournamentPickStrategy": "토너먼트 드래프트", - "DraftModeSinglePickStrategy": "교차 선택", - "AllRandomPickStrategy": "모두 무작위" - }, - "rune": { - "Shard": "파편", - "HealthScaling": "체력 증가", - "Armor": "방어력", - "MagicRes": "마법 저항력", - "AttackSpeed": "공격 속도", - "CDRScaling": "스킬 가속", - "AdaptiveForce": "적응형 능력치" - }, - /** - * Champion Name Alias - * - * [key] champion id - * [value] champion name alias array - * - * ex) "Aatrox": ["testName1", "abc"] - * => When searching for champions, you can search for "Aatrox" as "testName1", "abc" - * - * Champion ID Reference Link: http://ddragon.leagueoflegends.com/cdn/13.19.1/data/en_US/champion.json - */ - "championNameAlias": { - "Tristana": [ - "트타" - ], - "TwistedFate": [ - "트페" - ], - "Tryndamere": [ - "트린", - "트란" - ], - "Blitzcrank": [ - "블츠", - "깡통" - ], - "Heimerdinger": [ - "하딩" - ], - "Volibear": [ - "볼베" - ], - "MasterYi": [ - "마이" - ], - "Graves": [ - "그브" - ], - "AurelionSol": [ - "아솔", - "아우솔" - ], - "Caitlyn": [ - "케틀" - ], - "Gragas": [ - "글가" - ], - "Yuumi": [ - "고양이", - "라면" - ], - "MissFortune": [ - "미포" - ], - "Draven": [ - "드븐" - ], - "Warwick": [ - "워웍" - ], - "Malphite": [ - "돌" - ], - "Vladimir": [ - "모기" - ], - "Morgana": [ - "몰가" - ], - "Gwen": [ - "궨" - ], - "DrMundo": [ - "문박" - ], - "Poppy": [ - "삐뽀" - ] - }, - "statsRankRange": { - "EMERALD+": "에메랄드+", - "DIAMOND+": "다이아+", - "MASTER+": "마스터+", - "BSGP": "브실골플" - } - }, "main": { "contextMenu": { - "showHome": "LADA 홈 화면 보기", + "showHome": "홈 화면 보기", "setAppZoom": "앱 비율 설정", "quit": "앱 끄기", "nowValue": "현재값", @@ -132,243 +10,9 @@ } }, "renderer": { - "pages": { - "stats": "통계", - "championTier": "챔피언 티어", - "championDetail": "챔피언 상세", - "duoSynergy": "듀오 시너지", - "live": "라이브 게임", - "championSelect": "챔피언 선택", - "inGame": "인게임 정보", - "utility": "유틸리티", - "customGameTeamManager": "5:5 팀 구성", - "setting": "설정", - "generalSetting": "일반 설정", - "gameSetting": "게임 설정", - "developerSetting": "개발자 설정", - "notFound": "페이지를 찾을 수 없습니다 :(" - }, - "form": { - "save": { - "title": "저장하지 않은 변경 사항이 있어요!", - "ok": "변경사항 저장하기", - "rollback": "되돌리기", - "confirm": { - "title": "정말 저장하시겠습니까?", - "ok": "저장", - "cancel": "취소" - } - } - }, - "stats": { - "gameCount": "게임수", - "sampledCount": "표본수", - "winRate": "승률", - "pickRate": "픽률", - "banRate": "밴률", - "spell": "스펠", - "skillBuild": "스킬 빌드", - "startItem": "시작 아이템", - "shoes": "신발", - "coreItem": "코어 아이템", - "recommendItemBuild": "추천 아이템 빌드", - "championTier": { - "title": "라인별 챔피언 티어", - "loading": "불러오는 중...", - "timezone": "KST 기준", - "minPickRateInfo": "라인별 픽률 0.5% 이상만 표시", - "tableColumns": { - "score": "PS 스코어", - "honeyScore": "꿀챔 점수" - } - }, - "champion": { - "counter": { - "easy": "상대하기 쉬운 챔피언", - "hard": "상대하기 어려운 챔피언" - }, - "notFound": { - "title": "표시할 데이터가 없습니다", - "description": "다른 라인을 선택해주세요" - } - }, - "championSelect": { - "notFound": { - "title": "챔피언 선택 정보를 불러올 수 없습니다.", - "description": "현재 게임에 참여중이지 않거나, 챔피언 선택 정보를 불러올 수 없는 상태입니다." - } - }, - "duoSynergy": { - "title": "듀오 시너지", - "searchPlaceholder": "챔피언 이름으로 검색", - "tableColumns": { - "ranking": "순위", - "synergyScore": "시너지 점수", - "winRate": "듀오 승률", - "pickRate": "듀오 픽률" - }, - "lane": { - "adc-sup": "바텀", - "mid-jg": "미드 & 정글", - "top-jg": "탑 & 정글", - "jg-sup": "정글 & 서폿" - } - }, - "inGame": { - "title": "인게임 정보", - "tags": { - "gameType": "개인/2인 랭크", - "avg": "평균", - "avgNotFound": "평균 티어 정보 없음", - "playing": "게임중" - }, - "myTeam": "아군", - "enemyTeam": "적군", - "rankStats": "랭크 통계", - "score": "PS Score", - "runeBuild": "룬 빌드", - "game": "게임", - "notFound": { - "title": "인게임 정보를 불러올 수 없습니다.", - "description": "현재 게임에 참여중이지 않거나, 인게임 정보를 불러올 수 없는 상태입니다.", - "reload": "새로고침" - } - } - }, - "setting": { - "general": { - "autoLaunch": { - "title": "컴퓨터 부팅시 최소화로 자동 시작", - "description": "컴퓨터가 켜진 후 자동으로 앱이 최소화 모드로 시작되도록 설정합니다." - }, - "openWindowWhenLeagueClientLaunch": { - "title": "LADA 자동 창 열기", - "description": "롤 클라이언트가 켜지면 자동으로 LADA 창이 열립니다.\nLADA가 최소화 모드로 실행되어 있어야 합니다." - }, - "language": { - "title": "언어 설정", - "description": "LADA의 언어를 설정합니다.\n언어를 변경하면 앱이 재시작됩니다." - }, - "zoom": { - "title": "앱 비율 설정", - "description": "앱 화면의 비율을 설정합니다.\n기본 값은 100% 입니다." - }, - "developerMode": { - "title": "개발자모드 설정", - "description": "개발자모드를 활성화할지 설정합니다.\n개발자모드가 활성화되면 개발자 도구가 활성화됩니다." - }, - "restoreWindowPosition": { - "title": "창 위치 복원", - "description": "LADA 창이 켜질 때 이전 위치로 열립니다.\n비활성화 시 항상 화면 중앙에 열립니다." - }, - "appVersion": { - "title": "앱 버전", - "description": "현재 앱 버전이 몇인지 확인하실 수 있습니다.\n아래 링크를 통해 변경된 사항을 확인하실 수 있습니다.", - "releaseList": "앱 릴리즈 목록", - "updateNote": "업데이트 내역", - "updateStatus": { - "currentVersion": "현재버전", - "checking-for-update": { - "description": "업데이트를 확인중입니다..." - }, - "update-available": { - "description": "업데이트가 있습니다. 다운로드중입니다..." - }, - "update-not-available": { - "description": "최신 버전입니다.", - "checkForUpdate": "업데이트 확인" - }, - "error": { - "description": "업데이트를 확인하는 도중 오류가 발생했습니다.", - "checkForUpdate": "업데이트 확인" - }, - "download-progress": { - "description": "다운로드중입니다..." - }, - "update-downloaded": { - "description": "업데이트가 다운로드 되었습니다.\n앱을 재시작하면 업데이트가 적용됩니다.", - "quitAndInstall": "지금 설치" - } - } - } - }, - "game": { - "statsProvider": { - "title": "통계 정보 제공자", - "description": "통계 정보를 제공하는 서비스를 선택합니다.\n챔피언 티어와 같은 통계 관련된 데이터를 제공하는 서비스를 변경할 수 있습니다.\n\n현재는 LOL.PS 만 지원합니다." - }, - "useCurrentPositionChampionData": { - "title": "챔피언 정보 - 배치된 포지션 우선", - "description": "챔피언 선택 시 보여주는 정보를 현재 배치된 포지션 우선으로 표시됩니다.\n이 설정을 비활성화하면 챔피언 선택 시 해당 챔피언의 주 포지션으로 표시됩니다." - }, - "autoAccept": { - "title": "매칭 자동 수락", - "description": "게임 매칭을 자동으로 수락할지 설정합니다.\n이 설정을 활성화하면 수락 버튼을 누르지 않아도 매칭이 수락됩니다.", - "autoAcceptDelaySeconds": { - "label": "자동 수락까지 대기시간", - "seconds": "초" - } - }, - "autoRuneSetting": { - "title": "자동 룬 설정", - "description": "챔피언 선택 시 자동으로 룬 페이지를 설정합니다." - }, - "autoSummonerSpellSetting": { - "title": "자동 스펠 설정", - "description": "챔피언 선택 시 자동으로 스펠을 설정합니다.", - "flash": "점멸" - } - }, - "developer": { - "dataRefreshTime": { - "title": "데이터 조회일", - "description": "아래 모든 데이터의 조회일입니다.\n자동으로 {{minute}}분에 한번씩 새로고침됩니다.\n\n바로 데이터를 업데이트하려면 우측 새로고침 버튼을 눌러주세요.", - "refresh": "새로고침" - }, - "storePath": { - "title": "Store Path", - "description": "설정 및 데이터가 저장되는 경로입니다." - }, - "logs": { - "title": "Logs", - "description": "앱에서 남긴 로그들입니다.\n필요한 경우 우측 버튼을 통해 로그를 삭제할 수 있습니다.", - "logSize": "로그 용량", - "clear": "로그 비우기" - } - } - }, - "needUpdateLaterNotification": { - "title": "업데이트 미뤄짐", - "description": "챔피언 선택중이라 업데이트가 미뤄졌습니다.\n앱을 재시작하면 업데이트가 진행됩니다.\n\n수동으로도 업데이트 가능합니다. [일반 설정 > 앱 버전]" - }, "updateLoading": { "loading": "업데이트 다운로드중...", "autoRestart": "잠시 후 앱이 재시작됩니다." - }, - "teamManager": { - "subTitle": "소환사의 협곡 · 5대5 · {{pickType}}", - "team": "팀", - "empty": "비어있음", - "randomTeam": "팀 추첨", - "reset": "초기화", - "notFound": { - "title": "사용자 설정 게임을 생성해주세요.", - "description": "게임시작 > 사용자 설정 게임 생성 > 소환사의 협곡으로 생성해주세요." - } - }, - "updateNote": "업데이트 내역", - "autoAcceptOverlay": "자동 수락까지 {{seconds}}초 남았습니다", - "languageChangeInfoModal": { - "title": "앱 재시작 필요", - "description": "변경한 언어로 적용하기 위해 앱 재시작이 필요합니다.", - "ok": "앱 재시작", - "later": "미루기" - }, - "error": { - "title": "알 수 없는 곳으로 와버린 거 같아요", - "description": "원인은 저희가 찾아볼게요 여러분들은 다시 돌아가는 게 좋을 것 같아요", - "back": "돌아가기", - "errorContent": "오류 내용" } } } \ No newline at end of file diff --git a/src/main/modules/config/config.service.ts b/src/main/modules/config/config.service.ts index bd94155..e1625d1 100644 --- a/src/main/modules/config/config.service.ts +++ b/src/main/modules/config/config.service.ts @@ -17,7 +17,7 @@ export class ConfigService { oldValue: FieldPathValue, ) => void, ) { - // @ts-ignore: key 타입 무시 + // @ts-ignore: ignore key type return this.store.onDidChange(key, callback) } @@ -30,7 +30,7 @@ export class ConfigService { public get = FieldPath>( key: Key, ): FieldPathValue { - // @ts-ignore: key 타입 무시 + // @ts-ignore: ignore key type return this.store.get(key) } @@ -38,7 +38,7 @@ export class ConfigService { key: Key, value: FieldPathValue, ) { - // @ts-ignore: key 타입 무시 + // @ts-ignore: ignore key type this.store.set(key, value) } diff --git a/src/main/modules/electron/electron.service.ts b/src/main/modules/electron/electron.service.ts index 3a4ce0a..1e67820 100644 --- a/src/main/modules/electron/electron.service.ts +++ b/src/main/modules/electron/electron.service.ts @@ -68,7 +68,7 @@ export class ElectronService implements OnModuleInit, OnApplicationBootstrap { // deep link handlers public deepLinkHandlers: Record void> = {} - // update.module.ts -> autoUpdate() 참고 + // update.module.ts -> autoUpdate() public isNeedUpdate = false public isNeedUpdateLater = false @@ -233,7 +233,7 @@ export const generatedIpcOnContext = {` await writeFile(`src/preload/generated-ipc-on-context.ts`, `${importString}\n${contentString}`) } - // 앱 시작 (src/main/index.ts에서 실행) + // execute by `src/main/index.ts` @ExecuteLog() public async start() { console.log('start', !this.IS_HIDDEN_LAUNCH && !this.isNeedUpdate) @@ -264,7 +264,7 @@ export const generatedIpcOnContext = {` this.window = new BrowserWindow({ width: this.APP_WIDTH, height: this.APP_HEIGHT, - backgroundColor: '#36393F', + backgroundColor: '#2F3242', darkTheme: true, show: false, autoHideMenuBar: true, diff --git a/src/main/modules/migration/migration.module.ts b/src/main/modules/migration/migration.module.ts index 600e586..f1ab650 100644 --- a/src/main/modules/migration/migration.module.ts +++ b/src/main/modules/migration/migration.module.ts @@ -20,7 +20,7 @@ export class MigrationModule { let migratedVersions = migrationStore.get('migratedVersions') let executedMigrationVersions = migrationStore.get('executedMigrationVersions') - // 마이그레이션 스토어가 비어있는 경우 + // If the migration store is empty if (!migratedVersions || !executedMigrationVersions) { migratedVersions = [] executedMigrationVersions = [] @@ -32,27 +32,27 @@ export class MigrationModule { } } - // 선언된 모든 마이그레이션 불러오기 + // Import all declared migrations const migrationVersions = Object.getOwnPropertyNames(this).filter(propertyName => valid(propertyName), ) if (isInitialInstallation) { - // 초기 설치라면 현재 모든 마이그레이션을 실행할 필요가 없으므로 실행된 것으로 간주 + // If it's an initial installation, you don't need to run all current migrations, so consider it executed migrationStore.set('migratedVersions', migrationVersions) } else { - // 초기 설치가 아니라면 마이그레이션 실행 + // Run migration unless it is an initial installation const executableMigrationVersions = migrationVersions.filter(version => { const isExecutable = !migratedVersions!.includes(version) - // 개발 모드라면 버전 비교 없이 실행 + // Run without version comparison if in development mode if (!app.isPackaged) return isExecutable - // 프로덕션 모드라면 하위 및 같은 버전만 실행 + // If it's production mode, run only the lower and the same versions return isExecutable && lte(version, currentVersion) }) - // 실행 가능한 마이그레이션이 있다면 실행 + // Run if there is a viable migration if (executableMigrationVersions.length) { for (const version of executableMigrationVersions) { try { diff --git a/src/main/modules/update/update.service.ts b/src/main/modules/update/update.service.ts index 48e2d6c..44f42f3 100644 --- a/src/main/modules/update/update.service.ts +++ b/src/main/modules/update/update.service.ts @@ -51,7 +51,7 @@ export class UpdateService implements OnModuleInit { this.controller = this.moduleRef.get(UpdateController) } - // 자동 업데이트 (src/main/index.ts에서 실행) + // execute by `src/main/index.ts` @ExecuteLog() async autoUpdate() { return new Promise(async resolve => { @@ -91,7 +91,7 @@ export class UpdateService implements OnModuleInit { this.updateLoadingWindow = new BrowserWindow({ width: 300, height: 300, - backgroundColor: '#36393F', + backgroundColor: '#2F3242', darkTheme: true, show: false, autoHideMenuBar: true, @@ -103,6 +103,10 @@ export class UpdateService implements OnModuleInit { }, }) + this.updateLoadingWindow.on('ready-to-show', () => { + this.updateLoadingWindow?.show() + }) + if (app.isPackaged) { await this.updateLoadingWindow.loadFile(this.electronService.PROD_LOAD_FILE_PATH, { hash: '#/windows/update-loading', @@ -112,10 +116,6 @@ export class UpdateService implements OnModuleInit { this.electronService.DEV_URL + '#/windows/update-loading', ) } - - this.updateLoadingWindow.on('ready-to-show', () => { - this.updateLoadingWindow?.show() - }) } private handleUpdateEvent(event: UpdateStatusEvent) { diff --git a/src/renderer/index.html b/src/renderer/index.html index c86f53d..9d81b92 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -3,8 +3,6 @@ - - electron-nestjs-react-vite-template diff --git a/src/renderer/src/assets/update-loading.css b/src/renderer/src/assets/update-loading.css new file mode 100644 index 0000000..1d1213a --- /dev/null +++ b/src/renderer/src/assets/update-loading.css @@ -0,0 +1,52 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, +body { + background-color: transparent; + font-size: 14px; +} + +.update-loading-window { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + img { + height: 100px; + margin-bottom: 2rem; + } + + .text { + font-size: 1rem; + color: white; + margin-bottom: 1rem; + } + + progress { + width: 200px; + height: 6px; + appearance: none; + } + + progress::-webkit-progress-bar { + width: 100%; + height: 100%; + border-radius: 999px; + background: rgba(162, 236, 251, 0.2); + overflow: hidden; + } + + progress::-webkit-progress-value { + height: 100%; + border-radius: 999px; + background: #a2ecfb; + transition: all 250ms; + } +} diff --git a/src/renderer/src/components/FileSystemRouter/FileSystemRouter.tsx b/src/renderer/src/components/FileSystemRouter/FileSystemRouter.tsx index 2c48245..1fe08c9 100644 --- a/src/renderer/src/components/FileSystemRouter/FileSystemRouter.tsx +++ b/src/renderer/src/components/FileSystemRouter/FileSystemRouter.tsx @@ -10,7 +10,7 @@ interface Module { export interface ErrorFallbackProps {} const PRESERVED = import.meta.glob('/src/pages/(_app|_error).tsx', { eager: true }) -const ROUTES = import.meta.glob('/src/pages/**/[a-z[]*.tsx', { eager: true }) +const ROUTES = import.meta.glob('/src/pages/**/[a-z[]*.tsx') const preservedRoutes: Partial> = Object.keys(PRESERVED).reduce( (routes, key) => { @@ -26,13 +26,16 @@ const ErrorFallback = preservedRoutes?.['_error'] || Fragment const router = createHashRouter([ { path: '/', - element: , - errorElement: , + Component: App, + ErrorBoundary: ErrorFallback, children: Object.keys(ROUTES).reduce((routes, key) => { const module = ROUTES[key] const route: RouteObject = { - element: , + lazy: async () => { + const { default: Component } = await module() + return { Component } + }, } const segments = key diff --git a/src/renderer/src/index.tsx b/src/renderer/src/index.tsx index 62e32f2..1a2d32b 100644 --- a/src/renderer/src/index.tsx +++ b/src/renderer/src/index.tsx @@ -1,6 +1,19 @@ import FileSystemRouter from '@renderer/components/FileSystemRouter/FileSystemRouter' -import '@renderer/assets/main.css' +import i18next from 'i18next' import ReactDOM from 'react-dom/client' +import { initReactI18next } from 'react-i18next' + +const currentResource = await window.electron.getCurrentI18nextResource() + +await i18next.use(initReactI18next).init({ + lng: currentResource.language, + resources: { + [currentResource.language]: { + [currentResource.ns]: currentResource.resource, + }, + }, + debug: import.meta.env.MODE === 'development', +}) ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render() diff --git a/src/renderer/src/pages/_app.tsx b/src/renderer/src/pages/_app.tsx index de88cbf..7661423 100644 --- a/src/renderer/src/pages/_app.tsx +++ b/src/renderer/src/pages/_app.tsx @@ -1,10 +1,8 @@ -import { Link, Outlet } from 'react-router-dom' +import { Outlet } from 'react-router-dom' const App = () => { return ( <> - Home - About ) diff --git a/src/renderer/src/pages/index.tsx b/src/renderer/src/pages/index.tsx index a43e7b5..65f9f32 100644 --- a/src/renderer/src/pages/index.tsx +++ b/src/renderer/src/pages/index.tsx @@ -1,9 +1,14 @@ +import '@renderer/assets/main.css' + import Versions from '@renderer/components/Versions' import electronLogo from '@renderer/assets/electron.svg' +import { Link } from 'react-router-dom' function HomePage(): JSX.Element { return ( <> + About + logo
Powered by electron-vite
diff --git a/src/renderer/src/pages/windows/update-loading.tsx b/src/renderer/src/pages/windows/update-loading.tsx new file mode 100644 index 0000000..d465638 --- /dev/null +++ b/src/renderer/src/pages/windows/update-loading.tsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import '@renderer/assets/update-loading.css' +import logoImage from '@renderer/assets/electron.svg' +import useSWR from 'swr' + +const UpdateLoadingPage = () => { + const { t } = useTranslation('translation', { + keyPrefix: 'renderer.updateLoading', + }) + + const [percent, setPercent] = useState(0) + const [text, setText] = useState(t('loading')) + + const { data: status, mutate } = useSWR('update-status', window.electron.getUpdateStatus) + + useEffect(() => { + window.electron.onChangeUpdateStatus(status => { + mutate(status, { + revalidate: false, + }) + }) + }, []) + + useEffect(() => { + if (!status) return + + switch (status.event) { + case 'download-progress': { + if (percent !== 100) setPercent(Number(status.data.percent)) + break + } + + case 'update-downloaded': { + setText(t('autoRestart')) + break + } + } + }, [status]) + + return ( +
+ logo +
{text}
+ +
+ ) +} + +export default UpdateLoadingPage