Merge remote-tracking branch 'origin/main'

This commit is contained in:
Nevo David 2026-05-11 11:19:41 +07:00
commit 39f2a176e1
12 changed files with 47 additions and 68 deletions

View file

@ -1,3 +1,5 @@
<!-- Remember to first apply via [the contribution form](https://contribute.postiz.com/p/postiz) before submitting a PR. -->
# What kind of change does this PR introduce?
eg: Bug fix, feature, docs update, ...
@ -16,5 +18,5 @@ Put a "X" in the boxes below to indicate you have followed the checklist;
- [ ] I have read the [CONTRIBUTING](https://github.com/gitroomhq/postiz-app/blob/main/CONTRIBUTING.md) guide.
- [ ] I confirm I have not used AI to submit this PR or generate code for it.
- [ ] I checked that there were not similar issues or PRs already open for this.
- [ ] This PR fixes just ONE issue (do not include multiple issues or types of change in the same PR) For example, don't try and fix a UI issue and include new dependencies in the same PR.
- [ ] I checked that there were no similar issues or PRs already open for this.
- [ ] This PR fixes just ONE issue

View file

@ -3,6 +3,8 @@ name: Build
on:
push:
merge_group:
pull_request:
jobs:
build:

View file

@ -1,5 +1,5 @@
---
name: "Code Quality Analysis"
name: "Code Quality Analysis"
on:
push:
@ -9,6 +9,8 @@ on:
- apps/**
- '!apps/docs/**'
- libraries/**
merge_group:
jobs:
analyze:

View file

@ -1,52 +0,0 @@
name: PR Quality
permissions:
contents: read
issues: read
pull-requests: write
on:
pull_request_target:
types: [opened, reopened]
jobs:
anti-slop:
runs-on: ubuntu-latest
steps:
- uses: peakoss/anti-slop@v0
with:
# Overall
max-failures: 3
# Other
require-maintainer-can-modify: true
max-negative-reactions: 3
require-conventional-title: true
# Description
max-emoji-count: 2
max-code-references: 3
blocked-terms: "Generated with Claude Code,Generated with Codex"
# PR Template
require-pr-template: true
strict-pr-template-sections: "What kind of change does this PR introduce?,Why was this change needed?,Checklist:"
optional-pr-template-sections: "Other information:"
max-additional-pr-template-sections: 2
# User
detect-spam-usernames: true
min-account-age: 30
max-daily-forks: 5
min-profile-completeness: 4
# Exemptions
exempt-author-association: "OWNER,MEMBER,COLLABORATOR"
exempt-users: "nevo-david,egelhaus"
exempt-bots: "postiz-agent[bot]"
# Actions
exempt-label: "exempt"
close-pr: true
failure-add-pr-labels: "spam"
failure-pr-message: "This PR has been marked as Spam, please re-open if this is a mistake."

View file

@ -6,6 +6,10 @@ Contributions are welcome - code, docs, whatever it might be! If this is your fi
The main documentation site has a [developer guide](https://docs.postiz.com/developer-guide) . That guide provides you a good understanding of the project structure, and how to setup your development environment. Read this document after you have read that guide. This document is intended to provide you a good understanding of how to submit your first contribution.
## Apply via the contribution form
To submit your contribution, please fill out the [contribution form](https://contribute.postiz.com/p/postiz). This helps us evaluate whether your contribution is a good fit for the project. We will review your submission and get back to you as soon as possible.
## Write code with others
This is an open source project, with an open and welcoming community that is always keen to welcome new contributors. We recommend the two best ways to interact with the community are:

View file

@ -43,6 +43,9 @@ export class AuthService {
if (process.env.DISALLOW_PLUS && body.email.includes('+')) {
throw new Error('Email with plus sign is not allowed');
}
if (body instanceof CreateOrgUserDto) {
body.email = body.email.toLowerCase();
}
const user = await this._userService.getUserByEmail(body.email);
if (body instanceof CreateOrgUserDto) {
if (user) {

View file

@ -147,7 +147,7 @@ export const ImportDebugPostModal: FC<{ close: () => void }> = ({ close }) => {
<div className="text-[13px] font-[600] text-textColor">
{t('debug_info', 'Debug Info')}
</div>
<div className="text-[12px] text-textColor/70 flex flex-col gap-[4px]">
<div className="text-[12px] text-textColor/70 flex flex-col gap-[4px] min-w-0 break-all">
<div>
<span className="font-[500]">
{t('provider', 'Provider')}:
@ -175,7 +175,7 @@ export const ImportDebugPostModal: FC<{ close: () => void }> = ({ close }) => {
<span className="font-[500]">
{t('error_details', 'Error Details')}:
</span>
<div className="mt-[4px] max-h-[100px] overflow-y-auto bg-newBgColor p-[8px] rounded-[4px] text-[11px] font-mono">
<div className="mt-[4px] max-h-[100px] overflow-y-auto bg-newBgColor p-[8px] rounded-[4px] text-[11px] font-mono break-all whitespace-pre-wrap">
{parsed._debug.errors.map((err, i) => (
<div key={i} className="mb-[4px]">
[{err.platform}] {err.message}

View file

@ -433,6 +433,7 @@ const ImportDebugPost = () => {
const handleClick = useCallback(() => {
openModal({
title: t('import_debug_post', 'Import Debug Post'),
maxSize: 800,
children: (close) => <ImportDebugPostModal close={close} />,
});
}, []);

View file

@ -31,6 +31,7 @@ interface OpenModalInterface {
modal?: string;
};
size?: string | number;
maxSize?: string | number;
height?: string | number;
id?: string;
}
@ -200,10 +201,11 @@ export const Component: FC<{
modal.size ? '' : 'min-w-[600px]',
modal.fullScreen && 'h-full'
)}
{...((!!modal.size || !!modal.height) && {
{...((!!modal.size || !!modal.height || !!modal.maxSize) && {
style: {
...(modal.size ? { width: modal.size } : {}),
...(modal.height ? { height: modal.height } : {}),
...(modal.maxSize ? { maxWidth: modal.maxSize } : {}),
},
})}
onClick={(e) => e.stopPropagation()}

View file

@ -3,8 +3,10 @@ import dayjs, { ConfigType } from 'dayjs';
import { FC, useEffect } from 'react';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(timezone);
dayjs.extend(utc);
dayjs.extend(relativeTime);
const { utc: originalUtc } = dayjs;

View file

@ -4,6 +4,7 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
import { FC, useCallback, useState } from 'react';
import clsx from 'clsx';
import dayjs from 'dayjs';
import { useClickAway } from '@uidotdev/usehooks';
import ReactLoading from '@gitroom/frontend/components/layout/loading';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
@ -26,16 +27,29 @@ export const ShowNotification: FC<{
const [newNotification] = useState(
new Date(notification.createdAt) > new Date(props.lastReadNotification)
);
const createdAt = dayjs(notification.createdAt);
const isWithin24h = dayjs().diff(createdAt, 'hour') < 24;
const fullDate = createdAt.format('MMM D, YYYY h:mm A');
return (
<div
className={clsx(
`text-textColor px-[16px] py-[10px] border-b border-tableBorder last:border-b-0 transition-colors overflow-hidden text-ellipsis`,
`text-textColor px-[16px] py-[10px] border-b border-tableBorder last:border-b-0 transition-colors`,
newNotification && 'font-bold bg-seventh animate-newMessages'
)}
dangerouslySetInnerHTML={{
__html: replaceLinks(notification.content),
}}
/>
>
<div
className="break-words"
dangerouslySetInnerHTML={{
__html: replaceLinks(notification.content),
}}
/>
<div
className="text-[11px] mt-[4px] opacity-60 font-normal"
title={isWithin24h ? fullDate : undefined}
>
{isWithin24h ? createdAt.fromNow() : fullDate}
</div>
</div>
);
};
export const NotificationOpenComponent = () => {
@ -57,7 +71,7 @@ export const NotificationOpenComponent = () => {
{t('notifications', 'Notifications')}
</div>
<div className="flex flex-col">
<div className="flex flex-col max-h-[400px] overflow-y-auto scrollbar scrollbar-thumb-fifth scrollbar-track-newBgColor">
{isLoading && (
<div className="flex-1 flex justify-center pt-12">
<ReactLoading type="spin" color="#fff" width={36} height={36} />

View file

@ -478,14 +478,13 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider {
private async prepareMediaBuffer(mediaUrl: string): Promise<Buffer> {
const isVideo = mediaUrl.indexOf('mp4') > -1;
const isGif = lookup(mediaUrl) === 'image/gif';
if (isVideo) {
if (isVideo || isGif) {
return Buffer.from(await readOrFetch(mediaUrl));
}
return await sharp(await readOrFetch(mediaUrl), {
animated: lookup(mediaUrl) === 'image/gif',
})
return await sharp(await readOrFetch(mediaUrl), { animated: false })
.toFormat('jpeg')
.resize({ width: 1000 })
.toBuffer();