Modernize angular bootstrap
This commit is contained in:
1 parent
dc833b2eb2
commit
5d540a0761
15 files changed
+102
-333
No files matched your search
@@ -71,12 +71,6 @@
|
||||
},
|
||||
"configurations": {
|
||||
"prod-no-static": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"production": {
|
||||
@@ -92,12 +86,6 @@
|
||||
"maximumError": "6kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": {
|
||||
"styles": {
|
||||
"inlineCritical": false
|
||||
|
||||
Generated
-155
@@ -91,7 +91,6 @@
|
||||
"ts-node": "~7.0.0",
|
||||
"typescript": "^5.8.2",
|
||||
"wait-on": "^5.3.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"xliff": "^6.2.1"
|
||||
}
|
||||
},
|
||||
@@ -6553,13 +6552,6 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.28",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
|
||||
"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@redocly/ajv": {
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz",
|
||||
@@ -8652,19 +8644,6 @@
|
||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/adjust-sourcemap-loader": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
|
||||
@@ -11650,13 +11629,6 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
@@ -12016,13 +11988,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -13837,22 +13802,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/gzip-size": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
|
||||
"integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"duplexer": "^0.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/hachure-fill": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
|
||||
@@ -17469,16 +17418,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"dev": true,
|
||||
"license": "(WTFPL OR MIT)",
|
||||
"bin": {
|
||||
"opener": "bin/opener-bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
@@ -19816,21 +19755,6 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
|
||||
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@polka/url": "^1.0.0-next.24",
|
||||
"mrmime": "^2.0.0",
|
||||
"totalist": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
||||
@@ -21017,16 +20941,6 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
|
||||
@@ -21758,75 +21672,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
|
||||
"integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@discoveryjs/json-ext": "0.5.7",
|
||||
"acorn": "^8.0.4",
|
||||
"acorn-walk": "^8.0.0",
|
||||
"commander": "^7.2.0",
|
||||
"debounce": "^1.2.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"gzip-size": "^6.0.0",
|
||||
"html-escaper": "^2.0.2",
|
||||
"opener": "^1.5.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"sirv": "^2.0.3",
|
||||
"ws": "^7.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"webpack-bundle-analyzer": "lib/bin/analyzer.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz",
|
||||
|
||||
+1
-2
@@ -17,7 +17,7 @@
|
||||
"cy:openscreenshot": "cypress open --env screenshot=true",
|
||||
"cy:run": "cypress run",
|
||||
"e2e": "concurrently \"npm run start -- --no-hmr\" \"wait-on http-get://localhost:4200 && npm run cy:run\" --kill-others --success first",
|
||||
"build-stats": "ng build --stats-json",
|
||||
"build-stats": "ng build -c production --stats-json",
|
||||
"analyze": "webpack-bundle-analyzer dist/glitchtip-frontend/stats.json",
|
||||
"docs:json": "compodoc -p ./tsconfig.json -e json -d .",
|
||||
"extract-i18n": "ng extract-i18n",
|
||||
@@ -111,7 +111,6 @@
|
||||
"ts-node": "~7.0.0",
|
||||
"typescript": "^5.8.2",
|
||||
"wait-on": "^5.3.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"xliff": "^6.2.1"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
Vendored
+28
-28
@@ -3017,10 +3017,10 @@ export interface components {
|
||||
/** Status */
|
||||
status: string;
|
||||
project: components["schemas"]["ProjectReference"];
|
||||
/** Short Id */
|
||||
short_id: string;
|
||||
/** Num Comments */
|
||||
num_comments: number;
|
||||
/** Shortid */
|
||||
shortId: string;
|
||||
/** Numcomments */
|
||||
numComments: number;
|
||||
/**
|
||||
* Stats
|
||||
* @default {
|
||||
@@ -3055,24 +3055,24 @@ export interface components {
|
||||
user_count: number | null;
|
||||
/** Matching Event Id */
|
||||
matching_event_id?: string | null;
|
||||
/**
|
||||
* Firstseen
|
||||
* Format: date-time
|
||||
*/
|
||||
firstSeen: string;
|
||||
/**
|
||||
* Lastseen
|
||||
* Format: date-time
|
||||
*/
|
||||
lastSeen: string;
|
||||
/** Title */
|
||||
title: string;
|
||||
/** Metadata */
|
||||
metadata: Record<string, unknown>;
|
||||
/** Culprit */
|
||||
culprit?: string | null;
|
||||
/**
|
||||
* First Seen
|
||||
* Format: date-time
|
||||
*/
|
||||
first_seen?: string;
|
||||
/**
|
||||
* Last Seen
|
||||
* Format: date-time
|
||||
*/
|
||||
last_seen?: string;
|
||||
/** User Report Count */
|
||||
user_report_count: number;
|
||||
/** Userreportcount */
|
||||
userReportCount: number;
|
||||
};
|
||||
/** ProjectReference */
|
||||
ProjectReference: {
|
||||
@@ -3132,8 +3132,8 @@ export interface components {
|
||||
project: components["schemas"]["ProjectReference"];
|
||||
/** Short Id Display */
|
||||
short_id_display: string;
|
||||
/** Numcomments */
|
||||
numComments: number;
|
||||
/** Num Comments */
|
||||
num_comments: number;
|
||||
/**
|
||||
* Stats
|
||||
* @default {
|
||||
@@ -3168,22 +3168,22 @@ export interface components {
|
||||
userCount: number | null;
|
||||
/** Matchingeventid */
|
||||
matchingEventId?: string | null;
|
||||
/**
|
||||
* First Seen
|
||||
* Format: date-time
|
||||
*/
|
||||
first_seen: string;
|
||||
/**
|
||||
* Last Seen
|
||||
* Format: date-time
|
||||
*/
|
||||
last_seen: string;
|
||||
/** Title */
|
||||
title: string;
|
||||
/** Metadata */
|
||||
metadata: Record<string, unknown>;
|
||||
/** Culprit */
|
||||
culprit?: string | null;
|
||||
/**
|
||||
* First Seen
|
||||
* Format: date-time
|
||||
*/
|
||||
firstSeen?: string;
|
||||
/**
|
||||
* Last Seen
|
||||
* Format: date-time
|
||||
*/
|
||||
lastSeen?: string;
|
||||
};
|
||||
/** IssueTagSchema */
|
||||
IssueTagSchema: {
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { Injectable, inject } from "@angular/core";
|
||||
import { HttpClient, HttpParams } from "@angular/common/http";
|
||||
import { EMPTY, map } from "rxjs";
|
||||
import { EMPTY } from "rxjs";
|
||||
import { baseUrl } from "../../constants";
|
||||
import { APIBaseService } from "../api-base.service";
|
||||
import {
|
||||
IssueDetail,
|
||||
IssueStatus,
|
||||
IssueTags,
|
||||
UpdateStatusResponse,
|
||||
} from "src/app/issues/interfaces";
|
||||
import { normalizeID } from "../shared-api.utils";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
@@ -31,12 +29,7 @@ export class IssuesAPIService extends APIBaseService {
|
||||
}
|
||||
|
||||
retrieve(id: string) {
|
||||
return this.http.get<IssueDetail>(this.detailURL(id)).pipe(
|
||||
map((issueDetail) => {
|
||||
issueDetail.project.id = normalizeID(issueDetail.project.id);
|
||||
return issueDetail;
|
||||
}),
|
||||
);
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
update(status: IssueStatus, orgSlug: string, id: number) {
|
||||
|
||||
@@ -37,7 +37,7 @@ export class UserReportsService extends PaginationStatefulService<UserReportsSta
|
||||
super(initialState);
|
||||
}
|
||||
|
||||
getReportsForIssue(issue: number, cursor?: string | null) {
|
||||
getReportsForIssue(issue: string, cursor?: string | null) {
|
||||
this.setLoadingReports(true);
|
||||
this.resetUserReports();
|
||||
this.getIssueReports(issue, cursor)
|
||||
@@ -60,7 +60,7 @@ export class UserReportsService extends PaginationStatefulService<UserReportsSta
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private getIssueReports(issueId: number, cursor?: string | null) {
|
||||
private getIssueReports(issueId: string, cursor?: string | null) {
|
||||
let httpParams = new HttpParams();
|
||||
if (cursor) {
|
||||
httpParams = httpParams.set("cursor", cursor);
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
inject,
|
||||
input,
|
||||
effect,
|
||||
computed,
|
||||
} from "@angular/core";
|
||||
import { RouterLink } from "@angular/router";
|
||||
@@ -56,12 +55,6 @@ export class EventDetailComponent {
|
||||
nextEventUrl = this.issueService.nextEventUrl;
|
||||
previousEventUrl = this.issueService.previousEventUrl;
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
this.issueService.setParams(this.issueID(), this.eventID());
|
||||
});
|
||||
}
|
||||
|
||||
/** TODO fix these types */
|
||||
generateQuery(tag: { [key: string]: string | null }) {
|
||||
// Assume unresolved if not present; tag overrides query otherwise
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
inject,
|
||||
computed,
|
||||
input,
|
||||
effect,
|
||||
} from "@angular/core";
|
||||
import { FormControl, FormGroup } from "@angular/forms";
|
||||
import { ActivatedRoute, RouterModule } from "@angular/router";
|
||||
@@ -47,7 +48,7 @@ export class IssueDetailComponent implements OnInit {
|
||||
issue = this.issueService.issue;
|
||||
issueTitle = computed(() => {
|
||||
const issue = this.issue();
|
||||
if (issue === null || issue.metadata === null) {
|
||||
if (!issue || issue.metadata === null) {
|
||||
return ["", null] as [string, string | null];
|
||||
}
|
||||
const metadata = issue.metadata;
|
||||
@@ -75,23 +76,23 @@ export class IssueDetailComponent implements OnInit {
|
||||
return [metadata.title!, null] as [string, string | null];
|
||||
}
|
||||
});
|
||||
issueSubtitle = computed(() => {
|
||||
issueSubtitle = computed<string>(() => {
|
||||
const issue = this.issue();
|
||||
if (issue === null || issue.metadata === null) {
|
||||
if (!issue || issue.metadata === null) {
|
||||
return "";
|
||||
}
|
||||
const metadata = issue.metadata;
|
||||
switch (issue.type) {
|
||||
case "error":
|
||||
return metadata.value;
|
||||
return metadata.value as string;
|
||||
case "csp":
|
||||
return metadata.message;
|
||||
return metadata.message as string;
|
||||
case "expectct":
|
||||
case "expectstaple":
|
||||
case "hpkp":
|
||||
return "";
|
||||
default:
|
||||
return issue.culprit;
|
||||
return issue.culprit as string;
|
||||
}
|
||||
});
|
||||
initialLoadComplete = this.issueService.issueInitialLoadComplete;
|
||||
@@ -99,6 +100,7 @@ export class IssueDetailComponent implements OnInit {
|
||||
assignee: new FormControl(""),
|
||||
});
|
||||
issueID = input.required<string>({ alias: "issue-id" });
|
||||
eventID = input<string | null>(null, { alias: "event-id" });
|
||||
organization = this.organizationsService.activeOrganization;
|
||||
participantCountPluralMapping: { [k: string]: string } = {
|
||||
"=0": "No Participants",
|
||||
@@ -106,11 +108,14 @@ export class IssueDetailComponent implements OnInit {
|
||||
other: "# Participants",
|
||||
};
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
this.issueService.setParams(this.issueID(), this.eventID());
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.issueService.clearState();
|
||||
const issueID = this.issueID();
|
||||
// Consider moving to effect once ported to resource
|
||||
this.issueService.retrieveIssue(+issueID).toPromise();
|
||||
}
|
||||
|
||||
markResolved() {
|
||||
@@ -137,7 +142,7 @@ export class IssueDetailComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
generateBackLink(projectId: number) {
|
||||
generateBackLink(projectId: string) {
|
||||
return {
|
||||
...this.route.snapshot.queryParams,
|
||||
project: projectId,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { HttpErrorResponse } from "@angular/common/http";
|
||||
import { Injectable, computed, inject, resource, signal } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
import { EMPTY, lastValueFrom } from "rxjs";
|
||||
import { catchError, filter, take, tap, withLatestFrom } from "rxjs/operators";
|
||||
import { EMPTY } from "rxjs";
|
||||
import { catchError, tap, withLatestFrom } from "rxjs/operators";
|
||||
import {
|
||||
IssueDetail,
|
||||
IssueStatus,
|
||||
ExceptionValueData,
|
||||
Request,
|
||||
@@ -28,23 +26,18 @@ import { client } from "src/app/api/api";
|
||||
import { components } from "src/app/api/api-schema";
|
||||
|
||||
type EventDetail = components["schemas"]["IssueEventDetailSchema"];
|
||||
type IssueDetail = components["schemas"]["IssueDetailSchema"];
|
||||
|
||||
interface IssueDetailState {
|
||||
issue: IssueDetail | null;
|
||||
issueLoading: boolean;
|
||||
issueInitialLoadComplete: boolean;
|
||||
tags: IssueTags[] | null;
|
||||
isReversed: boolean;
|
||||
showShowMore: boolean;
|
||||
}
|
||||
|
||||
const initialState: IssueDetailState = {
|
||||
issue: null,
|
||||
tags: null,
|
||||
isReversed: true,
|
||||
showShowMore: false,
|
||||
issueLoading: false,
|
||||
issueInitialLoadComplete: false,
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
@@ -56,9 +49,9 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private router = inject(Router);
|
||||
|
||||
readonly issue = computed(() => this.state().issue);
|
||||
readonly issueInitialLoadComplete = computed(
|
||||
() => this.state().issueInitialLoadComplete,
|
||||
readonly issue = computed(() => this.#issueResource.value());
|
||||
readonly issueInitialLoadComplete = computed(() =>
|
||||
this.#issueResource.hasValue(),
|
||||
);
|
||||
readonly event = computed(() => this.#eventResource.value());
|
||||
readonly tags = computed(() => {
|
||||
@@ -82,7 +75,7 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
const event = this.event();
|
||||
|
||||
if (event && event.nextEventID) {
|
||||
return this.eventUrl(orgSlug, issue, event.nextEventID);
|
||||
return this.eventUrl(orgSlug, issue!, event.nextEventID);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -92,7 +85,7 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
const event = this.event();
|
||||
|
||||
if (event && event.previousEventID) {
|
||||
return this.eventUrl(orgSlug, issue, event.previousEventID);
|
||||
return this.eventUrl(orgSlug, issue!, event.previousEventID);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -131,6 +124,18 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
issueID: "",
|
||||
eventID: "",
|
||||
});
|
||||
#issueResource = resource({
|
||||
params: () => ({ issueID: this.#params().issueID }),
|
||||
loader: async ({ params }) => {
|
||||
if (!params.issueID) {
|
||||
return;
|
||||
}
|
||||
const { data } = await client.GET("/api/0/issues/{issue_id}/", {
|
||||
params: { path: { issue_id: parseInt(params.issueID) } },
|
||||
});
|
||||
return data;
|
||||
},
|
||||
});
|
||||
#eventResource = resource({
|
||||
params: () => ({ params: this.#params() }),
|
||||
loader: async ({ params }) => {
|
||||
@@ -164,27 +169,6 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
super(initialState);
|
||||
}
|
||||
|
||||
retrieveIssue(id: number) {
|
||||
this.setState({ issueLoading: true });
|
||||
return this.issuesAPIService.retrieve(id.toString()).pipe(
|
||||
tap((issue) => this.setIssue(issue)),
|
||||
catchError((error) => {
|
||||
if (error instanceof HttpErrorResponse && error.status === 404) {
|
||||
this.clearIssue();
|
||||
}
|
||||
return EMPTY;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
setIssue(issue: IssueDetail) {
|
||||
this.setState({
|
||||
issue,
|
||||
issueLoading: false,
|
||||
issueInitialLoadComplete: true,
|
||||
});
|
||||
}
|
||||
|
||||
setParams(issueID: string, eventID: string | null) {
|
||||
this.#params.set({ issueID, eventID });
|
||||
}
|
||||
@@ -205,24 +189,24 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
this.setState({ showShowMore: value });
|
||||
}
|
||||
|
||||
setStatus(status: IssueStatus) {
|
||||
const issue = this.state().issue;
|
||||
async setStatus(status: IssueStatus) {
|
||||
const issue = this.issue();
|
||||
if (issue) {
|
||||
lastValueFrom(
|
||||
this.organization.activeOrganizationSlug$.pipe(
|
||||
filter((slug) => !!slug),
|
||||
take(1),
|
||||
tap((slug) => {
|
||||
if (slug) {
|
||||
lastValueFrom(
|
||||
this.issuesAPIService
|
||||
.update(status, slug, issue.id)
|
||||
.pipe(tap((resp) => this.setIssueStatus(resp.status))),
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
const { data } = await client.PUT(
|
||||
"/api/0/organizations/{organization_slug}/issues/{issue_id}/",
|
||||
{
|
||||
params: {
|
||||
path: {
|
||||
organization_slug: this.organization.activeOrganizationSlug(),
|
||||
issue_id: parseInt(this.#params().issueID),
|
||||
},
|
||||
},
|
||||
body: { status: status as any },
|
||||
},
|
||||
);
|
||||
if (data) {
|
||||
this.setIssueStatus(data.status as IssueStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,31 +271,14 @@ export class IssueDetailService extends StatefulService<IssueDetailState> {
|
||||
|
||||
/** Set local state issue state */
|
||||
private setIssueStatus(status: IssueStatus) {
|
||||
const state = this.state();
|
||||
if (state.issue) {
|
||||
const issue = { ...state.issue, status };
|
||||
this.setState({ issue });
|
||||
}
|
||||
}
|
||||
|
||||
private clearIssue() {
|
||||
this.setState({
|
||||
issue: null,
|
||||
issueLoading: false,
|
||||
issueInitialLoadComplete: true,
|
||||
});
|
||||
this.#issueResource.update((issue) => ({ ...issue!, status }));
|
||||
}
|
||||
|
||||
private setUpdatedCommentCount(num: number) {
|
||||
const state = this.state();
|
||||
if (state.issue) {
|
||||
this.setState({
|
||||
issue: {
|
||||
...state.issue,
|
||||
numComments: state.issue.numComments + num,
|
||||
},
|
||||
});
|
||||
}
|
||||
this.#issueResource.update((issue) => ({
|
||||
...issue!,
|
||||
numComments: issue!.numComments + num,
|
||||
}));
|
||||
}
|
||||
|
||||
setTags(tags: IssueTags[]) {
|
||||
|
||||
@@ -15,7 +15,7 @@ export class SlugifyDirective {
|
||||
return true;
|
||||
}
|
||||
|
||||
@HostListener("paste", ["$event"]) blockPaste(event: KeyboardEvent) {
|
||||
@HostListener("paste", ["$event"]) blockPaste(event: ClipboardEvent) {
|
||||
this.validateFields();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
billingEmail: "sales@glitchtip.com",
|
||||
};
|
||||
@@ -3,6 +3,5 @@
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
billingEmail: "sales@glitchtip.com",
|
||||
};
|
||||
+5
-18
@@ -1,21 +1,14 @@
|
||||
import {
|
||||
enableProdMode,
|
||||
ErrorHandler,
|
||||
importProvidersFrom,
|
||||
inject,
|
||||
provideZonelessChangeDetection,
|
||||
Provider,
|
||||
} from "@angular/core";
|
||||
import { loadTranslations } from "@angular/localize";
|
||||
|
||||
import { environment } from "./environments/environment";
|
||||
import { AppComponent } from "./app/app.component";
|
||||
import { MicroSentryModule } from "@micro-sentry/angular";
|
||||
import {
|
||||
MAT_SNACK_BAR_DEFAULT_OPTIONS,
|
||||
MatSnackBarModule,
|
||||
} from "@angular/material/snack-bar";
|
||||
import { provideAnimationsAsync } from "@angular/platform-browser/animations/async";
|
||||
import { provideMicroSentry } from "@micro-sentry/angular";
|
||||
import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from "@angular/material/snack-bar";
|
||||
import { routes, TemplatePageTitleStrategy } from "./app/app.routes";
|
||||
import { bootstrapApplication } from "@angular/platform-browser";
|
||||
import { LessAnnoyingErrorStateMatcher } from "./app/shared/less-annoying-error-state-matcher";
|
||||
@@ -50,10 +43,6 @@ if (window.Cypress) {
|
||||
}
|
||||
const serverErrorsRegex = new RegExp(`403 Forbidden|404 OK`, "mi");
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
// First locale is default, add additional after it
|
||||
const availableLocales = ["en", "fr", "nb"];
|
||||
// Direct macrolanguages to specific ones. Example: Norwegian becomes Bokmål
|
||||
@@ -108,10 +97,9 @@ const bootstrap = () =>
|
||||
}),
|
||||
),
|
||||
provideMarkdown(),
|
||||
importProvidersFrom(
|
||||
MatSnackBarModule,
|
||||
MicroSentryModule.forRoot({ ignoreErrors: [serverErrorsRegex] }),
|
||||
),
|
||||
provideMicroSentry({
|
||||
ignoreErrors: [serverErrorsRegex],
|
||||
}),
|
||||
{
|
||||
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
|
||||
useValue: { duration: snackBarDuration },
|
||||
@@ -122,7 +110,6 @@ const bootstrap = () =>
|
||||
provide: ErrorStateMatcher,
|
||||
useClass: LessAnnoyingErrorStateMatcher,
|
||||
},
|
||||
provideAnimationsAsync(),
|
||||
provideHttpClient(
|
||||
withFetch(),
|
||||
withXsrfConfiguration({
|
||||
|
||||
+2
-6
@@ -4,12 +4,8 @@
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
|
||||
+5
-4
@@ -9,7 +9,7 @@
|
||||
"declaration": false,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ES2022",
|
||||
"module": "preserve",
|
||||
"moduleResolution": "bundler",
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitReturns": true,
|
||||
@@ -25,8 +25,9 @@
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictTemplates": true,
|
||||
"strictInjectionParameters": true
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user