Skip to content

Fix/pictique issues#775

Merged
coodos merged 10 commits intomainfrom
fix/pictique-issues
Feb 6, 2026
Merged

Fix/pictique issues#775
coodos merged 10 commits intomainfrom
fix/pictique-issues

Conversation

@Bekiboo
Copy link
Collaborator

@Bekiboo Bekiboo commented Feb 5, 2026

Description of change

UI/UX Improvements

  • Made CreatePostModal responsive (90vw → responsive breakpoints)
  • Redesigned image upload: replaced separate button with grid-integrated "Add Photo" frame
  • Implemented responsive image grids (2/3/4 columns) across CreatePostModal, Post, and UploadedPostView
  • Added visual progress bar showing upload size usage (green → yellow → red)
  • Improved profile settings page: better layout with preview circle, file size info

Upload Validation

  • Added file size tracking with 1MB per image, 5MB total limits
  • Real-time validation with error messages
  • Disables upload when limit reached
  • Added payload size logging for debugging

Bug Fixes

  • Prevented message spam by adding isSubmitting state with proper async handling
  • Profile picture validation and error handling
  • Post button submission protection

Technical

  • Identified server has ~1MB proxy limit despite 50MB backend config

Issue Number

Closes #732

Type of change

  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

  • New Features

    • Image upload with per-file and total size limits, previews, add-photo frame, per-image removal, and dynamic usage progress.
    • Empty-state message for conversations when history is loaded.
    • Profile picture upload with validation and save/loading states.
  • Style

    • Responsive grid layouts for image galleries and modals; refined spacing and updated icons/buttons.
  • Accessibility

    • Added ARIA labels for image removal and improved accessible controls.
  • Bug Fixes

    • Disabled submit actions when content is empty; surfaced upload and submission errors via banners.

@Bekiboo Bekiboo self-assigned this Feb 5, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

Warning

Rate limit exceeded

@Bekiboo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 34 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Introduces client-side image upload handling with per-file and total size validation, UI changes for image previews/removal, submission flow updates, message input submission state, profile picture validation/save handling, and small styling/layout adjustments across pictique components.

Changes

Cohort / File(s) Summary
Image Upload & Post UI
platforms/pictique/src/lib/fragments/CreatePostModal/CreatePostModal.svelte, platforms/pictique/src/routes/(protected)/post/+page.svelte, platforms/pictique/src/lib/fragments/UploadedPostView/UploadedPostView.svelte, platforms/pictique/src/lib/stores/posts.ts
Added structured uploadItems (file + dataUrl), MAX_FILE_SIZE/MAX_TOTAL_SIZE limits, per-file & total validation, dataUrl previews, image removal, responsive grid layout, progress bar and error banner, and payload logging before createPost.
File utilities & types
platforms/pictique/src/lib/utils/fileValidation.ts, platforms/pictique/src/lib/types.ts
Added validateFileSize and formatSize utilities plus FileValidationResult; extended exported Image type with optional size?: number.
Message Input & Messaging pages
platforms/pictique/src/lib/fragments/MessageInput/MessageInput.svelte, platforms/pictique/src/routes/(protected)/messages/[id]/+page.svelte, platforms/pictique/src/routes/(protected)/messages/+page.svelte
Replaced clickable div with Button, added isSubmitting/isDisabled and handleSubmit flow, prevented empty sends via disabled logic, and introduced conditional "No messages yet" empty-state when historyLoaded.
Profile/account avatar upload
platforms/pictique/src/routes/(protected)/settings/account/username/+page.svelte
Added client-side avatar file validation (MAX_FILE_SIZE), isSaving loading state, error/success banners, preview and InputFile handling, and guarded save action to prevent concurrent saves.
Minor styling & layout tweaks
platforms/pictique/src/lib/fragments/SideBar/SideBar.svelte, platforms/pictique/src/routes/(protected)/group/[id]/+page.svelte, platforms/pictique/src/routes/(protected)/messages/+page.svelte
Adjusted image path, spacing, button width classes, avatar sizing, and minor DOM/class rearrangements for alignment and responsiveness.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant UI as CreatePostModal
  participant Validator as validateFileSize
  participant Reader as FileReader
  participant Store as posts.createPost (API)

  User->>UI: selects file(s)
  UI->>Validator: validateFileSize(file, maxFile, currentTotal, maxTotal)
  alt validation fails
    Validator-->>UI: { valid: false, error }
    UI-->>User: show error banner
  else validation passes
    UI->>Reader: readAsDataURL(file)
    Reader-->>UI: dataUrl
    UI-->>UI: append uploadItems { file, dataUrl }
    UI->>User: show preview & update progress
    User->>UI: clicks Post
    UI->>Store: createPost({ text, images: uploadItems.map(url) })
    Store-->>UI: success / error
    UI-->>User: reset on success or show submission error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • coodos
  • sosweetham

