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
2 changes: 1 addition & 1 deletion src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export const App = () => {
<Header />
<Pages />
<Footer />
<Toaster />
</div>
<Toaster />
</RouterProvider>
</ThemeProvider>
);
Expand Down
2 changes: 1 addition & 1 deletion src/entities/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class UserEntity {
}

getPersonalStyle() {
const hue = hash(this.displayName + ' ' + this.username);
const hue = hash([this.displayName, this.username].join('..'));
const color = `hsl(${hue}deg 90% 40%)`;
return {hue, color};
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/inline-avatars/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
}

.avatar {
outline: 2px solid var(--background);
outline: 3px solid var(--background);
overflow: hidden;

&:not(:first-child) {
Expand Down
8 changes: 3 additions & 5 deletions src/features/profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,11 @@ export const Profile = memo((props: ProfileProps) => {
{...otherProps}
>
<Avatar user={user} className={cn(variant === 'xs' && 'w-4 h-4')} />
<div className={cn('flex gap-1.5', variant === 'md' && 'flex-col')}>
<p className="text-sm font-medium leading-none">
<div className={cn('grid gap-1.5', variant === 'md' && 'flex-col')}>
<p className="text-sm font-medium leading-none -m-1 p-1 truncate">
{user.displayName}
{variant === 'xs' && (
<span className="ml-2 font-normal text-sm opacity-50 leading-none">
{user.username}
</span>
<span className="ml-2 font-normal text-muted-foreground">{user.username}</span>
)}
{isTopicStarter && (
<span className="ml-2 text-muted-foreground font-normal">
Expand Down
11 changes: 1 addition & 10 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,5 @@ export const getTimeSince = (timestamp: string | Date): string => {
};

export const hash = (input: string) => {
let hash = 0,
i,
chr;
if (input.length === 0) return hash;
for (i = 0; i < input.length; i++) {
chr = input.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0;
}
return hash;
return input.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
};
21 changes: 1 addition & 20 deletions src/pages/edit-profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import {useForm} from 'effector-forms';
import {useUnit} from 'effector-react';

import {Profile} from '~/features/profile';

import {UserEntity} from '~/entities/types';

import {Container, ContainerSize, CustomButton, Input, Page, Text, TextSize} from '~/shared/ui';

import {$form, avatarFileChanged} from './model';
import {$form} from './model';
import cls from './page.module.scss';

export const EditProfilePage = () => {
const {fields, eachValid, submit} = useForm($form);
const onAvatarFileChanged = useUnit(avatarFileChanged);

return (
<Page>
<Container size={ContainerSize.M}>
Expand All @@ -23,54 +20,38 @@ export const EditProfilePage = () => {

<form className={cls.grid} onSubmit={() => submit()}>
<Input
label="Display name"
placeholder="Display name"
value={fields.displayName?.value}
onChange={(e) => fields.displayName?.onChange(e.target.value)}
isInvalid={fields.displayName?.hasError()}
errorMessage={fields.displayName?.errorText()}
/>
<Input
label="Username"
placeholder="Username"
value={fields.username?.value}
onChange={(e) => fields.username?.onChange(e.target.value)}
isInvalid={fields.username?.hasError()}
errorMessage={fields.username?.errorText()}
/>

<Input
label="Current password"
placeholder="Current password"
value={fields.password?.value}
onChange={(e) => fields.password?.onChange(e.target.value)}
isInvalid={fields.password?.hasError()}
errorMessage={fields.password?.errorText()}
type="password"
name="password"
/>
<Input
label="New password"
placeholder="New password"
value={fields.newPassword?.value}
onChange={(e) => fields.newPassword?.onChange(e.target.value)}
isInvalid={fields.newPassword?.hasError()}
errorMessage={fields.newPassword?.errorText()}
type="password"
name="new_password"
/>

<Input
label="Avatar URL"
placeholder="Avatar URL"
readOnly={true}
value={fields.avatarUrl?.value}
isInvalid={fields.avatarUrl?.hasError()}
errorMessage={fields.avatarUrl?.errorText()}
type="file"
accept="image/png, image/jpeg"
name="avatar_url"
onFileChanged={onAvatarFileChanged}
/>
<div className={cls.row}>
<Profile
Expand Down
13 changes: 9 additions & 4 deletions src/pages/explore/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import {$discussions, $totalCount} from './model';
export const ExplorePage = () => {
const [discussions, totalCount] = useUnit([$discussions, $totalCount]);
return (
<Page className="mx-auto w-full pt-16 pb-48">
<Page className="mx-auto w-full">
<div className="flex-1 w-full mt-6 sm:max-w-xl md:max-w-3xl">
<div className="flex w-full mb-6 justify-between items-center px-6 sm:px-0 sm:ps-2">
<div className="flex w-full mb-6 justify-between items-center px-6 sm:pe-0 sm:ps-2">
{discussions === 'loading' ? (
<Skeleton className="w-24 h-3" />
) : (
Expand All @@ -30,9 +30,14 @@ export const ExplorePage = () => {
</Button>
</CreateDiscussionDialog>
</div>
<div className="sm:space-y-6 sm:mb-6">
<div className="sm:space-y-6">
{discussions === 'loading' &&
[...Array(5).keys()].map((i) => <DiscussionSkeleton key={i} />)}
[...Array(5).keys()].map((i) => (
<DiscussionSkeleton
key={i}
className="rounded-none sm:rounded-xl border-x-0 border-b-0 border-t sm:border"
/>
))}
{discussions !== 'loading' &&
discussions.map((discussion) => (
<Discussion
Expand Down
7 changes: 4 additions & 3 deletions src/pages/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import Fresco from './assets/fresco.webp';
export const HomePage = () => {
const repos = useUnit($repos);
return (
<Page className="pt-16">
<Page>
<div className="fixed top-0 z-999 h-[100vh] w-[100vw] mix-blend-screen pointer-events-none"></div>
<div className="min-h-screen w-full px-10 sm:px-0 sm:max-w-xl lg:max-w-2xl">
<Link to="/explore" className="block w-fit mx-auto mt-16 mb-12">
<motion.div
Expand All @@ -33,7 +34,7 @@ export const HomePage = () => {
}}
viewport={{once: true}}
>
<Button className="rounded-full bg-primary group gap-0">
<Button className="rounded-full bg-foreground group gap-0">
Discussions
<ArrowRight className="w-0! group-hover:w-4! group-hover:ms-2 pt-px transition-all" />
</Button>
Expand Down Expand Up @@ -67,7 +68,7 @@ export const HomePage = () => {
<a href={repo.html_url} target="_blank" key={repo.id} className="w-full">
<Card className="bg-background">
<CardHeader>
<div className="flex justify-between items-center text-sm whitespace-nowrap">
<div className="md:flex space-y-4 md:space-y-0 justify-between items-center text-sm whitespace-nowrap">
<Profile
user={
new UserEntity({
Expand Down
3 changes: 3 additions & 0 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {createRoutesView} from 'atomic-router-react';

import {ProfilesRoute} from '~/pages/profiles';

import {DiscussionRoute} from './discussion';
import {EditProfileRoute} from './edit-profile';
import {ExploreRoute} from './explore';
Expand All @@ -15,6 +17,7 @@ export const Pages = createRoutesView({
HomeRoute,
SignInRoute,
SignUpRoute,
ProfilesRoute,
ExploreRoute,
ProfileRoute,
EditProfileRoute,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/profile/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const dataLoadedRoute = chainRoute({
mapParams: ({params}) => {
return {
query: {
username: params.username,
username: decodeURIComponent(params.username),
},
};
},
Expand Down
7 changes: 7 additions & 0 deletions src/pages/profiles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {currentRoute} from '~/pages/profiles/model';
import {ProfilesPage} from '~/pages/profiles/page';

export const ProfilesRoute = {
view: ProfilesPage,
route: currentRoute,
};
36 changes: 36 additions & 0 deletions src/pages/profiles/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {attach, createStore, sample} from 'effector';
import {spread} from 'patronum';

import {UserEntity} from '~/entities/types';

import {apiV1UsersGetFx} from '~/shared/api';
import {routes} from '~/shared/routes';

export const currentRoute = routes.profiles;

export const $users = createStore<UserEntity[] | 'loading'>('loading');
export const $totalCount = createStore(0);

const loadUsersFx = attach({
effect: apiV1UsersGetFx,
mapParams: () => ({query: {offset: 0, count: 100}}),
});

sample({
clock: currentRoute.opened,
target: loadUsersFx,
});

sample({
clock: loadUsersFx.doneData,
fn: ({answer}) => ({
users: (answer.items as any[]).map((item) => UserEntity.fromResponse(item)!),
totalCount: answer.total_count!,
}),
target: spread({
targets: {
users: $users,
totalCount: $totalCount,
},
}),
});
32 changes: 32 additions & 0 deletions src/pages/profiles/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {useUnit} from 'effector-react';
import {cn} from '~/lib/utils';

import {Profile, ProfileSkeleton} from '~/features/profile';

import {Page} from '~/shared/ui';
import {Skeleton} from '~/shared/ui/skeleton';

import * as model from './model';

export const ProfilesPage = () => {
const [users, totalCount] = useUnit([model.$users, model.$totalCount]);
return (
<Page>
<div className="flex-1 w-full mt-6 sm:max-w-xl md:max-w-3xl">
<div className="flex w-full h-9 mb-6 justify-between items-center px-6 sm:pe-0 sm:ps-2">
{users === 'loading' ? <Skeleton className="w-16 h-3" /> : <p>{totalCount} users</p>}
</div>
<div
className={cn(
'grid sm:grid-cols-2 md:grid-cols-3 px-3 sm:px-0 gap-3 w-full sm:max-w-xl md:max-w-3xl',
'[&>*]:w-full [&>*]:p-3 [&>*]:rounded-xl [&>*]:border',
)}
>
{users === 'loading' && [...Array(50).keys()].map((i) => <ProfileSkeleton key={i} />)}
{users !== 'loading' &&
users.map((user) => <Profile key={user.id} variant={'md'} user={user} />)}
</div>
</div>
</Page>
);
};
Loading
Loading