Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
59e9812
fix: header logic in secondary
Sahil2004 Jun 5, 2025
9e0f554
fix: fixed the text in header in post
Sahil2004 Jun 5, 2025
7ac4cff
feat: trying some hack to get file image input.
Sahil2004 Jun 5, 2025
2a0baf1
feat: added image input on clicking the post bottom nav
Sahil2004 Jun 5, 2025
5856dcb
chore: got rid of non-required code.
Sahil2004 Jun 5, 2025
4e9a762
feat: added the logic to get the images from user on clicking post tab.
Sahil2004 Jun 5, 2025
468db66
feat: added store.
Sahil2004 Jun 6, 2025
f1e5ad4
feat: added correct conversion of files.
Sahil2004 Jun 6, 2025
78e648d
feat: added the correct display of image when uploading.
Sahil2004 Jun 6, 2025
cdfb4d4
feat: added settings tile to the post page and fixed the settingsTile…
Sahil2004 Jun 6, 2025
ae3ba2d
feat: added hte correct header for the audience page.
Sahil2004 Jun 6, 2025
89ac7ed
fix: fixed the page transition not happening to audience page.
Sahil2004 Jun 6, 2025
2a1eb67
feat: added audience setting
Sahil2004 Jun 6, 2025
16046c9
feat: added store to audience.
Sahil2004 Jun 6, 2025
bb019ac
chore: removed console log
Sahil2004 Jun 6, 2025
7ed4214
feat: added post button.
Sahil2004 Jun 6, 2025
74b859c
feat: correct button placement
Sahil2004 Jun 6, 2025
dabf6b9
Merge branch 'main' into feat/mobile-upload-flow
Sahil2004 Jun 6, 2025
73841de
fix: horizontal scroll
Sahil2004 Jun 7, 2025
9720ae8
fix: positioning of the post button.
Sahil2004 Jun 7, 2025
8c32220
fix: protecting post route when no image is selected.
Sahil2004 Jun 7, 2025
7f3a1a5
fix: improved type saftey
Sahil2004 Jun 7, 2025
e659343
feat: added memory helper function
Sahil2004 Jun 7, 2025
18e135d
feat: added memory cleanup.
Sahil2004 Jun 7, 2025
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
28 changes: 25 additions & 3 deletions platforms/metagram/src/lib/fragments/BottomNav/BottomNav.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { Home, CommentsTwo, Search, Camera } from '$lib/icons';
import { goto } from '$app/navigation';
import { isNavigatingThroughNav, ownerId } from '$lib/store/store.svelte';
import { page } from '$app/state';
import { Camera, CommentsTwo, Home, Search } from '$lib/icons';
import { isNavigatingThroughNav, ownerId } from '$lib/store/store.svelte';
import { uploadedImages } from '$lib/store/store.svelte';
import { revokeImageUrls } from '$lib/utils';
import type { HTMLAttributes } from 'svelte/elements';