Poem

🐰 I hopped in with files and care,
Checked each byte with watchful stare,
Previews bloom and errors sing,
Buttons hum—now send!—take wing,
A tiny rabbit clap and share. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Fix/pictique issues' is vague and generic, using a non-descriptive 'Fix/' prefix without clearly summarizing the main change or primary improvement made. Provide a more specific title describing the main change, such as 'Add image upload validation and responsive grid improvements' or similar.
Linked Issues check ❓ Inconclusive The PR addresses most key requirements: prevents empty messages [✓], adds 'no messages yet' placeholder [✓], improves save button and error handling [✓], makes buttons smaller/repositioned [✓], adds upload validation with size limits [✓], and improves layout. However, post date mismatch, profile picture stretching in sidebar/chats, receiver profile picture in chat, and auto-close on size-exceeded error remain unaddressed or incomplete. Verify whether unaddressed items (post date sync, profile picture stretching, receiver avatar in chat, auto-close modal on size error) are deferred to future PRs or if additional changes are required.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers all required template sections including issue number, type of change, and checklist items, with substantial detail about UI/UX improvements, upload validation, and bug fixes.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: image upload validation, responsive layouts, progress bars, error handling, preventing message/post submission spam, and placeholder display. No out-of-scope changes detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/pictique-issues

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Bekiboo Bekiboo marked this pull request as ready for review February 6, 2026 11:59
@Bekiboo Bekiboo requested a review from coodos as a code owner February 6, 2026 11:59
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
platforms/pictique/src/routes/(protected)/messages/[id]/+page.svelte (2)

103-103: ⚠️ Potential issue | 🔴 Critical

Fix inverted isOwn logic on line 103.

isOwn is set to true when sender.id !== userId, meaning it's true for messages from other users. However, the ChatMessage component uses isOwn to determine message styling and positioning—when isOwn is true, it renders the message with grey background, black text, and positioned on the right (own message formatting). This causes all messages to render on the wrong side.

Change line 103 to:

const isOwn = sender.id === userId;

220-220: ⚠️ Potential issue | 🟡 Minor

Remove unused src prop or implement avatar display for direct messages.

The src prop with the hardcoded picsum.photos URL is not rendered when variant="dm". The MessageInput component only displays the Avatar when variant="comment" (lines 48–50 of MessageInput.svelte). Either remove the unused prop, or modify MessageInput to display receiver avatars for direct messages and pass the actual participant avatar from the available chat.participants data.

platforms/pictique/src/routes/(protected)/settings/account/username/+page.svelte (1)

131-131: ⚠️ Potential issue | 🟡 Minor

Double space in placeholder text.

"Edit your public name" has two spaces between "Edit" and "your".

Fix
-		<Input type="text" placeholder="Edit  your public name" bind:value={name} />
+		<Input type="text" placeholder="Edit your public name" bind:value={name} />
platforms/pictique/src/lib/fragments/CreatePostModal/CreatePostModal.svelte (1)

53-53: ⚠️ Potential issue | 🟡 Minor

Remove console.log debug artifact.

This logs the full base64 data URL for every uploaded image. It clutters the console and leaks image data.

Fix
 		reader.onload = (e) => {
 			const result = e.target?.result;
 			if (typeof result === 'string') {
-				console.log(result);
 				images = [...images, result];
 			}
 		};
platforms/pictique/src/routes/(protected)/post/+page.svelte (2)

3-3: ⚠️ Potential issue | 🟡 Minor

UploadedPostView import is unused.

The template now renders an inline grid instead of using this component. Remove the dead import.

Fix
-	import { SettingsTile, UploadedPostView } from '$lib/fragments';
+	import { SettingsTile } from '$lib/fragments';

