mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-11 17:47:00 -05:00
Move more .d.ts files to .ts (#5060)
* Move more .d.ts files to .ts * Add release notes * Some errors with templates snuck in * Fix API build * CodeRabbit feedback * Move budget templates to new directory * Fix type errors in library module
This commit is contained in:
committed by
GitHub
parent
8ccc1af77e
commit
5a40b017f0
@@ -11,7 +11,7 @@
|
||||
"outDir": "dist",
|
||||
"declarationDir": "@types",
|
||||
"paths": {
|
||||
"loot-core/*": ["./@types/loot-core/*"]
|
||||
"loot-core/*": ["./@types/loot-core/src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["."],
|
||||
|
||||
@@ -22,7 +22,7 @@ const ACTUAL_VERSION = Platform.isPlaywright
|
||||
: packageJson.version;
|
||||
|
||||
// *** Start the backend ***
|
||||
let worker;
|
||||
let worker = null;
|
||||
|
||||
function createBackendWorker() {
|
||||
worker = new Worker(backendWorkerUrl);
|
||||
|
||||
@@ -71,7 +71,7 @@ function AppInner() {
|
||||
};
|
||||
|
||||
async function init() {
|
||||
const socketName = await maybeUpdate(() =>
|
||||
const serverSocket = await maybeUpdate(() =>
|
||||
global.Actual.getServerSocket(),
|
||||
);
|
||||
|
||||
@@ -82,7 +82,7 @@ function AppInner() {
|
||||
),
|
||||
}),
|
||||
);
|
||||
await initConnection(socketName);
|
||||
await initConnection(serverSocket);
|
||||
|
||||
// Load any global prefs
|
||||
dispatch(
|
||||
|
||||
@@ -3,11 +3,11 @@ import { useCallback, useMemo, useReducer, useState } from 'react';
|
||||
import { Stack } from '@actual-app/components/stack';
|
||||
import { type CSSProperties } from '@actual-app/components/styles';
|
||||
|
||||
import { type Template } from 'loot-core/server/budget/types/templates';
|
||||
import {
|
||||
type CategoryGroupEntity,
|
||||
type ScheduleEntity,
|
||||
} from 'loot-core/types/models';
|
||||
import { type Template } from 'loot-core/types/models/templates';
|
||||
|
||||
import { type Action } from './actions';
|
||||
import { BudgetAutomationEditor } from './BudgetAutomationEditor';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button } from '@actual-app/components/button';
|
||||
import { SvgChartPie } from '@actual-app/components/icons/v1';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
|
||||
import { type Template } from 'loot-core/server/budget/types/templates';
|
||||
import { type Template } from 'loot-core/types/models/templates';
|
||||
|
||||
import { useFeatureFlag } from '@desktop-client/hooks/useFeatureFlag';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Template } from 'loot-core/server/budget/types/templates';
|
||||
import { type Template } from 'loot-core/types/models/templates';
|
||||
|
||||
import { type DisplayTemplateType } from './constants';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type ScheduleTemplate,
|
||||
type SimpleTemplate,
|
||||
type WeekTemplate,
|
||||
} from 'loot-core/server/budget/types/templates';
|
||||
} from 'loot-core/types/models/templates';
|
||||
|
||||
export const displayTemplateTypes = [
|
||||
['simple', 'Fixed (monthly)'] as const,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Stack } from '@actual-app/components/stack';
|
||||
import type {
|
||||
CopyTemplate,
|
||||
AverageTemplate,
|
||||
} from 'loot-core/server/budget/types/templates';
|
||||
} from 'loot-core/types/models/templates';
|
||||
|
||||
import {
|
||||
type Action,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Trans } from 'react-i18next';
|
||||
import type {
|
||||
CopyTemplate,
|
||||
AverageTemplate,
|
||||
} from 'loot-core/server/budget/types/templates';
|
||||
} from 'loot-core/types/models/templates';
|
||||
|
||||
type HistoricalAutomationReadOnlyProps = {
|
||||
template: CopyTemplate | AverageTemplate;
|
||||
|
||||
@@ -4,11 +4,11 @@ import { Select } from '@actual-app/components/select';
|
||||
import { Stack } from '@actual-app/components/stack';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import type { PercentageTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import type {
|
||||
CategoryGroupEntity,
|
||||
CategoryEntity,
|
||||
} from 'loot-core/types/models';
|
||||
import type { PercentageTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
import { CategoryAutocomplete } from '@desktop-client/components/autocomplete/CategoryAutocomplete';
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
|
||||
import type { PercentageTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import type { PercentageTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
type PercentageAutomationReadOnlyProps = {
|
||||
template: PercentageTemplate;
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Select } from '@actual-app/components/select';
|
||||
import { Stack } from '@actual-app/components/stack';
|
||||
import { Text } from '@actual-app/components/text';
|
||||
|
||||
import type { ScheduleTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import type { ScheduleEntity } from 'loot-core/types/models';
|
||||
import type { ScheduleTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
import {
|
||||
type Action,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import type { ScheduleTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import type { ScheduleTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
type ScheduleAutomationReadOnlyProps = {
|
||||
template: ScheduleTemplate;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { SimpleTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import type { SimpleTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
import {
|
||||
type Action,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import type { SimpleTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import { integerToCurrency } from 'loot-core/shared/util';
|
||||
import type { SimpleTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
type SimpleAutomationReadOnlyProps = {
|
||||
template: SimpleTemplate;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { WeekTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import type { WeekTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
import {
|
||||
type Action,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import type { WeekTemplate } from 'loot-core/server/budget/types/templates';
|
||||
import { integerToCurrency } from 'loot-core/shared/util';
|
||||
import type { WeekTemplate } from 'loot-core/types/models/templates';
|
||||
|
||||
type WeekAutomationReadOnlyProps = {
|
||||
template: WeekTemplate;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Template } from 'loot-core/server/budget/types/templates';
|
||||
import { type Template } from 'loot-core/types/models/templates';
|
||||
|
||||
import { type Action } from './actions';
|
||||
import { type ReducerState, type DisplayTemplateType } from './constants';
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Stack } from '@actual-app/components/stack';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
import { uniqueId } from 'lodash';
|
||||
|
||||
import { type Template } from 'loot-core/server/budget/types/templates';
|
||||
import { q } from 'loot-core/shared/query';
|
||||
import { type Template } from 'loot-core/types/models/templates';
|
||||
|
||||
import { BudgetAutomation } from '@desktop-client/components/budget/goals/BudgetAutomation';
|
||||
import { useBudgetAutomationCategories } from '@desktop-client/components/budget/goals/useBudgetAutomationCategories';
|
||||
|
||||
@@ -596,7 +596,7 @@ ipcMain.handle('relaunch', () => {
|
||||
});
|
||||
|
||||
export type OpenFileDialogPayload = {
|
||||
properties: OpenDialogSyncOptions['properties'];
|
||||
properties?: OpenDialogSyncOptions['properties'];
|
||||
filters?: OpenDialogSyncOptions['filters'];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-strict-ignore
|
||||
import type * as T from '../index.d';
|
||||
import type * as T from '../index-types';
|
||||
|
||||
let listeners = new Map();
|
||||
let serverHandler = null;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Handlers } from '../../../types/handlers';
|
||||
import type { ServerEvents } from '../../../types/server-events';
|
||||
|
||||
export function init(socketName: string): Promise<unknown>;
|
||||
export declare function init(worker: Worker): Promise<unknown>;
|
||||
export type Init = typeof init;
|
||||
|
||||
export function send<K extends keyof Handlers>(
|
||||
export declare function send<K extends keyof Handlers>(
|
||||
name: K,
|
||||
args: Parameters<Handlers[K]>[0],
|
||||
options: { catchErrors: true },
|
||||
@@ -15,14 +15,14 @@ export function send<K extends keyof Handlers>(
|
||||
error: { type: 'APIError' | 'InternalError'; message: string };
|
||||
}
|
||||
>;
|
||||
export function send<K extends keyof Handlers>(
|
||||
export declare function send<K extends keyof Handlers>(
|
||||
name: K,
|
||||
args?: Parameters<Handlers[K]>[0],
|
||||
options?: { catchErrors?: boolean },
|
||||
): Promise<Awaited<ReturnType<Handlers[K]>>>;
|
||||
export type Send = typeof send;
|
||||
|
||||
export function sendCatch<K extends keyof Handlers>(
|
||||
export declare function sendCatch<K extends keyof Handlers>(
|
||||
name: K,
|
||||
args?: Parameters<Handlers[K]>[0],
|
||||
): Promise<
|
||||
@@ -34,21 +34,21 @@ export function sendCatch<K extends keyof Handlers>(
|
||||
>;
|
||||
export type SendCatch = typeof sendCatch;
|
||||
|
||||
export function listen<K extends keyof ServerEvents>(
|
||||
export declare function listen<K extends keyof ServerEvents>(
|
||||
name: K,
|
||||
cb: (arg: ServerEvents[K]) => void,
|
||||
): () => void;
|
||||
export type Listen = typeof listen;
|
||||
|
||||
export function unlisten(name: string): void;
|
||||
export declare function unlisten(name: string): void;
|
||||
export type Unlisten = typeof unlisten;
|
||||
|
||||
/** Mock functions */
|
||||
export function initServer(handlers: Partial<Handlers>): void;
|
||||
export declare function initServer(handlers: Partial<Handlers>): void;
|
||||
export type InitServer = typeof initServer;
|
||||
|
||||
export function serverPush(name: string, args: unknown): void;
|
||||
export declare function serverPush(name: string, args: unknown): void;
|
||||
export type ServerPush = typeof serverPush;
|
||||
|
||||
export async function clearServer(): void;
|
||||
export declare function clearServer(): Promise<void>;
|
||||
export type ClearServer = typeof clearServer;
|
||||
@@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { captureException, captureBreadcrumb } from '../../exceptions';
|
||||
import * as undo from '../undo';
|
||||
|
||||
import type * as T from './index.d';
|
||||
import type * as T from './index-types';
|
||||
|
||||
const replyHandlers = new Map();
|
||||
const listeners = new Map();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as undo from '../undo';
|
||||
|
||||
import type * as T from './index.d';
|
||||
import type * as T from './index-types';
|
||||
|
||||
const replyHandlers = new Map();
|
||||
const listeners = new Map();
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
SyncServerSimpleFinAccount,
|
||||
SyncServerPluggyAiAccount,
|
||||
type GoCardlessToken,
|
||||
ImportTransactionEntity,
|
||||
} from '../../types/models';
|
||||
import { createApp } from '../app';
|
||||
import * as db from '../db';
|
||||
@@ -1087,7 +1088,7 @@ async function importTransactions({
|
||||
opts,
|
||||
}: {
|
||||
accountId: AccountEntity['id'];
|
||||
transactions: TransactionEntity[];
|
||||
transactions: ImportTransactionEntity[];
|
||||
isPreview: boolean;
|
||||
opts?: {
|
||||
defaultCleared: boolean;
|
||||
|
||||
@@ -2,11 +2,11 @@ import { vi } from 'vitest';
|
||||
|
||||
import { amountToInteger } from '../../shared/util';
|
||||
import { type CategoryEntity } from '../../types/models';
|
||||
import { type Template } from '../../types/models/templates';
|
||||
import * as db from '../db';
|
||||
|
||||
import * as actions from './actions';
|
||||
import { CategoryTemplateContext } from './category-template-context';
|
||||
import { type Template } from './types/templates';
|
||||
|
||||
// Mock getSheetValue and getCategories
|
||||
vi.mock('./actions', () => ({
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
import * as monthUtils from '../../shared/months';
|
||||
import { amountToInteger } from '../../shared/util';
|
||||
import { CategoryEntity } from '../../types/models';
|
||||
import * as db from '../db';
|
||||
|
||||
import { getSheetValue, getSheetBoolean } from './actions';
|
||||
import { runSchedule } from './schedule-template';
|
||||
import { getActiveSchedules } from './statements';
|
||||
import {
|
||||
AverageTemplate,
|
||||
ByTemplate,
|
||||
@@ -19,7 +14,12 @@ import {
|
||||
SpendTemplate,
|
||||
Template,
|
||||
WeekTemplate,
|
||||
} from './types/templates';
|
||||
} from '../../types/models/templates';
|
||||
import * as db from '../db';
|
||||
|
||||
import { getSheetValue, getSheetBoolean } from './actions';
|
||||
import { runSchedule } from './schedule-template';
|
||||
import { getActiveSchedules } from './statements';
|
||||
|
||||
export class CategoryTemplateContext {
|
||||
/*----------------------------------------------------------------------------
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import * as monthUtils from '../../shared/months';
|
||||
import { q } from '../../shared/query';
|
||||
import { CategoryEntity, CategoryGroupEntity } from '../../types/models';
|
||||
import { Template } from '../../types/models/templates';
|
||||
import { aqlQuery } from '../aql';
|
||||
import { batchMessages } from '../sync';
|
||||
|
||||
import { isReflectBudget, getSheetValue, setGoal, setBudget } from './actions';
|
||||
import { CategoryTemplateContext } from './category-template-context';
|
||||
import { checkTemplates, storeTemplates } from './template-notes';
|
||||
import { Template } from './types/templates';
|
||||
|
||||
type Notification = {
|
||||
type?: 'message' | 'error' | 'warning' | undefined;
|
||||
|
||||
@@ -6,11 +6,11 @@ import {
|
||||
extractScheduleConds,
|
||||
} from '../../shared/schedules';
|
||||
import { CategoryEntity } from '../../types/models';
|
||||
import { ScheduleTemplate, Template } from '../../types/models/templates';
|
||||
import * as db from '../db';
|
||||
import { getRuleForSchedule } from '../schedules/app';
|
||||
|
||||
import { isReflectBudget } from './actions';
|
||||
import { ScheduleTemplate, Template } from './types/templates';
|
||||
|
||||
type ScheduleTemplateTarget = {
|
||||
name: string;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Template } from '../../types/models/templates';
|
||||
import * as db from '../db';
|
||||
|
||||
import { parse } from './goal-template.pegjs';
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
getCategoriesWithTemplateNotes,
|
||||
resetCategoryGoalDefsWithNoTemplates,
|
||||
} from './statements';
|
||||
import { Template } from './types/templates';
|
||||
|
||||
type Notification = {
|
||||
type?: 'message' | 'error' | 'warning' | undefined;
|
||||
|
||||
@@ -12,7 +12,12 @@ import type {
|
||||
import { BudgetFileHandlers } from '../server/budgetfiles/app';
|
||||
import { type batchUpdateTransactions } from '../server/transactions';
|
||||
|
||||
import type { NewRuleEntity, RuleEntity, TransactionEntity } from './models';
|
||||
import type {
|
||||
ImportTransactionEntity,
|
||||
NewRuleEntity,
|
||||
RuleEntity,
|
||||
TransactionEntity,
|
||||
} from './models';
|
||||
|
||||
export interface ApiHandlers {
|
||||
'api/batch-budget-start': () => Promise<unknown>;
|
||||
@@ -83,7 +88,7 @@ export interface ApiHandlers {
|
||||
|
||||
'api/transactions-import': (arg: {
|
||||
accountId;
|
||||
transactions;
|
||||
transactions: ImportTransactionEntity[];
|
||||
isPreview?;
|
||||
opts?: ImportTransactionsOpts;
|
||||
}) => Promise<ImportTransactionsResult>;
|
||||
|
||||
@@ -8,7 +8,7 @@ export type AccountEntity = {
|
||||
tombstone: 0 | 1;
|
||||
} & (_SyncFields<true> | _SyncFields<false>);
|
||||
|
||||
type _SyncFields<T> = {
|
||||
export type _SyncFields<T> = {
|
||||
account_id: T extends true ? string : null;
|
||||
bank: T extends true ? string : null;
|
||||
bankName: T extends true ? string : null;
|
||||
@@ -5,6 +5,7 @@ export type * from './category';
|
||||
export type * from './category-group';
|
||||
export type * from './dashboard';
|
||||
export type * from './gocardless';
|
||||
export type * from './import-transaction';
|
||||
export type * from './note';
|
||||
export type * from './openid';
|
||||
export type * from './payee';
|
||||
|
||||
@@ -4,14 +4,14 @@ interface BaseTemplate {
|
||||
directive: string;
|
||||
}
|
||||
|
||||
interface PercentageTemplate extends BaseTemplate {
|
||||
export interface PercentageTemplate extends BaseTemplate {
|
||||
type: 'percentage';
|
||||
percent: number;
|
||||
previous: boolean;
|
||||
category: string;
|
||||
}
|
||||
|
||||
interface WeekTemplate extends BaseTemplate {
|
||||
export interface WeekTemplate extends BaseTemplate {
|
||||
type: 'week';
|
||||
amount: number;
|
||||
weeks: number | null;
|
||||
@@ -24,7 +24,7 @@ interface WeekTemplate extends BaseTemplate {
|
||||
};
|
||||
}
|
||||
|
||||
interface ByTemplate extends BaseTemplate {
|
||||
export interface ByTemplate extends BaseTemplate {
|
||||
type: 'by';
|
||||
amount: number;
|
||||
month: string;
|
||||
@@ -33,7 +33,7 @@ interface ByTemplate extends BaseTemplate {
|
||||
from?: string;
|
||||
}
|
||||
|
||||
interface SpendTemplate extends BaseTemplate {
|
||||
export interface SpendTemplate extends BaseTemplate {
|
||||
type: 'spend';
|
||||
amount: number;
|
||||
month: string;
|
||||
@@ -42,7 +42,7 @@ interface SpendTemplate extends BaseTemplate {
|
||||
repeat?: number;
|
||||
}
|
||||
|
||||
interface SimpleTemplate extends BaseTemplate {
|
||||
export interface SimpleTemplate extends BaseTemplate {
|
||||
type: 'simple';
|
||||
monthly?: number;
|
||||
limit?: {
|
||||
@@ -53,14 +53,14 @@ interface SimpleTemplate extends BaseTemplate {
|
||||
};
|
||||
}
|
||||
|
||||
interface ScheduleTemplate extends BaseTemplate {
|
||||
export interface ScheduleTemplate extends BaseTemplate {
|
||||
type: 'schedule';
|
||||
name: string;
|
||||
full?: boolean;
|
||||
adjustment?: number;
|
||||
}
|
||||
|
||||
interface RemainderTemplate extends BaseTemplate {
|
||||
export interface RemainderTemplate extends BaseTemplate {
|
||||
type: 'remainder';
|
||||
weight: number;
|
||||
limit?: {
|
||||
@@ -71,17 +71,17 @@ interface RemainderTemplate extends BaseTemplate {
|
||||
};
|
||||
}
|
||||
|
||||
interface AverageTemplate extends BaseTemplate {
|
||||
export interface AverageTemplate extends BaseTemplate {
|
||||
type: 'average';
|
||||
numMonths: number;
|
||||
}
|
||||
|
||||
interface GoalTemplate extends BaseTemplate {
|
||||
export interface GoalTemplate extends BaseTemplate {
|
||||
type: 'goal';
|
||||
amount: number;
|
||||
}
|
||||
|
||||
interface CopyTemplate extends BaseTemplate {
|
||||
export interface CopyTemplate extends BaseTemplate {
|
||||
type: 'copy';
|
||||
lookBack: number;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"rootDir": ".",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"allowJs": false,
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
// @ts-strict-ignore
|
||||
export {};
|
||||
|
||||
type FileDialogOptions = {
|
||||
properties?: Array<'openFile' | 'openDirectory'>;
|
||||
filters?: {
|
||||
name: string;
|
||||
extensions: string[];
|
||||
}[];
|
||||
};
|
||||
|
||||
type Actual = {
|
||||
IS_DEV: boolean;
|
||||
ACTUAL_VERSION: string;
|
||||
@@ -9,7 +18,7 @@ type Actual = {
|
||||
filename: string,
|
||||
dialogTitle?: string,
|
||||
) => Promise<void>;
|
||||
openFileDialog: (options) => Promise<string[]>;
|
||||
openFileDialog: (options: FileDialogOptions) => Promise<string[]>;
|
||||
relaunch: () => void;
|
||||
reload: (() => Promise<void>) | undefined;
|
||||
restartElectronServer: () => void;
|
||||
@@ -20,7 +29,7 @@ type Actual = {
|
||||
applyAppUpdate: () => Promise<void>;
|
||||
updateAppMenu: (budgetId: string) => void;
|
||||
ipcConnect: (callback: (client) => void) => void;
|
||||
getServerSocket: () => Promise<string | null>;
|
||||
getServerSocket: () => Promise<Worker | null>;
|
||||
setTheme: (theme: string) => void;
|
||||
logToTerminal: (...args: unknown[]) => void;
|
||||
onEventFromMain: (
|
||||
6
upcoming-release-notes/5060.md
Normal file
6
upcoming-release-notes/5060.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [jfdoming]
|
||||
---
|
||||
|
||||
Move more .d.ts files to .ts
|
||||
Reference in New Issue
Block a user