interface IBottomNavProps extends HTMLAttributes<HTMLElement> {
activeTab?: string;
Expand All @@ -19,6 +21,9 @@
let _activeTab = $derived(page.url.pathname);
let fullPath = $derived(page.url.pathname);

let imageInput: HTMLInputElement;
let images: FileList | null = $state(null);

const handleNavClick = (newTab: string) => {
// activeTab = newTab;
isNavigatingThroughNav.value = true;
Expand All @@ -29,16 +34,33 @@
previousTab = newTab;
if (newTab === 'profile') {
goto(`/profile/${ownerId}`);
} else if (newTab === "post") {
uploadedImages.value = null;
imageInput.value = "";
imageInput.click();
} else {
goto(`/${newTab}`);
}
};

$effect(() => {
activeTab = _activeTab.split('/').pop() ?? '';
if (images && images.length > 0 && activeTab !== 'post' && previousTab === 'post' && !_activeTab.includes('post/audience')) {
if (uploadedImages.value)
revokeImageUrls(uploadedImages.value);
uploadedImages.value = Array.from(images).map(file => ({
url: URL.createObjectURL(file),
alt: file.name
}));
images = null; // To prevent re-triggering the effect and thus making an infinite loop with /post route's effect when the length of uploadedImages goes to 0
if (uploadedImages.value.length > 0) {
goto("/post");
}
}
});
</script>

<input type="file" accept="image/*" multiple bind:files={images} bind:this={imageInput} class="hidden" />
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
<nav
aria-label="Main navigation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
>
<HugeiconsIcon icon={menuButton[variant]} size={24} color="var(--color-black-500)" />
</button>
{:else if variant === 'secondary' && options}
{:else if variant === 'secondary' && options && isCallBackNeeded}
<ActionMenu {options} />
{/if}
</header>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

interface ISettingsTile extends HTMLButtonAttributes {
title: string;
currentStatus: 'string';
currentStatus: string;
}

const { title, currentStatus, ...restProps }: ISettingsTile = $props();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
<script lang="ts">
import { Cross } from '$lib/icons';
import type { Image } from '$lib/types';
import { cn } from '$lib/utils';
import type { HTMLAttributes } from 'svelte/elements';

interface IImage {
url: string;
alt: string;
}

interface IUploadedPostViewProps extends HTMLAttributes<HTMLElement> {
images: IImage[];
images: Image[];
width?: string;
height?: string;
callback: (i: number) => void;
Expand All @@ -26,14 +23,12 @@

<article
{...restProps}
class={cn(
['flex max-w-screen flex-row items-center gap-4 scroll-auto', restProps.class].join(' ')
)}
class={cn(['pl-0.5 pr-4 max-w-screen flex flex-row items-center gap-4 overflow-x-auto min-h-max', restProps.class].join(' '))}
>
{#each images as image, i}
<div class={cn(['group relative shrink-0'])}>
<div class={cn(['group relative shrink-0 mt-3 mb-2'])}>
<Cross
class="absolute top-0 right-0 hidden translate-x-1/2 -translate-y-1/2 cursor-pointer group-hover:block"
class="absolute right-0 top-0 hidden -translate-y-1/2 translate-x-1/2 cursor-pointer group-hover:block"
onclick={() => callback(i)}
/>
<img
Expand Down
18 changes: 13 additions & 5 deletions platforms/metagram/src/lib/store/store.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import type { PostData } from '$lib/types';
import type { Image, PostData } from "$lib/types";

export const isNavigatingThroughNav = $state({
value: false
value: false,
});

export const showComments = $state({
value: false
value: false,
});

export const ownerId = $state({
value: '1'
value: "1",
});

export const selectedPost: { value: PostData | null } = $state({
value: null
value: null,
});

export const uploadedImages: { value: Image[] | null } = $state({
value: null,
});

export const audience: { value: string } = $state({
value: "Everyone",
});
67 changes: 36 additions & 31 deletions platforms/metagram/src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,48 @@
import type { SVGAttributes } from 'svelte/elements';
import type { SVGAttributes } from "svelte/elements";

export interface ISvgProps extends SVGAttributes<SVGElement> {
size?: number | string;
color?: string;
size?: number | string;
color?: string;
}

export type CommentType = {
commentId: string;
name: string;
userImgSrc: string;
comment: string;
isUpVoted: boolean;
isDownVoted: boolean;
upVotes: number;
time: string;
replies: CommentType[];
commentId: string;
name: string;
userImgSrc: string;
comment: string;
isUpVoted: boolean;
isDownVoted: boolean;
upVotes: number;
time: string;
replies: CommentType[];
};

export type PostData = {
id: string;
avatar: string;
userId: string;
username: string;
imgUris: string[];
caption: string;
time: string;
count: {
likes: number;
comments: number;
};
id: string;
avatar: string;
userId: string;
username: string;
imgUris: string[];
caption: string;
time: string;
count: {
likes: number;
comments: number;
};
};

export type userProfile = {
userId: string;
username: string;
avatar: string;
totalPosts: number;
followers: number;
following: number;
userBio: string;
posts: PostData[];
userId: string;
username: string;
avatar: string;
totalPosts: number;
followers: number;
following: number;
userBio: string;
posts: PostData[];
};

export type Image = {
url: string;
alt: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
{value}
bind:group={selected}
bind:this={radioElement}
id={value}
{name}
checked={selected === value}
class={cn(['hidden', restProps.class].join(' '))}
Expand Down
5 changes: 3 additions & 2 deletions platforms/metagram/src/lib/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './mergeClasses';
export * from './clickOutside';
export * from "./mergeClasses";
export * from "./clickOutside";
export * from "./memoryHelper";
5 changes: 5 additions & 0 deletions platforms/metagram/src/lib/utils/memoryHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { Image } from "$lib/types";

export const revokeImageUrls = (imageArray: Image[]) => {
imageArray?.forEach((img) => URL.revokeObjectURL(img.url));
};
8 changes: 5 additions & 3 deletions platforms/metagram/src/routes/(protected)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { page } from '$app/state';
import { comments } from '$lib/dummyData';
import { BottomNav, Header, Comment, MessageInput, SideBar } from '$lib/fragments';
import { BottomNav, Comment, Header, MessageInput, SideBar } from '$lib/fragments';
import UserRequest from '$lib/fragments/UserRequest/UserRequest.svelte';
import { showComments } from '$lib/store/store.svelte';
import type { CommentType } from '$lib/types';
Expand Down Expand Up @@ -58,8 +58,10 @@
heading = 'Feed';
} else if (route.includes('discover')) {
heading = 'Search';
} else if (route.includes('/post/audience')) {
heading = 'Audience';
} else if (route.includes('post')) {
heading = 'Post';
heading = 'Upload photo';
} else if (route === `/messages/${idFromParams}`) {
heading = 'User Name';
} else if (route.includes('messages')) {
Expand All @@ -86,7 +88,7 @@
</button>
{:else}
<Header
variant={route === `/messages/${idFromParams}`
variant={route === `/messages/${idFromParams}` || route.includes("/post")
? 'secondary'
: route.includes('profile')
? 'tertiary'
Expand Down
43 changes: 43 additions & 0 deletions platforms/metagram/src/routes/(protected)/post/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { SettingsTile, UploadedPostView } from '$lib/fragments';
import { audience, uploadedImages } from '$lib/store/store.svelte';
import { Button, Textarea } from '$lib/ui';
import { revokeImageUrls } from '$lib/utils';

let caption: string = $state('');

$effect(() => {
if (!uploadedImages.value || uploadedImages.value.length === 0) {
window.history.back();
}
})
Comment on lines +10 to +14
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential navigation issue with history.back().

Using window.history.back() can cause unpredictable behavior if the user didn't navigate from within the app (e.g., direct URL access, page refresh).

-window.history.back();
+goto('/home');

This ensures users are redirected to a known safe route instead of relying on browser history.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In platforms/metagram/src/routes/(protected)/post/+page.svelte around lines 9 to
13, replace the use of window.history.back() with a programmatic navigation to a
specific safe route within the app (e.g., the home page or a dashboard). This
avoids unpredictable behavior when the user accesses the page directly or
refreshes, ensuring consistent and controlled redirection.

</script>

<section class="h-fit w-full flex flex-col gap-3 justify-stretch">
<UploadedPostView
images={uploadedImages.value ?? []}
width="w-auto"
height="h-40"
callback={(i: number) => {
if (uploadedImages.value)
uploadedImages.value = uploadedImages.value.filter((img, index) => {
revokeImageUrls([img]);
return index !== i
});
}}
/>

<label for="caption">
<span class="block mb-2 font-semibold">Add a caption</span>
<Textarea name="caption" rows={3} bind:value={caption} placeholder="Hey guys..." />
</label>

<SettingsTile
title="Who can see your posts?"
currentStatus={audience.value}
onclick={() => goto('/post/audience')}
/>

<Button variant="secondary" callback={() => alert('TODO: Post created!')} class="mt-1">Post</Button>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script lang="ts">
import { audience } from '$lib/store/store.svelte';
import { InputRadio } from '$lib/ui';
</script>

<h1 class="mb-4">Who can see your posts?</h1>
<span class="flex flex-col gap-4">
<label for="Everyone" class="flex items-center justify-between">
Everyone
<InputRadio value="Everyone" name="audience" bind:selected={audience.value} />
</label>
<label for="Only Followers" class="flex items-center justify-between">
Only Followers
<InputRadio value="Only Followers" name="audience" bind:selected={audience.value} />
</label>
<label for="No one" class="flex items-center justify-between">
No one
<InputRadio value="No one" name="audience" bind:selected={audience.value} />
</label>
</span>
Loading