33-41: ⚠️ Potential issue | 🟠 Major

No state cleanup or navigation after successful post creation.

After createPost succeeds, uploadedImages is never cleared and there is no goto(...) call. The user remains on this page with stale images. The $effect on lines 11-15 would navigate back only if uploadedImages were emptied. Compare with CreatePostModal.handleSubmit which resets text, images, imageFiles, and error on success.

Suggested fix
 	const postSubmissionHandler = async () => {
 		if (!uploadedImages.value) return;
 		const images = uploadedImages.value.map((img) => img.url);
 		try {
 			await createPost(caption, images);
+			uploadedImages.value = [];
+			caption = '';
 		} catch (error) {
 			console.error('Failed to create post:', error);
 		}
 	};
🤖 Fix all issues with AI agents
In `@platforms/pictique/src/lib/fragments/CreatePostModal/CreatePostModal.svelte`:
- Around line 46-48: images and imageFiles are updated separately and can get
out of sync; change to a single array (e.g., uploadItems) of objects like {
file: File, dataUrl: string } and only append to it inside reader.onload after
both file and dataUrl are ready (remove the early imageFiles push at the top of
the handler). Update removeImage(index) and any other places that splice or
reference imageFiles/images to operate on uploadItems (and handle read errors by
not appending and setting error). Also apply the same single-array approach for
the other upload code paths noted around the 61-64 area so all read+file data
are added atomically in the reader.onload callback.
- Around line 78-79: The catch uses a parameter named error which shadows the
component-level let error = $state('') in CreatePostModal.svelte, so failures
are only logged and not shown to the user; rename the catch parameter (e.g., to
err or creationError), assign the component error state (the let error variable)
to a user-friendly message derived from the caught err (err.message or
String(err)), and keep the console logging for debugging; update the catch block
around the post creation logic to set the component-level error so the existing
UI error display works.
- Line 116: The {`#each` images as image, index (index)} loop in
CreatePostModal.svelte uses the array index as the key causing DOM reuse issues;
change the key to a stable identifier (e.g., use the image data URL or an
assigned id: {`#each` images as image (image.url)} or (image.id)). If your image
objects are currently plain strings, wrap them into objects with a unique id at
upload time (assign in your upload/insert handler such as the function that
pushes into images) so the each block can use image.id as the key.

In
`@platforms/pictique/src/lib/fragments/UploadedPostView/UploadedPostView.svelte`:
- Line 27: The {`#each`} in UploadedPostView.svelte currently uses the array index
as the key ("{`#each` images as image, i (i)}"), which causes incorrect DOM reuse
when items are removed; change the key to a stable identifier such as image.url
by replacing the index-key usage with a stable key expression (e.g., use
image.url) in the {`#each` images as image, i (...) } block and, if image.url may
be missing, fall back to a stable unique property (like image.id) or generate a
stable key before rendering.

In `@platforms/pictique/src/routes/`(protected)/messages/[id]/+page.svelte:
- Around line 195-197: The placeholder "No messages yet" flashes because
messages starts as [] until SSE delivers history; add a boolean flag (e.g., let
connected = false or let historyLoaded = false) and set it when the SSE connects
or after the first onmessage in watchEventStream (or in the SSE onopen handler),
then change the conditional to render the placeholder only when historyLoaded is
true && messages.length === 0; also remove the redundant !messages check since
messages is always an array (reference the messages variable and the
watchEventStream / SSE onopen/onmessage handlers to locate where to set the
flag).

In `@platforms/pictique/src/routes/`(protected)/post/+page.svelte:
- Around line 17-31: The handleImageUpload function lacks file-size validation;
replicate the same rules from CreatePostModal (max 1MB per file and 5MB total)
by (1) checking file.size before reading and rejecting files >1MB, (2) computing
currentTotal by summing a new size property on uploadedImages.value items plus
the incoming file.size to ensure total <=5MB, and (3) storing size on each
uploaded image object (e.g., { url, alt, size }) so subsequent uploads can
validate against the accumulated total; update uploadedImages usage and the
FileReader flow in handleImageUpload to enforce and abort on violations.
- Line 46: The {`#each` uploadedImages.value ?? [] as image, i (i)} loop uses the
index as the key which can cause DOM reuse bugs when images are added/removed;
update the each block to use a stable unique identifier like image.url (i.e.,
change the key from i to image.url) so the loop is keyed by uploadedImages.value
and image.url to ensure correct DOM updates for the uploadedImages.value array
and each image item.

