Skip to content

Commit

Permalink
chore: clean up some todos (#321)
Browse files Browse the repository at this point in the history
* chore: co-locate translation files

* chore: tag conversion fix + remove app insights (needs log rotation now)

* feat: tag conversion todos
  • Loading branch information
mvdicarlo authored Dec 4, 2024
1 parent 1fd51a6 commit ad0e37f
Show file tree
Hide file tree
Showing 24 changed files with 138 additions and 1,102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ type ConversionWeights = {
[outputMimeType in SupportedOutputMimeTypes]: number;
};

// TODO - use this within the post service to convert alt files to acceptable format
// TODO - use overall conversion check within the validator service to see if we can convert the file, this may be useful for the end user
/**
* A class that converts text files to other text formats.
* Largely for use when converting AltFiles (text/html) to other desirable formats.
Expand Down
10 changes: 7 additions & 3 deletions apps/client-server/src/app/post/post-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { IsTestEnvironment } from '../utils/test.util';
import { ValidationService } from '../validation/validation.service';
import {
ImplementedFileWebsite,
isFileWebsite
isFileWebsite,
} from '../websites/models/website-modifiers/file-website';
import { MessageWebsite } from '../websites/models/website-modifiers/message-website';
import { Website } from '../websites/website';
Expand Down Expand Up @@ -110,16 +110,18 @@ export class PostManagerService {
* i.e. the submission is deleted.
* @param {SubmissionId} id
*/
public async cancelIfRunning(id: SubmissionId) {
public async cancelIfRunning(id: SubmissionId): Promise<boolean> {
if (this.currentPost) {
if (!this.currentPost.parent) {
const loaded = await wrap(this.currentPost).init(true, ['parent']);
if (loaded.parent.id === id) {
this.logger.info(`Cancelling current post`);
this.cancelToken.cancel();
return true;
}
}
}
return false;
}

/**
Expand Down Expand Up @@ -396,7 +398,9 @@ export class PostManagerService {

// Split files into batches based on instance file batch size
const batches = chunk(orderedFiles, fileBatchSize);
let batchIndex = 0;
for (const batch of batches) {
batchIndex += 1;
this.cancelToken.throwIfCancelled();

// Resize files if necessary
Expand All @@ -416,10 +420,10 @@ export class PostManagerService {
// Post
this.cancelToken.throwIfCancelled();
this.logger.info(`Posting file batch to ${instance.id}`);
// TODO - Do something with nextBatchNumber
const result = await instance.onPostFileSubmission(
data,
processedFiles,
batchIndex,
this.cancelToken,
);

Expand Down
10 changes: 4 additions & 6 deletions apps/client-server/src/app/post/post.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,11 @@ export class PostService extends PostyBirbService<PostRecord> {
parent: { $in: request.ids },
});

// TODO - reconsider what happens to a cancelled post since it seems to have strange behavior.
// Is it best to not remove it if it is already in a post state and just mark it as cancelled?

// Only remove those that are not marked as done as to protect the archived posts.
const incomplete = existing.filter(
(e: PostRecord) => e.completedAt === undefined,
);
// Ignore the running posts as they are in progress and will be handled naturally through throws.
const incomplete = existing
.filter((e: PostRecord) => e.completedAt === undefined)
.filter((e: PostRecord) => e.state === PostRecordState.PENDING);

request.ids.forEach((id) => this.postManagerService.cancelIfRunning(id));
await Promise.all(incomplete.map((i) => this.remove(i.id)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ export class SubmissionController extends PostyBirbController<Submission> {
const mapper = (res) => res.toJSON();
if ((files || []).length) {
const results = [];
// TODO - need to reconsider how to queue submission creation up.
// !NOTE: Currently this shouldn't be able to happen with the current UI, but may need to be addressed in the future.
// Efforts have been made to prevent this from happening, with the removal of using entity.create({}) but it may still be possible.
// There appears to be an issue where if trying to create many submissions in parallel
// the database will attempt to create them all at once and fail on a race condition.
// not sure if this is a database issue or a typeorm issue.
// eslint-disable-next-line no-restricted-syntax
for (const file of files) {
const createFileSubmission = new CreateSubmissionDto();
Object.assign(createFileSubmission, createSubmissionDto);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,15 @@ describe('TagConvertersService', () => {
await service.remove(record.id);
expect(await service.findAll()).toHaveLength(0);
});

it('should convert tags', async () => {
const dto = createTagConverterDto('test', { default: 'converted' });

await service.create(dto);
const result = await service.convert(
{ decoratedProps: { metadata: { name: 'default' } } } as any,
['test', 'test2'],
);
expect(result).toEqual(['converted', 'test2']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export class TagConvertersService extends PostyBirbService<TagConverter> {
return this.repository.update(id, update);
}

// TODO - write tests for this
/**
* Converts a list of tags using user defined conversion table.
*
Expand All @@ -56,7 +55,7 @@ export class TagConvertersService extends PostyBirbService<TagConverter> {
}
return (
converter.convertTo[instance.decoratedProps.metadata.name] ??
converter.convertTo.default ??
converter.convertTo.default ?? // NOTE: This is not currently used, but it's here for future proofing
tag
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function validateDescriptionMaxLength({
const maxLength =
descriptionSupport.maxDescriptionLength ?? Number.MAX_SAFE_INTEGER;
if (description.length > maxLength) {
result.errors.push({
result.warnings.push({
id: 'validation.description.max-length',
field: 'description',
values: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { DescriptionType, DynamicObject, ILoginState } from '@postybirb/types';
import { SupportsDescription } from '../../decorators/supports-description.decorator';
import { SupportsTags } from '../../decorators/supports-tags.decorator';
import { WebsiteMetadata } from '../../decorators/website-metadata.decorator';
import { Website } from '../../website';

// This is a stub used for filling in for places where we have a null account
// but need to have a website instance.
@SupportsTags()
@SupportsDescription(DescriptionType.PLAINTEXT)
@WebsiteMetadata({ name: 'default', displayName: 'Default' })
export default class DefaultWebsite extends Website<DynamicObject> {
protected BASE_URL: string;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { DiscordMessageSubmission } from './models/discord-message-submission';
acceptedFileSizes: {},
fileBatchSize: 10,
})
@SupportsDescription(DescriptionType.MARKDOWN)
@SupportsDescription(DescriptionType.MARKDOWN, 2_000)
export default class Discord
extends Website<DiscordAccountData>
implements
Expand Down Expand Up @@ -77,6 +77,7 @@ export default class Discord
onPostFileSubmission(
postData: PostData<FileSubmission, IWebsiteFormFields>,
files: PostingFile[],
batchIndex: number,
cancellationToken: CancellableToken,
): Promise<PostResponse> {
throw new Error('Method not implemented.');
Expand Down Expand Up @@ -104,18 +105,6 @@ export default class Discord
const result: SimpleValidationResult<DiscordMessageSubmission> = {
warnings: [],
};

// TODO - update validation
// if (postData.options.description.description.trim().length > 2_000) {
// result.warnings.push({
// id: 'validation.description.max-length',
// field: 'description',
// values: {
// maxLength: 2_000,
// },
// });
// }

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default class FurAffinity
onPostFileSubmission(
postData: PostData<FileSubmission, FurAffinityFileSubmission>,
files: PostingFile[],
batchIndex: number,
cancellationToken: CancellableToken,
): Promise<PostResponse> {
throw new Error('Method not implemented.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default class TestWebsite
onPostFileSubmission(
postData: PostData<FileSubmission, IWebsiteFormFields>,
files: PostingFile[],
batchIndex: number,
cancellationToken: CancellableToken,
): Promise<PostResponse> {
throw new Error('Method not implemented.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,19 @@ export interface FileWebsite<T extends IWebsiteFormFields> {

calculateImageResize(file: ISubmissionFile): ImageResizeProps | undefined;

/**
* Handles the submission of a file to the website.
*
* @param {PostData<FileSubmission, T>} postData
* @param {PostingFile[]} files - The files to post
* @param {number} batchIndex - The index of the batch (if batching is required)
* @param {CancellableToken} cancellationToken
* @return {*} {Promise<PostResponse>}
*/
onPostFileSubmission(
postData: PostData<FileSubmission, T>,
files: PostingFile[],
batchIndex: number,
cancellationToken: CancellableToken,
): Promise<PostResponse>;

Expand Down
4 changes: 2 additions & 2 deletions apps/postybirb-ui/src/components/form/fields/field-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { useLingui } from '@lingui/react';
import { Input } from '@mantine/core';
import type { FieldAggregateType } from '@postybirb/form-builder';
import { PropsWithChildren } from 'react';
import { FieldLabelTranslations } from '../../translations/field-translations';
import { ValidationTranslation } from '../../translations/validation-translation';
import { UseValidationResult } from '../hooks/use-validations';
import { fieldLabelTranslations } from './field-translations';
import { FormFieldProps } from './form-field.type';

type FieldLabelProps = FormFieldProps & {
Expand All @@ -16,7 +16,7 @@ export function getTranslatedLabel(
field: FieldAggregateType,
converter: (msg: MessageDescriptor) => string,
): string {
const translationLabel = fieldLabelTranslations[field.label];
const translationLabel = FieldLabelTranslations[field.label];

if (!translationLabel) {
// eslint-disable-next-line lingui/no-unlocalized-strings, no-console
Expand Down
1 change: 0 additions & 1 deletion apps/postybirb-ui/src/components/form/fields/field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,5 @@ export function Field(props: FormFieldProps): JSX.Element | null {
formField = <div>Unknown field type: {field.formField}</div>;
}

// TODO - Happily merge external translation and the shared translations.
return formField;
}
69 changes: 64 additions & 5 deletions apps/postybirb-ui/src/components/form/fields/tag-field.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { Trans } from '@lingui/macro';
import { Box, Checkbox, Pill, TagsInput, Text } from '@mantine/core';
import {
Badge,
Box,
Checkbox,
Group,
Pill,
TagsInput,
Text,
} from '@mantine/core';
import { TagFieldType } from '@postybirb/form-builder';
import { Tag, TagGroupDto, TagValue } from '@postybirb/types';
import { Tag, TagConverterDto, TagGroupDto, TagValue } from '@postybirb/types';
import { IconArrowRight } from '@tabler/icons-react';
import { flatten, uniq } from 'lodash';
import { useWebsites } from '../../../hooks/account/use-websites';
import { TagConverterStore } from '../../../stores/tag-converter-store';
import { TagGroupStore } from '../../../stores/tag-group-store';
import { useStore } from '../../../stores/use-store';
import { useDefaultOption } from '../hooks/use-default-option';
Expand All @@ -16,16 +27,45 @@ function containsAllTagsInGroup(tags: Tag[], group: TagGroupDto): boolean {
return group.tags.every((tag) => tags.includes(tag));
}

// TODO - figure out some way to support indicating a tag converter is being used
function getTagConversion(
website: string,
tagConverters: TagConverterDto[],
tag: Tag,
): Tag {
const matchingConverter = tagConverters.find(
(converter) => converter.tag === tag,
);
if (!matchingConverter) {
return tag;
}

return (
matchingConverter.convertTo[website] ??
matchingConverter.convertTo.default ??
tag
);
}

export function TagField(props: FormFieldProps<TagFieldType>): JSX.Element {
const { field, form, propKey, option } = props;
const { state: tagGroups } = useStore(TagGroupStore);
const defaultOption = useDefaultOption<TagValue>(props);
const validations = useValidations(props);

const { state: tagConverters } = useStore(TagConverterStore);
const { accounts } = useWebsites();
const account = accounts.find((acc) => acc.id === option.account);
const overrideProps = form.getInputProps(`${propKey}.overrideDefault`);
const tagsProps = form.getInputProps(`${propKey}.tags`);
const tagValue = tagsProps.defaultValue as Tag[];
const allTags = [...tagValue, ...(defaultOption?.tags || [])];
const convertedTags = (
account
? allTags.map((tag) => [
tag,
getTagConversion(account.website, tagConverters, tag),
])
: []
).filter(([tag, converted]) => converted !== tag);

const tagGroupsOptions = tagGroups.map((tagGroup) => ({
label: `${TAG_GROUP_LABEL}${JSON.stringify(tagGroup)}`,
Expand All @@ -48,7 +88,7 @@ export function TagField(props: FormFieldProps<TagFieldType>): JSX.Element {

const totalTags: number = overrideProps.defaultValue
? tagValue.length
: [...tagValue, ...(defaultOption?.tags || [])].length;
: allTags.length;

return (
<Box>
Expand All @@ -63,6 +103,25 @@ export function TagField(props: FormFieldProps<TagFieldType>): JSX.Element {
}
/>
)}
{convertedTags.length > 0 && (
<Box>
<Text display="inline-block" size="sm" c="green">
<Trans>Converted:</Trans>
</Text>
<Group display="inline-block" ml="4">
{convertedTags.map(([tag, convertedTag]) => (
<Badge size="xs" variant="light">
{tag}{' '}
<IconArrowRight
size="0.75rem"
style={{ verticalAlign: 'middle' }}
/>{' '}
{convertedTag}
</Badge>
))}
</Group>
</Box>
)}
<TagsInput
inputWrapperOrder={['label', 'input', 'description', 'error']}
clearable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IconAlertCircle, IconAlertTriangle } from '@tabler/icons-react';
import { useMemo } from 'react';
import { useWebsites } from '../../../../../hooks/account/use-websites';
import { SubmissionDto } from '../../../../../models/dtos/submission.dto';
import { ValidationTranslation } from '../../../../translations/translation';
import { ValidationTranslation } from '../../../../translations/validation-translation';

type FileValidationsProps = {
submission: SubmissionDto;
Expand Down
Loading

0 comments on commit ad0e37f

Please sign in to comment.