Skip to content

Commit f88d3d5

Browse files
authored
Feat/mobile upload flow (#193)
* fix: header logic in secondary * fix: fixed the text in header in post * feat: trying some hack to get file image input. * feat: added image input on clicking the post bottom nav * chore: got rid of non-required code. * feat: added the logic to get the images from user on clicking post tab. * feat: added store. * feat: added correct conversion of files. * feat: added the correct display of image when uploading. * feat: added settings tile to the post page and fixed the settingsTile component type of currentStatus * feat: added hte correct header for the audience page. * fix: fixed the page transition not happening to audience page. * feat: added audience setting * feat: added store to audience. * chore: removed console log * feat: added post button. * feat: correct button placement * fix: horizontal scroll * fix: positioning of the post button. * fix: protecting post route when no image is selected. * fix: improved type saftey * feat: added memory helper function * feat: added memory cleanup.
1 parent 486ffc2 commit f88d3d5

File tree

12 files changed

+158
-56
lines changed

12 files changed

+158
-56
lines changed

platforms/metagram/src/lib/fragments/BottomNav/BottomNav.svelte

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script lang="ts">
2-
import type { HTMLAttributes } from 'svelte/elements';
3-
import { Home, CommentsTwo, Search, Camera } from '$lib/icons';
42
import { goto } from '$app/navigation';
5-
import { isNavigatingThroughNav, ownerId } from '$lib/store/store.svelte';
63
import { page } from '$app/state';
4+
import { Camera, CommentsTwo, Home, Search } from '$lib/icons';
5+
import { isNavigatingThroughNav, ownerId } from '$lib/store/store.svelte';
6+
import { uploadedImages } from '$lib/store/store.svelte';
7+
import { revokeImageUrls } from '$lib/utils';
8+
import type { HTMLAttributes } from 'svelte/elements';
79
810
interface IBottomNavProps extends HTMLAttributes<HTMLElement> {
911
activeTab?: string;
@@ -19,6 +21,9 @@
1921
let _activeTab = $derived(page.url.pathname);
2022
let fullPath = $derived(page.url.pathname);
2123
24+
let imageInput: HTMLInputElement;
25+
let images: FileList | null = $state(null);
26+
2227
const handleNavClick = (newTab: string) => {
2328
// activeTab = newTab;
2429
isNavigatingThroughNav.value = true;
@@ -29,16 +34,33 @@
2934
previousTab = newTab;
3035
if (newTab === 'profile') {
3136
goto(`/profile/${ownerId}`);
37+
} else if (newTab === "post") {
38+
uploadedImages.value = null;
39+
imageInput.value = "";
40+
imageInput.click();
3241
} else {
3342
goto(`/${newTab}`);
3443
}
3544
};
3645
3746
$effect(() => {
3847
activeTab = _activeTab.split('/').pop() ?? '';
48+
if (images && images.length > 0 && activeTab !== 'post' && previousTab === 'post' && !_activeTab.includes('post/audience')) {
49+
if (uploadedImages.value)
50+
revokeImageUrls(uploadedImages.value);
51+
uploadedImages.value = Array.from(images).map(file => ({
52+
url: URL.createObjectURL(file),
53+
alt: file.name
54+
}));
55+
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
56+
if (uploadedImages.value.length > 0) {
57+
goto("/post");
58+
}
59+
}
3960
});
4061
</script>
4162

63+
<input type="file" accept="image/*" multiple bind:files={images} bind:this={imageInput} class="hidden" />
4264
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
4365
<nav
4466
aria-label="Main navigation"

platforms/metagram/src/lib/fragments/Header/Header.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
>
9191
<HugeiconsIcon icon={menuButton[variant]} size={24} color="var(--color-black-500)" />
9292
</button>
93-
{:else if variant === 'secondary' && options}
93+
{:else if variant === 'secondary' && options && isCallBackNeeded}
9494
<ActionMenu {options} />
9595
{/if}
9696
</header>

platforms/metagram/src/lib/fragments/SettingsTile/SettingsTile.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
77
interface ISettingsTile extends HTMLButtonAttributes {
88
title: string;
9-
currentStatus: 'string';
9+
currentStatus: string;
1010
}
1111
1212
const { title, currentStatus, ...restProps }: ISettingsTile = $props();

platforms/metagram/src/lib/fragments/UploadedPostView/UploadedPostView.svelte

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
<script lang="ts">
22
import { Cross } from '$lib/icons';
3+
import type { Image } from '$lib/types';
34
import { cn } from '$lib/utils';
45
import type { HTMLAttributes } from 'svelte/elements';
56
6-
interface IImage {
7-
url: string;
8-
alt: string;
9-
}
107
118
interface IUploadedPostViewProps extends HTMLAttributes<HTMLElement> {
12-
images: IImage[];
9+
images: Image[];
1310
width?: string;
1411
height?: string;
1512
callback: (i: number) => void;
@@ -26,14 +23,12 @@
2623

2724
<article
2825
{...restProps}
29-
class={cn(
30-
['flex max-w-screen flex-row items-center gap-4 scroll-auto', restProps.class].join(' ')
31-
)}
26+
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(' '))}
3227
>
3328
{#each images as image, i}
34-
<div class={cn(['group relative shrink-0'])}>
29+
<div class={cn(['group relative shrink-0 mt-3 mb-2'])}>
3530
<Cross
36-
class="absolute top-0 right-0 hidden translate-x-1/2 -translate-y-1/2 cursor-pointer group-hover:block"
31+
class="absolute right-0 top-0 hidden -translate-y-1/2 translate-x-1/2 cursor-pointer group-hover:block"
3732
onclick={() => callback(i)}
3833
/>
3934
<img
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
import type { PostData } from '$lib/types';
1+
import type { Image, PostData } from "$lib/types";
22

33
export const isNavigatingThroughNav = $state({
4-
value: false
4+
value: false,
55
});
66

77
export const showComments = $state({
8-
value: false
8+
value: false,
99
});
1010

1111
export const ownerId = $state({
12-
value: '1'
12+
value: "1",
1313
});
1414

1515
export const selectedPost: { value: PostData | null } = $state({
16-
value: null
16+
value: null,
17+
});
18+
19+
export const uploadedImages: { value: Image[] | null } = $state({
20+
value: null,
21+
});
22+
23+
export const audience: { value: string } = $state({
24+
value: "Everyone",
1725
});
Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,48 @@
1-
import type { SVGAttributes } from 'svelte/elements';
1+
import type { SVGAttributes } from "svelte/elements";
22

33
export interface ISvgProps extends SVGAttributes<SVGElement> {
4-
size?: number | string;
5-
color?: string;
4+
size?: number | string;
5+
color?: string;
66
}
77

88
export type CommentType = {
9-
commentId: string;
10-
name: string;
11-
userImgSrc: string;
12-
comment: string;
13-
isUpVoted: boolean;
14-
isDownVoted: boolean;
15-
upVotes: number;
16-
time: string;
17-
replies: CommentType[];
9+
commentId: string;
10+
name: string;
11+
userImgSrc: string;
12+
comment: string;
13+
isUpVoted: boolean;
14+
isDownVoted: boolean;
15+
upVotes: number;
16+
time: string;
17+
replies: CommentType[];
1818
};
1919

2020
export type PostData = {
21-
id: string;
22-
avatar: string;
23-
userId: string;
24-
username: string;
25-
imgUris: string[];
26-
caption: string;
27-
time: string;
28-
count: {
29-
likes: number;
30-
comments: number;
31-
};
21+
id: string;
22+
avatar: string;
23+
userId: string;
24+
username: string;
25+
imgUris: string[];
26+
caption: string;
27+
time: string;
28+
count: {
29+
likes: number;
30+
comments: number;
31+
};
3232
};
3333

3434
export type userProfile = {
35-
userId: string;
36-
username: string;
37-
avatar: string;
38-
totalPosts: number;
39-
followers: number;
40-
following: number;
41-
userBio: string;
42-
posts: PostData[];
35+
userId: string;
36+
username: string;
37+
avatar: string;
38+
totalPosts: number;
39+
followers: number;
40+
following: number;
41+
userBio: string;
42+
posts: PostData[];
43+
};
44+
45+
export type Image = {
46+
url: string;
47+
alt: string;
4348
};

platforms/metagram/src/lib/ui/InputRadio/InputRadio.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
{value}
2828
bind:group={selected}
2929
bind:this={radioElement}
30+
id={value}
3031
{name}
3132
checked={selected === value}
3233
class={cn(['hidden', restProps.class].join(' '))}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
export * from './mergeClasses';
2-
export * from './clickOutside';
1+
export * from "./mergeClasses";
2+
export * from "./clickOutside";
3+
export * from "./memoryHelper";
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { Image } from "$lib/types";
2+
3+
export const revokeImageUrls = (imageArray: Image[]) => {
4+
imageArray?.forEach((img) => URL.revokeObjectURL(img.url));
5+
};

platforms/metagram/src/routes/(protected)/+layout.svelte

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { page } from '$app/state';
33
import { comments } from '$lib/dummyData';
4-
import { BottomNav, Header, Comment, MessageInput, SideBar } from '$lib/fragments';
4+
import { BottomNav, Comment, Header, MessageInput, SideBar } from '$lib/fragments';
55
import UserRequest from '$lib/fragments/UserRequest/UserRequest.svelte';
66
import { showComments } from '$lib/store/store.svelte';
77
import type { CommentType } from '$lib/types';
@@ -58,8 +58,10 @@
5858
heading = 'Feed';
5959
} else if (route.includes('discover')) {
6060
heading = 'Search';
61+
} else if (route.includes('/post/audience')) {
62+
heading = 'Audience';
6163
} else if (route.includes('post')) {
62-
heading = 'Post';
64+
heading = 'Upload photo';
6365
} else if (route === `/messages/${idFromParams}`) {
6466
heading = 'User Name';
6567
} else if (route.includes('messages')) {
@@ -86,7 +88,7 @@
8688
</button>
8789
{:else}
8890
<Header
89-
variant={route === `/messages/${idFromParams}`
91+
variant={route === `/messages/${idFromParams}` || route.includes("/post")
9092
? 'secondary'
9193
: route.includes('profile')
9294
? 'tertiary'

0 commit comments

Comments
 (0)