In
`@platforms/pictique/src/routes/`(protected)/settings/account/username/+page.svelte:
- Around line 46-48: The base64 avatar upload via apiClient.patch with
profileImageDataUrl can exceed the server proxy ~1MB limit because base64
inflates size; update the upload flow to avoid base64: either lower
MAX_FILE_SIZE constant (reference MAX_FILE_SIZE used in the upload/validation
logic) to ~750KB so encoded payload stays under the proxy limit, or change the
request in the component that calls apiClient.patch('/api/users/', ...) to send
the avatar as multipart/form-data (send the raw File/Blob instead of
profileImageDataUrl) and adjust server/client headers accordingly; locate the
validation and submit logic around profileImageDataUrl and apiClient.patch to
implement the chosen fix.
🧹 Nitpick comments (10)
platforms/pictique/src/lib/fragments/SideBar/SideBar.svelte (1)

167-167: Minor inconsistency: only the Profile label uses mt-1 while the other four tabs still use mt-[4px].

Both are equivalent (4px), but the sidebar now mixes arbitrary and standard utility for the same spacing across sibling elements (lines 72, 95, 118, 141 vs. 167). Consider updating the other tabs to mt-1 as well for consistency.

♻️ Suggested diff to unify margin class across all tabs
 			<h3
-				class={`${activeTab === 'home' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-[4px]`}
+				class={`${activeTab === 'home' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-1`}
 			>
 				Feed
 			</h3>
 			<h3
-				class={`${activeTab === 'discover' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-[4px]`}
+				class={`${activeTab === 'discover' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-1`}
 			>
 				Search
 			</h3>
 			<h3
-				class={`${activeTab === 'messages' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-[4px]`}
+				class={`${activeTab === 'messages' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-1`}
 			>
 				Messages
 			</h3>
 			<h3
-				class={`${activeTab === 'settings' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-[4px]`}
+				class={`${activeTab === 'settings' ? 'text-brand-burnt-orange' : 'text-black-800'} mt-1`}
 			>
 				Settings
 			</h3>
platforms/pictique/src/lib/stores/posts.ts (2)

78-78: images.map((img) => img) is a no-op identity map.

This creates a shallow copy of the array without transforming any elements. If the intent is to clone the array, use [...images]; otherwise, just pass images directly.

♻️ Suggested simplification
 		const payload = {
 			text,
-			images: images.map((img) => img)
+			images
 		};

81-83: Debug console.log will ship to production.

This logging is helpful during development, but leaving it in a PR targeting main will add noise to end-user browser consoles on every post creation. Consider removing it before merge, or gating it behind an environment/debug flag.

♻️ Option A: Remove the debug log
-		// Log payload size for debugging
-		const payloadSize = new Blob([JSON.stringify(payload)]).size;
-		console.log(`Payload size: ${(payloadSize / 1024).toFixed(2)} KB (${images.length} images)`);
-		
 		const response = await apiClient.post('/api/posts', payload);
♻️ Option B: Gate behind a dev-only check
-		// Log payload size for debugging
-		const payloadSize = new Blob([JSON.stringify(payload)]).size;
-		console.log(`Payload size: ${(payloadSize / 1024).toFixed(2)} KB (${images.length} images)`);
+		if (import.meta.env.DEV) {
+			const payloadSize = new Blob([JSON.stringify(payload)]).size;
+			console.log(`Payload size: ${(payloadSize / 1024).toFixed(2)} KB (${images.length} images)`);
+		}
platforms/pictique/src/lib/fragments/MessageInput/MessageInput.svelte (2)

60-70: Stale a11y-ignore comments and tab character in class string.

Now that the raw <div> has been replaced with a <Button> component, the a11y_click_events_have_key_events and a11y_no_static_element_interactions suppression comments on lines 60–61 are likely unnecessary — a <button> element inherently satisfies both rules.

Also, line 63 contains a stray tab character (\t) between "bg-grey and flex in the class string.

Suggested cleanup
-	<!-- svelte-ignore a11y_click_events_have_key_events -->
-	<!-- svelte-ignore a11y_no_static_element_interactions -->
 	<Button
