Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/juxtaposition-ui/src/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,14 @@ export type ScreenshotUrls = {
};

export type UploadScreenshotOptions = {
buffer?: Buffer;
blob: string;
pid: number;
postId: string;
};
export async function uploadScreenshot(opts: UploadScreenshotOptions): Promise<ScreenshotUrls | null> {
const screenshotBuf = Buffer.from(opts.blob.replace(/\0/g, '').trim(), 'base64');
// try buffer, otherwise blob
const screenshotBuf = opts.buffer ?? Buffer.from(opts.blob.replace(/\0/g, '').trim(), 'base64');
const screenshots = processJpgScreenshot(screenshotBuf);
if (screenshots === null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ communitiesRouter.get('/:communityID/create', async function (req, res) {
name: community.name,
url: `/posts/new`,
show: 'post',
shotMode
shotMode,
community
};
res.jsxForDirectory({
ctr: <CtrNewPostPage {...props} />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import type { PaintingUrls } from '@/images';
import type { PostPageViewProps } from '@/services/juxt-web/views/web/postPageView';
import type { HydratedSettingsDocument } from '@/models/settings';
import type { ContentSchema } from '@/models/content';
const upload = multer({ dest: 'uploads/' });
const storage = multer.memoryStorage();
const upload = multer({ storage });
export const postsRouter = express.Router();

const postLimit = rateLimit({
Expand Down Expand Up @@ -136,7 +137,7 @@ postsRouter.post('/empathy', yeahLimit, async function (req, res) {
await redisRemove(`${post.pid}_user_page_posts`);
});

postsRouter.post('/new', postLimit, upload.none(), async function (req, res) {
postsRouter.post('/new', postLimit, upload.fields([{ name: 'shot', maxCount: 1 }]), async function (req, res) {
await newPost(req, res);
});

Expand Down Expand Up @@ -242,7 +243,7 @@ postsRouter.delete('/:post_id', async function (req, res) {
await redisRemove(`${post.pid}_user_page_posts`);
});

postsRouter.post('/:post_id/new', postLimit, upload.none(), async function (req, res) {
postsRouter.post('/:post_id/new', postLimit, upload.fields([{ name: 'shot', maxCount: 1 }]), async function (req, res) {
await newPost(req, res);
});

Expand Down Expand Up @@ -270,7 +271,8 @@ postsRouter.get('/:post_id/create', async function (req, res) {
pid: parent.pid,
url: `/posts/${parent.id}/new`,
show: 'post',
shotMode
shotMode,
community
};
res.jsxForDirectory({
ctr: <CtrNewPostPage {...props} />,
Expand Down Expand Up @@ -335,7 +337,7 @@ postsRouter.post('/:post_id/report', upload.none(), async function (req, res) {
});

async function newPost(req: Request, res: Response): Promise<void> {
const { params, body, auth } = parseReq(req, {
const { params, body, files, auth } = parseReq(req, {
params: z.object({
post_id: z.string().optional()
}),
Expand All @@ -350,7 +352,8 @@ async function newPost(req: Request, res: Response): Promise<void> {
spoiler: z.stringbool().default(false),
is_app_jumpable: z.stringbool().default(false),
language_id: z.coerce.number().optional()
})
}),
files: ['shot']
});

const userSettings = await database.getUserSettings(auth().pid);
Expand All @@ -370,7 +373,7 @@ async function newPost(req: Request, res: Response): Promise<void> {
}
}
}
if (params.post_id && (body.body === '' && body.painting === '' && body.screenshot === '')) {
if (params.post_id && (body.body === '' && body.painting === '' && body.screenshot === '' && files.shot.length == 0)) {
res.status(422);
return res.redirect('/posts/' + req.params.post_id.toString());
}
Expand Down Expand Up @@ -404,8 +407,10 @@ async function newPost(req: Request, res: Response): Promise<void> {
}
}
let screenshots = null;
if (body.screenshot && getShotMode(community, auth().paramPackData) !== 'block') {
if ((body.screenshot || files.shot.length === 1) &&
getShotMode(community, auth().paramPackData) !== 'block') {
screenshots = await uploadScreenshot({
buffer: files.shot[0]?.buffer,
blob: body.screenshot,
pid: auth().pid,
postId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,19 @@ export function CtrCommunityView(props: CommunityViewProps): ReactNode {
<button
type="button"
className={cx('small-button follow', {
suggested: props.hasSubCommunities,
selected: props.isUserFollowing
suggested: props.hasSubCommunities

})}
evt-click="follow(this)"
data-sound="SE_WAVE_CHECKBOX_UNCHECK"
data-url="/titles/follow"
data-community-id={community.olive_community_id}
>
<span className="sprite sp-yeah inline-sprite"></span>
<span className={cx('sprite sp-yeah inline-sprite', {
selected: props.isUserFollowing
})}
>
</span>
</button>
)
: null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ export function CtrNewPostView(props: NewPostViewProps): ReactNode {
const url = useUrl();
const user = useUser();
const cache = useCache();
const { ctrBanner, ctrLegacy } = props;
const { bannerUrl, legacy } = props.community ? url.ctrHeader(props.community) : {};
const name = props.name ?? cache.getUserName(props.pid ?? 0);
return (
<div id="add-post-page" className="add-post-page official-user-post">
<header
id="header"
style={{
background: ctrBanner ? `url('${ctrBanner}')` : ''
background: bannerUrl ? `url('${bannerUrl}')` : ''
}}
className={cx(
{ 'header-legacy': ctrLegacy }
{ 'header-legacy': legacy }
)}

data-toolbar-mode="wide"
Expand All @@ -61,7 +61,7 @@ export function CtrNewPostView(props: NewPostViewProps): ReactNode {
<T k="new_post.post_to" values={{ user: name ?? '' }} />
</h1>
</header>
<form method="post" action={props.url} id="posts-form" data-is-own-title="1" data-is-identified="1">
<form method="post" action={props.url} id="posts-form" data-is-own-title="1" data-is-identified="1" encType="multipart/form-data">
<input type="hidden" name="community_id" value={props.id} />
<input type="hidden" name="bmp" value="true" />
<div className="add-post-page-content">
Expand Down Expand Up @@ -93,7 +93,18 @@ export function CtrNewPostView(props: NewPostViewProps): ReactNode {
{props.shotMode !== 'block'
? (
<CtrTabView name="_post_type" value="shot" sprite="sp-shot-input" data-shot-mode={props.shotMode}>
<div id="shot-msg">Screenshots are not ready yet. Check back soon!</div>
<div id="shot-preview" data-shot-preview="1"></div>

<div className="shot-picker">
<input type="radio" name="shot-type" className="shot top" data-shot="1" data-lls="shot-top" />
<input type="radio" name="shot-type" className="shot btm" data-shot="0" data-lls="shot-btm" />
<div id="shot-clear">
<div className="sprite sp-clear centred" />
<input type="radio" name="shot-type" data-shot-clear="1" />
</div>
</div>

<input type="file" name="shot" data-shot-upload="1" disabled />
</CtrTabView>
)
: null }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function CtrNotificationItem(props: NotificationItemProps): ReactNode {
<CtrMiiIcon pid={Number(notif.objectID)} type="icon"></CtrMiiIcon>
<div className="body">
<p>
<a className="link" href={notif.link ?? '#'}>
<a className="link" href={notif.link ?? '#'} data-pjax="#body">
<T
k={i18nKey}
values={{
Expand Down Expand Up @@ -59,7 +59,7 @@ function CtrNotificationItem(props: NotificationItemProps): ReactNode {
<>
<CtrIcon href={notif.link ?? undefined} src={notif.image ?? ''}></CtrIcon>
<div className="body">
<a href={notif.link ?? undefined}>
<a href={notif.link ?? undefined} data-pjax="#body">
<p style={{ color: 'black' }}>
<span>{notif.text}</span>
<span className="timestamp">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export function CtrUserPageView(props: UserPageViewProps): ReactNode {
{isSelf ? <a id="header-communities-button" className="header-button left" href="/users/me/settings" data-pjax="#body"><T k="user_page.settings" /></a> : null}
{ canViewUser && !isSelf
? (
<button type="button" className={cx('small-button follow', { selected: isRequesterFollowingUser })} evt-click="follow(this)" data-sound="SE_WAVE_CHECKBOX_UNCHECK" data-url="/users/follow" data-community-id={props.user.pid}>
<span className="sprite sp-yeah inline-sprite"></span>
<button type="button" className="small-button follow" evt-click="follow(this)" data-sound="SE_WAVE_CHECKBOX_UNCHECK" data-url="/users/follow" data-community-id={props.user.pid}>
<span className={cx('sprite sp-yeah inline-sprite', { selected: isRequesterFollowingUser })}></span>
</button>
)
: null}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { T } from '@/services/juxt-web/views/common/components/T';
import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl';
import { useUser } from '@/services/juxt-web/views/common/hooks/useUser';
import type { InferSchemaType } from 'mongoose';
import type { ReactNode } from 'react';
import type { CommunityShotMode } from '@/models/communities';
import type { CommunitySchema, CommunityShotMode } from '@/models/communities';

const empathies = [
{
Expand Down Expand Up @@ -51,11 +52,10 @@ export type NewPostViewProps = {
pid?: number;
url: string;
show: string;
// must provide messagePid OR community
messagePid?: number;
community?: InferSchemaType<typeof CommunitySchema>;
shotMode: CommunityShotMode;
// ctr only
ctrBanner?: string;
ctrLegacy?: boolean;
};

export function WebNewPostView(props: NewPostViewProps): ReactNode {
Expand Down
79 changes: 77 additions & 2 deletions apps/juxtaposition-ui/webfiles/ctr/css/new-post-view.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,83 @@
height: 100%;
}

#shot-msg {
padding: 5px;
#shot-preview {
float: right;
width: 200px;
height: 120px;

background-color: lightgray;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
border: 0;
}

.shot-picker {
display: inline-block;
padding: 4px;
margin: 5px 14px;
border: 1px solid gray;

position: relative;
border-radius: 2px;

input {
appearance: none;
display: block;
padding: 0;
margin: 0;
cursor: pointer;
}

.shot {
margin: auto;
border: 1px solid white;
border-radius: 2px;

/* background-image set via script */
background-size: contain;

height: 50px; /* 48 + border */
&.top {
/* 400x240px -> 80px + border */
width: 82px;
}
&.btm {
/* 320x240px -> 64 + border*/
width: 66px;
}
&:checked {
border: 1px solid #6b3cb5;
}
}

#shot-clear {
width: 17px;
height: 17px;
position: absolute;
bottom: -4px; /* ^^; */
right: -9px;

border-radius: 2px;
border: 1px solid gray;
background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dfdfdf)) 0 0;
input {
width: 100%;
height: 100%;
}
}
}

/* Display: none makes it stop taking focus.. so instead just kinda hide it */
input[type="file"] {
position: absolute;
top: 0px;
opacity: 0;
/* Small enough to fit */
width: 10px;
height: 10px;
pointer-events: none;
}

#memo-img-input {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions apps/juxtaposition-ui/webfiles/ctr/js/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,9 +550,9 @@ if (typeof cave === 'undefined') {
console.log('cave.getPath(' + key + ')');
var value = localStorage.getItem(key);
if (value == 0) {
return '/img/dummy-image/screenshot-dummy-3ds-low.jpeg';
return '/images/dummy-shot-bottom.jpg';
} else {
return '/img/dummy-image/screenshot-dummy-3ds-upper.jpeg';
return '/images/dummy-shot-top.jpg';
}
},
/**
Expand Down
Loading