diff --git a/frontend/package.json b/frontend/package.json
index 001b0f00a..d88275828 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -23,7 +23,7 @@
"@angular/router": "^16.2.9",
"@angular/service-worker": "^16.2.9",
"@ctrl/ngx-codemirror": "^7.0.0",
- "@mean-stream/ngbx": "^0.11.0",
+ "@mean-stream/ngbx": "^0.12.0",
"@ng-bootstrap/ng-bootstrap": "^15.1.1",
"@popperjs/core": "^2.11.8",
"@sentry/angular-ivy": "^7.74.0",
@@ -32,6 +32,7 @@
"bootstrap": "~5.2.3",
"bootstrap-darkmode": "^5.0.1",
"bootstrap-icons": "^1.11.1",
+ "class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"codemirror": "^5.65.15",
"file-saver": "^2.0.5",
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index 4f515d636..98bc34099 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.1'
+lockfileVersion: '6.0'
settings:
autoInstallPeers: true
@@ -45,8 +45,8 @@ dependencies:
specifier: ^7.0.0
version: 7.0.0(@angular/core@16.2.9)(@angular/forms@16.2.9)(codemirror@5.65.15)
'@mean-stream/ngbx':
- specifier: ^0.11.0
- version: 0.11.0(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/forms@16.2.9)(@angular/router@16.2.9)(@ng-bootstrap/ng-bootstrap@15.1.1)(bootstrap-darkmode@5.0.1)(bootstrap@5.2.3)(class-validator@0.14.0)(rxjs@7.8.1)
+ specifier: ^0.12.0
+ version: 0.12.0(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/router@16.2.9)(@ng-bootstrap/ng-bootstrap@15.1.1)(bootstrap-darkmode@5.0.1)(bootstrap@5.2.3)(class-validator@0.14.0)
'@ng-bootstrap/ng-bootstrap':
specifier: ^15.1.1
version: 15.1.1(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/forms@16.2.9)(@angular/localize@16.2.9)(@popperjs/core@2.11.8)(rxjs@7.8.1)
@@ -71,6 +71,9 @@ dependencies:
bootstrap-icons:
specifier: ^1.11.1
version: 1.11.1
+ class-transformer:
+ specifier: ^0.5.1
+ version: 0.5.1
class-validator:
specifier: ^0.14.0
version: 0.14.0
@@ -2288,19 +2291,17 @@ packages:
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
dev: true
- /@mean-stream/ngbx@0.11.0(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/forms@16.2.9)(@angular/router@16.2.9)(@ng-bootstrap/ng-bootstrap@15.1.1)(bootstrap-darkmode@5.0.1)(bootstrap@5.2.3)(class-validator@0.14.0)(rxjs@7.8.1):
- resolution: {integrity: sha512-KYtpF3VlaoLb2QCQg4EPl/mD54207tYwzj1PKqYWa9m3AvIUBzt0HLYvJtNJL9LholwhGV1kxEdp9YJ0r3JgKQ==}
+ /@mean-stream/ngbx@0.12.0(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/router@16.2.9)(@ng-bootstrap/ng-bootstrap@15.1.1)(bootstrap-darkmode@5.0.1)(bootstrap@5.2.3)(class-validator@0.14.0):
+ resolution: {integrity: sha512-rXuaI0YrZUSFToMD+XTyQSBI/KFRVbNo4NpSQ2cBjD+ck8kon0xNflq/BnzgSxUuO9T6FDlpZED17Cnnj0p7/Q==}
peerDependencies:
- '@angular/common': ^15.2.0
- '@angular/core': ^15.2.0
- '@angular/forms': 15.2.8
- '@angular/router': ^15.2.0
- '@ng-bootstrap/ng-bootstrap': ^14.0.0
+ '@angular/common': ^16.2.0
+ '@angular/core': ^16.2.0
+ '@angular/router': ^16.2.0
+ '@ng-bootstrap/ng-bootstrap': ^15.1.0
bootstrap: ^5.2.3
bootstrap-darkmode: ^0.9.1
class-validator: ^0.14.0
reflect-metadata: ^0.1.13
- rxjs: 7.8.1
peerDependenciesMeta:
bootstrap-darkmode:
optional: true
@@ -2311,13 +2312,11 @@ packages:
dependencies:
'@angular/common': 16.2.9(@angular/core@16.2.9)(rxjs@7.8.1)
'@angular/core': 16.2.9(rxjs@7.8.1)(zone.js@0.13.3)
- '@angular/forms': 16.2.9(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/platform-browser@16.2.9)(rxjs@7.8.1)
'@angular/router': 16.2.9(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/platform-browser@16.2.9)(rxjs@7.8.1)
'@ng-bootstrap/ng-bootstrap': 15.1.1(@angular/common@16.2.9)(@angular/core@16.2.9)(@angular/forms@16.2.9)(@angular/localize@16.2.9)(@popperjs/core@2.11.8)(rxjs@7.8.1)
bootstrap: 5.2.3(@popperjs/core@2.11.8)
bootstrap-darkmode: 5.0.1(bootstrap@5.2.3)
class-validator: 0.14.0
- rxjs: 7.8.1
tslib: 2.6.2
dev: false
@@ -3905,6 +3904,10 @@ packages:
engines: {node: '>=6.0'}
dev: true
+ /class-transformer@0.5.1:
+ resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
+ dev: false
+
/class-validator@0.14.0:
resolution: {integrity: sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==}
dependencies:
@@ -7177,6 +7180,7 @@ packages:
/prr@1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+ requiresBuild: true
dev: true
optional: true
@@ -7494,6 +7498,7 @@ packages:
/sax@1.3.0:
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
+ requiresBuild: true
dev: true
optional: true
diff --git a/frontend/src/app/assignment/assignment.module.ts b/frontend/src/app/assignment/assignment.module.ts
index 36c0ca247..006c76ed3 100644
--- a/frontend/src/app/assignment/assignment.module.ts
+++ b/frontend/src/app/assignment/assignment.module.ts
@@ -4,14 +4,13 @@ import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
-import {ModalModule} from '@mean-stream/ngbx';
+import {FormsModule as NgbxFormsModule, ModalModule} from '@mean-stream/ngbx';
import {ClipboardModule} from 'ngx-clipboard';
import {DndModule} from 'ngx-drag-drop';
import {SharedModule} from '../shared/shared.module';
import {AssignmentRoutingModule} from './assignment-routing.module';
import {AssignmentSharedModule} from './modules/shared/shared.module';
-import {ConfigFormComponent} from './pages/config-form/config-form.component';
import {CreateSolutionComponent} from './pages/create-solution/create-solution.component';
import {MyAssignmentsComponent} from './pages/my-assignments/my-assignments.component';
import {MySolutionsComponent} from './pages/my-solutions/my-solutions.component';
@@ -42,7 +41,6 @@ import {KeycloakBearerInterceptor} from "keycloak-angular";
TokenModalComponent,
OverviewComponent,
SettingsComponent,
- ConfigFormComponent,
],
imports: [
CommonModule,
@@ -55,6 +53,7 @@ import {KeycloakBearerInterceptor} from "keycloak-angular";
AssignmentRoutingModule,
DndModule,
ModalModule,
+ NgbxFormsModule,
],
providers: [
{
diff --git a/frontend/src/app/assignment/model/config.ts b/frontend/src/app/assignment/model/config.ts
new file mode 100644
index 000000000..aa5125020
--- /dev/null
+++ b/frontend/src/app/assignment/model/config.ts
@@ -0,0 +1,75 @@
+import {IsBoolean, IsEmail, IsIn, IsNotEmpty, IsString} from "class-validator";
+import {Presentation} from "@mean-stream/ngbx";
+import {Transform} from "class-transformer";
+
+export class Config {
+ @Presentation({
+ description: 'Your full name for use in assignments, solutions, comments and evaluations.',
+ })
+ @IsString()
+ @IsNotEmpty()
+ name: string = '';
+
+ @Presentation({
+ label: 'E-Mail Address',
+ description: 'Your email address for use in assignments, solutions, comments and evaluations.',
+ })
+ @IsEmail()
+ email: string = '';
+
+ @Presentation({
+ label: 'IDE',
+ description: 'Your preferred IDE for cloning repositories.',
+ optionLabels: {
+ vscode: 'VSCode',
+ 'code-oss': 'Code - OSS',
+ vscodium: 'VSCodium',
+ },
+ })
+ @IsIn(['vscode', 'code-oss', 'vscodium'])
+ ide: 'vscode' | 'code-oss' | 'vscodium' = 'vscode';
+
+ @Presentation({
+ label: 'Git Clone Protocol',
+ description: 'The protocol to use when cloning a repository.',
+ optionLabels: {
+ https: 'HTTPS',
+ ssh: 'SSH',
+ },
+ })
+ @IsIn(['https', 'ssh'])
+ cloneProtocol: 'https' | 'ssh' = 'https';
+
+ @Presentation({
+ label: 'Git Clone Ref',
+ description: 'The ref to use when cloning a repository. ' +
+ 'Tags are only supported in VSCode v1.74+ and Assignments imported after 2022-12-21.',
+ optionLabels: {
+ none: 'None',
+ tag: 'Tag',
+ },
+ })
+ @IsIn(['none', 'tag'])
+ cloneRef: 'none' | 'tag' = 'tag';
+
+ @Presentation({
+ description: 'Enable Code Search globally.',
+ })
+ @IsBoolean()
+ @Transform(({value}) => value === 'true')
+ codeSearch = true;
+
+ @Presentation({
+ description: 'Enable Similar Solutions globally.',
+ })
+ @IsBoolean()
+ @Transform(({value}) => value === 'true')
+ similarSolutions = true;
+
+ @Presentation({
+ description: 'Enable Snippet Suggestions globally.',
+ })
+ @IsBoolean()
+ @Transform(({value}) => value === 'true')
+ snippetSuggestions = true;
+}
diff --git a/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.html b/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.html
index 8c6d188a5..7234d22e1 100644
--- a/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.html
+++ b/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.html
@@ -54,34 +54,6 @@
-
Search terms are separated by spaces and case sensitive
diff --git a/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.ts b/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.ts
index 1ba9d34dd..e1934edc3 100644
--- a/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.ts
+++ b/frontend/src/app/assignment/modules/assignment/solution-table/solution-table.component.ts
@@ -8,7 +8,7 @@ import {Assignee} from '../../../model/assignee';
import Assignment, {ReadAssignmentDto} from '../../../model/assignment';
import Solution, {AuthorInfo, authorInfoProperties} from '../../../model/solution';
import {AssignmentService} from '../../../services/assignment.service';
-import {CONFIG_OPTIONS, ConfigKey, ConfigService} from '../../../services/config.service';
+import {ConfigService} from '../../../services/config.service';
import {SolutionContainerService} from '../../../services/solution-container.service';
import {SolutionService} from '../../../services/solution.service';
import {TaskService} from '../../../services/task.service';
@@ -48,7 +48,6 @@ export class SolutionTableComponent implements OnInit {
loading = false;
- optionItems = CONFIG_OPTIONS.filter(o => o.options);
options = this.configService.getAll();
search$ = new BehaviorSubject('');
@@ -132,12 +131,6 @@ export class SolutionTableComponent implements OnInit {
this.userService.getGitHubToken().subscribe(token => this.userToken = token);
}
- setOption(key: ConfigKey, value: string) {
- // copy is necessary to re-evaluate link pipes
- this.options = {...this.options, [key]: value};
- this.configService.set(key, value);
- }
-
select(id: string, selected: boolean) {
if (selected) {
this.selected[id] = selected;
diff --git a/frontend/src/app/assignment/modules/shared/pipes/clone-link.pipe.ts b/frontend/src/app/assignment/modules/shared/pipes/clone-link.pipe.ts
index 1a2e6fd70..c96f68846 100644
--- a/frontend/src/app/assignment/modules/shared/pipes/clone-link.pipe.ts
+++ b/frontend/src/app/assignment/modules/shared/pipes/clone-link.pipe.ts
@@ -1,7 +1,7 @@
import {Pipe, PipeTransform} from '@angular/core';
import {ReadAssignmentDto} from '../../../model/assignment';
import Solution from '../../../model/solution';
-import {ConfigKey} from '../../../services/config.service';
+import {Config} from "../../../model/config";
const clonePrefix = {https: 'https://github.com/', ssh: 'git@github.com:'};
@@ -11,16 +11,13 @@ const cloneSuffix = {https: '', ssh: '.git'};
name: 'cloneLink',
})
export class CloneLinkPipe implements PipeTransform {
- transform(assignment: ReadAssignmentDto, solution: Solution, options: Record): string {
+ transform(assignment: ReadAssignmentDto, solution: Solution, options: Config): string {
const {ide, cloneProtocol, cloneRef} = options;
const user = solution.author.github;
const org = assignment.classroom?.org;
const prefix = assignment.classroom?.prefix;
let ref = '';
switch (cloneRef) {
- case 'commit':
- ref = `&ref=${solution.commit}`;
- break;
case 'tag':
ref = `&ref=assignments/${assignment._id}`;
break;
diff --git a/frontend/src/app/assignment/modules/solution/share/share.component.html b/frontend/src/app/assignment/modules/solution/share/share.component.html
index 42e303eaa..e7339b7a8 100644
--- a/frontend/src/app/assignment/modules/solution/share/share.component.html
+++ b/frontend/src/app/assignment/modules/solution/share/share.component.html
@@ -37,12 +37,8 @@
fulibFeedback Settings
- Use the button below to configure fulibFeedback and view evaluations for this submission in
-
- {{ option[1] }}
-
- .
+ Use the button below to configure fulibFeedback and view evaluations for this submission in your IDE
+ .
diff --git a/frontend/src/app/assignment/modules/solution/share/share.component.ts b/frontend/src/app/assignment/modules/solution/share/share.component.ts
index f29699cbc..2ea08811d 100644
--- a/frontend/src/app/assignment/modules/solution/share/share.component.ts
+++ b/frontend/src/app/assignment/modules/solution/share/share.component.ts
@@ -5,7 +5,7 @@ import {of} from 'rxjs';
import {map, switchMap, tap} from 'rxjs/operators';
import {SolutionService} from 'src/app/assignment/services/solution.service';
import {environment} from '../../../../../environments/environment';
-import {CONFIG_OPTIONS, ConfigService} from '../../../services/config.service';
+import {ConfigService} from '../../../services/config.service';
@Component({
selector: 'app-solution-share',
@@ -20,7 +20,6 @@ export class SolutionShareComponent implements OnInit {
readonly origin: string;
readonly encodedApiServer = encodeURIComponent(new URL(environment.assignmentsApiUrl, location.origin).origin);
- readonly ideOption = CONFIG_OPTIONS.find(o => o.key === 'ide')!;
constructor(
private solutionService: SolutionService,
diff --git a/frontend/src/app/assignment/pages/config-form/config-form.component.html b/frontend/src/app/assignment/pages/config-form/config-form.component.html
deleted file mode 100644
index 8bc49b1cc..000000000
--- a/frontend/src/app/assignment/pages/config-form/config-form.component.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- {{ option.title }}
- *
-
-
-
- {{ option[1] }}
-
-
- {{ option.description }}
-
-
diff --git a/frontend/src/app/assignment/pages/config-form/config-form.component.scss b/frontend/src/app/assignment/pages/config-form/config-form.component.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/frontend/src/app/assignment/pages/config-form/config-form.component.ts b/frontend/src/app/assignment/pages/config-form/config-form.component.ts
deleted file mode 100644
index 9dbd56c02..000000000
--- a/frontend/src/app/assignment/pages/config-form/config-form.component.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Component } from '@angular/core';
-import {ToastService} from '@mean-stream/ngbx';
-import {CONFIG_OPTIONS, ConfigService} from '../../services/config.service';
-
-@Component({
- selector: 'app-config-form',
- templateUrl: './config-form.component.html',
- styleUrls: ['./config-form.component.scss']
-})
-export class ConfigFormComponent {
- options = CONFIG_OPTIONS;
- optionValues = this.configService.getAll();
-
- constructor(
- private configService: ConfigService,
- private toastService: ToastService,
- ) {
- }
-
- save() {
- this.configService.setAll(this.optionValues);
- this.toastService.success('Settings', 'Successfully saved settings');
- }
-}
diff --git a/frontend/src/app/assignment/pages/overview/overview.component.html b/frontend/src/app/assignment/pages/overview/overview.component.html
index 047d48afe..34d7bc30a 100644
--- a/frontend/src/app/assignment/pages/overview/overview.component.html
+++ b/frontend/src/app/assignment/pages/overview/overview.component.html
@@ -21,13 +21,21 @@ Complete Setup
Please enter some information about yourself to begin using Assignments.
All data will only be saved locally on your device.
-
+
diff --git a/frontend/src/app/assignment/pages/overview/overview.component.ts b/frontend/src/app/assignment/pages/overview/overview.component.ts
index 8b7a9ffc6..ee1432317 100644
--- a/frontend/src/app/assignment/pages/overview/overview.component.ts
+++ b/frontend/src/app/assignment/pages/overview/overview.component.ts
@@ -1,6 +1,8 @@
import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {ConfigService} from '../../services/config.service';
+import {ModalComponent, ToastService, ValidatorFormComponent} from "@mean-stream/ngbx";
+import {Config} from "../../model/config";
@Component({
selector: 'app-overview',
@@ -10,17 +12,33 @@ import {ConfigService} from '../../services/config.service';
export class OverviewComponent implements OnInit {
@ViewChild('configModal', {static: true}) configModal: TemplateRef;
- needsConfig = !this.configService.get('name');
+ config?: Config;
+
+ protected readonly Config = Config;
constructor(
private configService: ConfigService,
+ private toastService: ToastService,
private modal: NgbModal,
) {
}
ngOnInit() {
if (!this.configService.get('name')) {
+ this.config = this.configService.getAll();
this.modal.open(this.configModal);
}
}
+
+ async save(form: ValidatorFormComponent, modal: ModalComponent) {
+ const errors = await form.validateAll();
+ if (errors.length) {
+ this.toastService.error('Setup', 'Please fix the errors in the form');
+ return false;
+ } else {
+ this.configService.setAll(this.config!);
+ this.toastService.success('Setup', 'Successfully saved settings');
+ modal.close('Save click');
+ }
+ }
}
diff --git a/frontend/src/app/assignment/pages/settings/settings.component.html b/frontend/src/app/assignment/pages/settings/settings.component.html
index 9dbaa80c5..f9aa66fa0 100644
--- a/frontend/src/app/assignment/pages/settings/settings.component.html
+++ b/frontend/src/app/assignment/pages/settings/settings.component.html
@@ -3,8 +3,8 @@
Settings
-
-
+
+
Save
diff --git a/frontend/src/app/assignment/pages/settings/settings.component.ts b/frontend/src/app/assignment/pages/settings/settings.component.ts
index 3dd77fafc..4c70061e9 100644
--- a/frontend/src/app/assignment/pages/settings/settings.component.ts
+++ b/frontend/src/app/assignment/pages/settings/settings.component.ts
@@ -1,6 +1,7 @@
import {Component} from '@angular/core';
-import {ToastService} from '@mean-stream/ngbx';
-import {CONFIG_OPTIONS, ConfigService} from '../../services/config.service';
+import {ToastService, ValidatorFormComponent} from "@mean-stream/ngbx";
+import {Config} from "../../model/config";
+import {ConfigService} from "../../services/config.service";
@Component({
selector: 'app-settings',
@@ -8,5 +9,25 @@ import {CONFIG_OPTIONS, ConfigService} from '../../services/config.service';
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent {
+ config = this.configService.getAll();
+ protected readonly Config = Config;
+
+ constructor(
+ private configService: ConfigService,
+ private toastService: ToastService,
+ ) {
+ }
+
+ async save(form: ValidatorFormComponent): Promise {
+ const errors = await form.validateAll();
+ if (errors.length) {
+ this.toastService.error('Settings', 'Please fix the errors in the form');
+ return false;
+ } else {
+ this.configService.setAll(this.config);
+ this.toastService.success('Settings', 'Successfully saved settings');
+ return true;
+ }
+ }
}
diff --git a/frontend/src/app/assignment/services/config.service.ts b/frontend/src/app/assignment/services/config.service.ts
index 637544766..0a8e38737 100644
--- a/frontend/src/app/assignment/services/config.service.ts
+++ b/frontend/src/app/assignment/services/config.service.ts
@@ -1,105 +1,36 @@
import {Injectable} from '@angular/core';
import {PrivacyService} from '../../services/privacy.service';
+import {Config} from "../model/config";
+import {transformDecoratorResources} from "@angular/compiler-cli/src/ngtsc/annotations/component/src/resources";
+import {plainToClass} from "class-transformer";
-export type ConfigKey =
- | 'name'
- | 'email'
- | 'ide'
- | 'cloneProtocol'
- | 'cloneRef'
- | 'codeSearch'
- | 'similarSolutions'
- | 'snippetSuggestions'
- ;
-
-export interface ConfigOption {
- key: ConfigKey;
- title: string;
- description: string;
- options?: [string, string][];
- default?: string;
-}
-
-export const CONFIG_OPTIONS: ConfigOption[] = [
- {
- key: 'name',
- title: 'Name',
- description: 'Your full name for use in assignments, solutions, comments and evaluations.',
- },
- {
- key: 'email',
- title: 'E-Mail Address',
- description: 'Your email address for use in assignments, solutions, comments and evaluations.',
- },
- {
- key: 'ide',
- title: 'IDE',
- description: 'Your preferred IDE for cloning repositories.',
- options: [['vscode', 'VSCode'], ['code-oss', 'Code - OSS'], ['vscodium', 'VSCodium']],
- default: 'vscode',
- },
- {
- key: 'cloneProtocol',
- title: 'Git Clone Protocol',
- description: 'The protocol to use when cloning a repository.',
- options: [['https', 'HTTPS'], ['ssh', 'SSH']],
- default: 'https',
- },
- {
- key: 'cloneRef',
- title: 'Git Clone Ref',
- description: 'The ref to use when cloning a repository. ' +
- 'Tags are only supported in VSCode v1.74+ and Assignments imported after 2022-12-21.',
- options: [['none', 'None'], ['tag', 'Tag']],
- default: 'tag',
- },
- {
- key: 'codeSearch',
- title: 'Code Search',
- description: 'Enable Code Search globally.',
- options: [['true', '✔️ Enabled'], ['false', '❌ Disabled']],
- default: 'true',
- },
- {
- key: 'snippetSuggestions',
- title: 'Snippet Suggestions',
- description: 'Enable Snippet Suggestions globally.',
- options: [['true', '✔️ Enabled'], ['false', '❌ Disabled']],
- default: 'true',
- },
- {
- key: 'similarSolutions',
- title: 'Similar Solutions',
- description: 'Enable Similar Solutions globally.',
- options: [['true', '✔️ Enabled'], ['false', '❌ Disabled']],
- default: 'true',
- },
-];
+export type ConfigKey = keyof Config;
@Injectable()
export class ConfigService {
+ readonly default = new Config();
+
constructor(
private privacyService: PrivacyService,
) {
}
- getAll(): Record {
- const options = {} as Record;
- for (const option of CONFIG_OPTIONS) {
- options[option.key] = this.get(option.key);
+ getAll(): Config {
+ const options = {...this.default};
+ for (const key of Object.keys(this.default)) {
+ options[key] = this.get(key as ConfigKey);
}
- return options;
+ return plainToClass(Config, options);
}
- setAll(options: Record) {
+ setAll(options: Config) {
for (const key of Object.keys(options) as ConfigKey[]) {
- this.set(key, options[key]);
+ this.set(key, options[key].toString());
}
}
- get(key: ConfigKey): string {
- const option = CONFIG_OPTIONS.find(o => o.key === key);
- return this.privacyService.getStorage('assignments/' + key) || option?.default || '';
+ get(key: K): Config[K] {
+ return this.privacyService.getStorage('assignments/' + key) as Config[K] || this.default[key];
}
getBool(key: ConfigKey): boolean {
@@ -107,16 +38,8 @@ export class ConfigService {
}
set(key: ConfigKey, value: string) {
- if (this.getOption(key)) {
+ if (key in this.default) {
this.privacyService.setStorage('assignments/' + key, value);
}
}
-
- setBool(key: ConfigKey, value: boolean) {
- this.set(key, value ? 'true' : 'false');
- }
-
- private getOption(key: ConfigKey): ConfigOption | undefined {
- return CONFIG_OPTIONS.find(o => o.key === key);
- }
}