-		class="bg-grey 	flex aspect-square h-13 w-13 items-center justify-center rounded-full px-0 transition-opacity {isDisabled
+		class="bg-grey flex aspect-square h-13 w-13 items-center justify-center rounded-full px-0 transition-opacity {isDisabled
 			? 'cursor-not-allowed opacity-50'
 			: 'cursor-pointer hover:opacity-80'}"
 		callback={handleSubmit}

3-3: Inconsistent import style.

Button is imported directly from its file path while Avatar and Input on line 2 use the barrel export ($lib/ui). Consider importing Button from $lib/ui as well for consistency, unless there's a specific reason (e.g., circular dependency).

-	import Button from '$lib/ui/Button/Button.svelte';
+	import { Avatar, Button, Input } from '$lib/ui';

(and drop the separate Avatar/Input import on line 2.)

platforms/pictique/src/routes/(protected)/settings/account/username/+page.svelte (1)

54-55: Axios errors carry the message in a nested property.

err.message on an AxiosError yields a generic string like "Request failed with status code 413". If the server returns a meaningful error body, extract it with err.response?.data?.message (or equivalent) before falling back to err.message.

Suggested improvement
-			error = err instanceof Error ? err.message : 'Failed to save changes';
+			const axiosErr = err as any;
+			error = axiosErr?.response?.data?.message
+				?? (err instanceof Error ? err.message : 'Failed to save changes');
platforms/pictique/src/lib/fragments/CreatePostModal/CreatePostModal.svelte (1)

161-178: Progress bar hidden for the first image.

The {#if images.length > 1} guard means users won't see any size feedback until the second upload. Consider showing it when images.length > 0 so users know how much budget a single large image consumed.

platforms/pictique/src/lib/fragments/UploadedPostView/UploadedPostView.svelte (2)

7-20: width and height props are now unused.

These props are still declared in the interface (lines 9-10) and destructured with defaults (lines 16-17), but the template no longer references them — the grid uses fixed Tailwind classes. Remove them to avoid confusion, and update the component docs (lines 50-51) accordingly.

Suggested cleanup
 	interface IUploadedPostViewProps extends HTMLAttributes<HTMLElement> {
 		images: Image[];
-		width?: string;
-		height?: string;
 		callback: (i: number) => void;
 	}
 
 	let {
 		images,
-		width = 'w-full',
-		height = 'h-full',
 		callback,
 		...restProps
 	}: IUploadedPostViewProps = $props();

25-25: Simplify cn call.

Wrapping a .join(' ') result in an array is redundant — cn accepts multiple string arguments directly.

Simplification
-	class={cn(['grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4', restProps.class].join(' '))}
+	class={cn('grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4', restProps.class)}
platforms/pictique/src/routes/(protected)/post/+page.svelte (1)

51-56: revokeImageUrls is a no-op on data URLs.

Images here are read via FileReader.readAsDataURL, producing data: URLs. URL.revokeObjectURL only applies to blob URLs created by URL.createObjectURL. This call does nothing — it won't break, but it's misleading. Consider either switching to URL.createObjectURL for the preview (more memory-efficient) or removing the revoke call.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
platforms/pictique/src/routes/(protected)/post/+page.svelte (1)

60-68: ⚠️ Potential issue | 🔴 Critical

catch (error) shadows the component-level error state — submission failures are silently swallowed.

The catch parameter error on line 65 shadows let error = $state('') on line 14. The caught exception is only logged; the UI error banner (lines 72-76) will never display post-creation failures.

Proposed fix
-	} catch (error) {
-		console.error('Failed to create post:', error);
+	} catch (err) {
+		console.error('Failed to create post:', err);
+		error = err instanceof Error ? err.message : 'Failed to create post';
 	}
platforms/pictique/src/routes/(protected)/settings/account/username/+page.svelte (1)

132-132: ⚠️ Potential issue | 🟡 Minor

Typo: double space in placeholder text.

"Edit your public name" has two spaces between "Edit" and "your".

