Skip to content

Commit dc2a6d7

Browse files
feat: Add option to clone existing profile (#761)
* add dev button to refetch profiles * add dialog box with a table of all profiles * add button to delete non default profiles to dialog box * add button to clone profile to dialog box * style: Add trailing comma * docs: Add doc comment explaining function * fix: Remove leftover testing code * fix: Delete duplicate function registration * chore: Sort Tauri function registrations alphabetically --------- Co-authored-by: Jan200101 <[email protected]>
1 parent 4d930dc commit dc2a6d7

File tree

5 files changed

+64
-2
lines changed

5 files changed

+64
-2
lines changed

src-tauri/src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ fn main() {
138138
northstar::install::install_northstar_wrapper,
139139
northstar::install::update_northstar,
140140
northstar::launch_northstar,
141+
northstar::profile::clone_profile,
141142
northstar::profile::delete_profile,
142143
northstar::profile::fetch_profiles,
143144
northstar::profile::validate_profile,

src-tauri/src/northstar/profile.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::util::copy_dir_all;
12
use crate::GameInstall;
23

34
// These folders are part of Titanfall 2 and
@@ -91,3 +92,30 @@ pub fn delete_profile(game_install: GameInstall, profile: String) -> Result<(),
9192
Err(err) => Err(format!("Failed to delete Profile: {}", err)),
9293
}
9394
}
95+
96+
/// Clones a profile by simply duplicating the folder under a new name
97+
#[tauri::command]
98+
pub fn clone_profile(
99+
game_install: GameInstall,
100+
old_profile: String,
101+
new_profile: String,
102+
) -> Result<(), String> {
103+
// Check if the old Profile already exists
104+
if !validate_profile(game_install.clone(), old_profile.clone()) {
105+
return Err(format!("{} is not a valid Profile", old_profile));
106+
}
107+
108+
// Check that new Profile does not already exist
109+
if validate_profile(game_install.clone(), new_profile.clone()) {
110+
return Err(format!("{} already exists", new_profile));
111+
}
112+
113+
log::info!("Cloning Profile {} to {}", old_profile, new_profile);
114+
115+
let old_profile_path = format!("{}/{}", game_install.game_path, old_profile);
116+
let new_profile_path = format!("{}/{}", game_install.game_path, new_profile);
117+
118+
copy_dir_all(old_profile_path, new_profile_path).unwrap();
119+
120+
Ok(())
121+
}

src-tauri/src/util.rs

-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ pub fn check_northstar_running() -> bool {
188188
}
189189

190190
/// Copies a folder and all its contents to a new location
191-
#[allow(dead_code)]
192191
pub fn copy_dir_all(
193192
src: impl AsRef<std::path::Path>,
194193
dst: impl AsRef<std::path::Path>,

src-vue/src/i18n/lang/en.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@
119119
"title": "Profiles",
120120
"delete_confirm": "Are you sure to delete this profile?",
121121
"delete": "Delete",
122-
"clone": "Clone"
122+
"clone": "Clone",
123+
"new_profile_name": "Enter the new Profile name",
124+
"create_empty": "New Profile"
123125
}
124126
},
125127

src-vue/src/views/SettingsView.vue

+32
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
</el-button>
2222
</template>
2323
</el-popconfirm>
24+
<el-button @click="cloneProfileDialog(scope.row.name)">
25+
{{ $t('settings.profile.dialog.clone') }}
26+
</el-button>
2427
</template>
2528
</el-table-column>
2629
</el-table>
@@ -140,6 +143,7 @@ import LanguageSelector from "../components/LanguageSelector.vue";
140143
const persistentStore = new Store('flight-core-settings.json');
141144
import { open } from '@tauri-apps/api/shell';
142145
import { i18n } from '../main';
146+
import { ElMessageBox } from 'element-plus'
143147
144148
export default defineComponent({
145149
name: "SettingsView",
@@ -274,6 +278,34 @@ export default defineComponent({
274278
showErrorNotification(error);
275279
});
276280
},
281+
async cloneProfileDialog(profile: string) {
282+
ElMessageBox.prompt(
283+
i18n.global.tc("settings.profile.dialog.new_profile_name"),
284+
i18n.global.tc("settings.profile.dialog.title"),
285+
{
286+
confirmButtonText: i18n.global.tc("generic.confirm"),
287+
cancelButtonText: i18n.global.tc("generic.cancel"),
288+
}
289+
).then(async ({ value }) => {
290+
await this.cloneProfile(profile, value);
291+
}).catch(() => {
292+
// Nothing to do here
293+
})
294+
},
295+
async cloneProfile(old_profile: string, new_profile: string) {
296+
let store = this.$store;
297+
await invoke("clone_profile", {
298+
gameInstall: store.state.game_install,
299+
oldProfile: old_profile,
300+
newProfile: new_profile
301+
}).then(async (message) => {
302+
store.commit('fetchProfiles');
303+
showNotification('Success');
304+
}).catch((error) => {
305+
console.error(error);
306+
showErrorNotification(error);
307+
});
308+
},
277309
async deleteProfile(profile: string) {
278310
let store = this.$store;
279311
await invoke("delete_profile", {

0 commit comments

Comments
 (0)