Proposed fix
-		<Input type="text" placeholder="Edit  your public name" bind:value={name} />
+		<Input type="text" placeholder="Edit your public name" bind:value={name} />
🤖 Fix all issues with AI agents
In `@platforms/pictique/src/lib/fragments/CreatePostModal/CreatePostModal.svelte`:
- Line 44: In CreatePostModal.svelte remove the debug statement that logs the
full base64 payload—delete the console.log(result) call (or replace it with a
safe metadata-only log such as filename/size/type) inside the image/attachment
handling code so you no longer dump full data URLs to the console; look for the
console.log(result) occurrence in the component's image processing function and
remove or replace it with a minimal log of non-sensitive metadata.

In
`@platforms/pictique/src/routes/`(protected)/settings/account/username/+page.svelte:
- Line 89: The UI helper text in +page.svelte incorrectly states "Maximum file
size: 1MB" while the actual limit is defined by MAX_FILE_SIZE (0.5 * 1024 *
1024). Update the displayed text to match the constant (e.g., "Maximum file
size: 500KB" or derive the value from MAX_FILE_SIZE) so the text and the
MAX_FILE_SIZE value are consistent; locate the string in +page.svelte and
replace it or format it from MAX_FILE_SIZE to ensure parity with the upload
limit.
🧹 Nitpick comments (6)
platforms/pictique/src/routes/(protected)/post/+page.svelte (3)

10-11: MAX_TOTAL_SIZE and MAX_FILE_SIZE are duplicated across files.

These same constants are defined identically in CreatePostModal.svelte. Consider exporting them from fileValidation.ts alongside the validation functions to keep a single source of truth.


84-89: revokeImageUrls is a no-op on data URLs.

Images in this flow are created via FileReader.readAsDataURL, producing data: URIs — not object URLs from URL.createObjectURL(). Calling revokeObjectURL on a data URL does nothing. Not harmful, but misleading.


78-133: Add Photo frame is always visible, even when total size limit is reached.

Unlike CreatePostModal which conditionally renders the frame with {#if remainingSize > 0}, this page always shows it. Users will see the frame, attempt an upload, and only then get an error. Consider hiding it when the budget is exhausted, consistent with CreatePostModal.

platforms/pictique/src/routes/(protected)/settings/account/username/+page.svelte (1)

30-38: Missing reader.onerror handler.

If FileReader.readAsDataURL fails, the error is silently swallowed. Other upload flows in this PR (e.g., CreatePostModal, post/+page.svelte) include reader.onerror to surface the failure. Add the same here for consistency.

Proposed fix
 			reader.onload = (e) => {
 				if (e.target?.result) {
 					profileImageDataUrl = e.target.result as string;
 				}
 			};
+			reader.onerror = () => {
+				error = 'Error reading image file';
+			};
 
 			reader.readAsDataURL(file);
platforms/pictique/src/lib/fragments/UploadedPostView/UploadedPostView.svelte (1)

7-20: width and height props are declared but no longer used.

The interface declares width and height (with defaults), and they're destructured from $props(), but the template now uses a grid layout that doesn't reference them. This is dead code that may mislead consumers of the component.

Proposed cleanup
 	interface IUploadedPostViewProps extends HTMLAttributes<HTMLElement> {
 		images: Image[];
-		width?: string;
-		height?: string;
 		callback: (i: number) => void;
 	}

 	let {
 		images,
-		width = 'w-full',
-		height = 'h-full',
 		callback,
 		...restProps
 	}: IUploadedPostViewProps = $props();

Also update the documentation block (lines 50–51) to remove references to width and height.

platforms/pictique/src/lib/fragments/CreatePostModal/CreatePostModal.svelte (1)

63-80: No isSubmitting guard at the top of handleSubmit.

The disabled prop on the Post button (line 184) doesn't account for isSubmitting, so rapid clicks before state propagates could trigger concurrent submissions. Add an early return, consistent with saveProfileData in the settings page.

Proposed fix
 	const handleSubmit = async () => {
+		if (isSubmitting) return;
 		if (!text.trim() && uploadItems.length === 0) return;

And also disable the button during submission:

-			disabled={!text.trim() && uploadItems.length === 0}
+			disabled={isSubmitting || (!text.trim() && uploadItems.length === 0)}

Comment on lines +18 to +19
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB total
const MAX_FILE_SIZE = 1 * 1024 * 1024; // 1MB per individual image
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah well, we don't really want to enforce these that hard

@coodos coodos merged commit 4731cc5 into main Feb 6, 2026
4 checks passed
@coodos coodos deleted the fix/pictique-issues branch February 6, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Pictique issues

2 participants