Skip to content

Commit 1bbc88f

Browse files
committed
svg logo; sync score change
1 parent 4eb17a6 commit 1bbc88f

File tree

14 files changed

+168
-55
lines changed

14 files changed

+168
-55
lines changed

assets/stackoverflow-logo.svg

Lines changed: 12 additions & 0 deletions
Loading

electron/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function createWindows() {
2525
mainWindow = new BrowserWindow({
2626
// icon: path.join(assetsPath, 'assets', 'icon.png'),
2727
width: 1000,
28-
height: 600,
28+
height: 700,
2929
minWidth: 750,
3030
minHeight: 400,
3131
show: false,

src/@types/assets.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ declare module '*.png';
33
declare module '*.jpeg';
44
declare module '*.gif';
55
declare module '*.ogg';
6+
declare module '*.svg';

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function App() {
2727

2828
stackoverflow
2929
.get(`questions/${questionId}`, {
30-
filter: '!9MyMg2qFPpNbuLMPVtF3UyZX-N4MWSjZwlQ(VqCZ3LoiM_GpZITfZz5'
30+
filter: '!9MyMg2qFPpNbuLMPVtF3UyZX-N4MWSjZwlQ(VqCZ)UMRPTuNScvYNba'
3131
})
3232
.then((response) => {
3333
const question = (response as any).items[0];

src/components/layout/Layout.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Box, DarkMode, Divider, Flex, Image, Stack, Text } from '@chakra-ui/react';
2-
import Logo from '../../../assets/stackoverflow-logo.png';
2+
import Logo from '../../../assets/stackoverflow-logo.svg';
33
import { NavItem } from './NavItem';
44
import { Route, Routes } from 'react-router-dom';
55
import { QuestionsPage } from '../../pages/QuestionsPage';
@@ -18,12 +18,12 @@ import { TagsPage } from '../../pages/TagsPage';
1818
import { MyTagsPage } from '../../pages/MyTags';
1919
import { UserContext } from '../../contexts/use-user';
2020
import { MyAnswersPage } from '../../pages/MyAnswersPage';
21-
import { SettingsPage } from "../../pages/SettingsPage";
22-
import { SettingsAdvancedPage } from "../../pages/settings/SettingsAdvancedPage";
23-
import { SettingsAppearancePage } from "../../pages/settings/SettingsAppearancePage";
24-
import { SettingsNotificationsPage } from "../../pages/settings/SettingsNotificationsPage";
25-
import { SettingsHotkeysPage } from "../../pages/settings/SettingsHotkeysPage";
26-
import { SettingsAccessibilityPage } from "../../pages/settings/SettingsAccessibilityPage";
21+
import { SettingsPage } from '../../pages/SettingsPage';
22+
import { SettingsAdvancedPage } from '../../pages/settings/SettingsAdvancedPage';
23+
import { SettingsAppearancePage } from '../../pages/settings/SettingsAppearancePage';
24+
import { SettingsNotificationsPage } from '../../pages/settings/SettingsNotificationsPage';
25+
import { SettingsHotkeysPage } from '../../pages/settings/SettingsHotkeysPage';
26+
import { SettingsAccessibilityPage } from '../../pages/settings/SettingsAccessibilityPage';
2727

2828
export function Layout() {
2929
return (
@@ -41,7 +41,7 @@ export function Layout() {
4141
>
4242
<DarkMode>
4343
<Box>
44-
<Image mt="8px" mb="16px" ml="10px" src={Logo} h="20px" />
44+
<Image mt="8px" mb="16px" ml="10px" src={Logo} h="25px" />
4545
<Stack spacing={0}>
4646
<NavItem to="/">
4747
<RiEarthFill />

src/components/layout/NavItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function NavItem({ children, count, to }: Props) {
1313
// const isNested = countInString('/', location.pathname) > 1;
1414
const resolved = useResolvedPath(to);
1515
const match = useMatch({ path: resolved.pathname, end: true });
16-
// || (isNested && to === prevPathname);
16+
// || (isNested && to === prevPathname);
1717

1818
const hoverStyles = {
1919
color: match ? 'whiteAlpha.900' : 'whiteAlpha.800',

src/components/layout/ReputationDropdown.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,10 @@ export function ReputationDropdown() {
4444
fetchReputationHistory();
4545
}, []);
4646

47-
useEffect(() => {
48-
if (isOpen) {
49-
setIsUnseen(false);
50-
}
51-
}, [isOpen]);
52-
5347
async function fetchReputationHistory() {
54-
const response: any = await stackoverflow.get('me/reputation-history/full');
48+
const response: any = await stackoverflow.get('me/reputation-history');
5549
const items: ReputationHistoryItemType[] = response.items;
5650

57-
// items.reduce(() => {
58-
//
59-
// }, )
60-
6151
const postsIds = uniq(items.map((i) => i.post_id)).join(';');
6252
const postsResponse: any = await stackoverflow.get(`posts/${postsIds}`, { filter: '!az5Wc(MqdIquv2' });
6353

@@ -73,16 +63,21 @@ export function ReputationDropdown() {
7363
return posts.find((i) => i.post_id === postId)!;
7464
}
7565

66+
function close() {
67+
setIsUnseen(false);
68+
setIsOpen(false);
69+
}
70+
7671
async function handleItemClick(item: ReputationHistoryItemType) {
7772
const post = getPostById(item.post_id);
7873

79-
setIsOpen(false);
74+
close();
8075
navigate(`/questions/${item.post_id}`, { state: { postType: post.post_type } });
8176
}
8277

8378
return (
8479
<>
85-
<Popover isOpen={isOpen} onClose={() => setIsOpen(false)} onOpen={() => setIsOpen(true)}>
80+
<Popover isOpen={isOpen} onClose={close} onOpen={() => setIsOpen(true)}>
8681
<PopoverTrigger>
8782
<Center
8883
sx={{ WebkitAppRegion: 'no-drag' }}

src/components/posts/QuestionDetails.tsx

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Box, Heading, HStack } from '@chakra-ui/react';
22
import parse from 'html-react-parser';
33
import { TagList } from '../tags/TagList';
44
import { PostProfileBadge } from '../profile/PostProfileBadge';
5-
import { useState } from 'react';
5+
import { useEffect, useState } from "react";
66
import { VotingControls } from './VotingControls';
77
import parseBody from '../../uitls/parse-body';
88
import { QuestionType } from '../../interfaces/QuestionType';
@@ -21,13 +21,77 @@ export function QuestionDetails({ question }: Props) {
2121
const [isBookmarked, setIsBookmarked] = useState(question.favorited);
2222
const [bookmarkCount, setBookmarkCount] = useState(question.favorite_count);
2323
const [comments, setComments] = useState<CommentType[]>(question.comments || []);
24+
const [isUpvoted, setIsUpvoted] = useState<boolean>(question.upvoted);
25+
const [isDownvoted, setIsDownvoted] = useState<boolean>(question.downvoted);
26+
27+
useEffect(() => {
28+
setScore(question.score);
29+
setIsUpvoted(question.upvoted);
30+
setIsDownvoted(question.downvoted);
31+
}, [question.score]);
32+
33+
async function handleUpvote() {
34+
if (isDownvoted) {
35+
await handleUndownvote();
36+
}
2437

25-
function handleUpvote() {
2638
setScore(score + 1);
39+
setIsUpvoted(true);
40+
41+
const response: QuestionType = await stackoverflow
42+
.post(`questions/${question.question_id}/upvote`, {
43+
filter: '!)fHrHyZKQR.5Q9a'
44+
})
45+
.then((r: any) => r.items[0]);
46+
47+
setIsUpvoted(response.upvoted);
48+
setScore(response.score);
2749
}
2850

29-
function handleDownvote() {
51+
async function handleUnupvote() {
3052
setScore(score - 1);
53+
setIsUpvoted(false);
54+
55+
const response: QuestionType = await stackoverflow
56+
.post(`questions/${question.question_id}/upvote/undo`, {
57+
filter: '!)fHrHyZKQR.5Q9a'
58+
})
59+
.then((r: any) => r.items[0]);
60+
61+
setIsUpvoted(response.upvoted);
62+
setScore(response.score);
63+
}
64+
65+
async function handleDownvote() {
66+
if (isUpvoted) {
67+
await handleUnupvote();
68+
}
69+
70+
setScore(score - 1);
71+
setIsDownvoted(true);
72+
73+
const response: QuestionType = await stackoverflow
74+
.post(`questions/${question.question_id}/downvote`, {
75+
filter: '!)fHrHyZKQR.5Q9a'
76+
})
77+
.then((r: any) => r.items[0]);
78+
79+
setIsDownvoted(response.downvoted);
80+
setScore(response.score);
81+
}
82+
83+
async function handleUndownvote() {
84+
setScore(score + 1);
85+
setIsDownvoted(false);
86+
87+
const response: QuestionType = await stackoverflow
88+
.post(`questions/${question.question_id}/downvote/undo`, {
89+
filter: '!)fHrHyZKQR.5Q9a'
90+
})
91+
.then((r: any) => r.items[0]);
92+
93+
setIsDownvoted(response.downvoted);
94+
setScore(response.score);
3195
}
3296

3397
function handleToggleBookmark() {
@@ -63,8 +127,12 @@ export function QuestionDetails({ question }: Props) {
63127
isBookmarked={isBookmarked}
64128
bookmarkCount={bookmarkCount}
65129
score={score}
130+
isUpvoted={isUpvoted}
131+
isDownvoted={isDownvoted}
66132
onUpvote={handleUpvote}
133+
onUnupvote={handleUnupvote}
67134
onDownvote={handleDownvote}
135+
onUndownvote={handleUndownvote}
68136
onToggleBookmark={handleToggleBookmark}
69137
/>
70138

src/components/posts/VotingControls.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,41 @@ import { BsBookmarkStar, BsFillBookmarkCheckFill } from 'react-icons/bs';
66
type Props = {
77
score: number;
88
bookmarkCount?: number;
9+
isUpvoted?: boolean;
10+
isDownvoted?: boolean;
911
onUpvote?: () => void;
12+
onUnupvote?: () => void;
1013
onDownvote?: () => void;
14+
onUndownvote?: () => void;
1115
onToggleBookmark?: () => void;
1216
postType: 'question' | 'answer';
1317
isAccepted?: boolean;
1418
isBookmarked?: boolean;
1519
};
1620

1721
export function VotingControls({
22+
isUpvoted,
23+
isDownvoted,
1824
isAccepted,
1925
isBookmarked,
2026
postType,
2127
score,
2228
bookmarkCount,
2329
onUpvote,
30+
onUnupvote,
2431
onDownvote,
32+
onUndownvote,
2533
onToggleBookmark
2634
}: Props) {
2735
return (
2836
<Stack spacing="8px" mt="-5px">
2937
<Center
3038
fontSize="35px"
31-
color="gray.300"
39+
color={isUpvoted ? 'orange.400' : 'gray.300'}
3240
cursor="pointer"
33-
_hover={{ color: 'gray.400' }}
41+
_hover={isUpvoted ? undefined : { color: 'gray.400' }}
3442
userSelect="none"
35-
onClick={onUpvote}
43+
onClick={isUpvoted ? onUnupvote : onUpvote}
3644
>
3745
<GoTriangleUp />
3846
</Center>
@@ -41,11 +49,11 @@ export function VotingControls({
4149
</Text>
4250
<Center
4351
fontSize="35px"
44-
color="gray.300"
52+
color={isDownvoted ? 'orange.400' : 'gray.300'}
4553
cursor="pointer"
46-
_hover={{ color: 'gray.400' }}
54+
_hover={isDownvoted ? undefined : { color: 'gray.400' }}
4755
userSelect="none"
48-
onClick={onDownvote}
56+
onClick={isDownvoted ? onUndownvote : onDownvote}
4957
>
5058
<GoTriangleDown />
5159
</Center>

src/interfaces/QuestionType.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export type QuestionType = {
1919
tags: string[];
2020
title: string;
2121
upvoted: boolean;
22+
downvoted: boolean;
2223
view_count: number;
2324
};

src/pages/QuestionDetailsPage.tsx

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,60 @@ export function QuestionDetailsPage() {
5757
if (!question || question.question_id !== parseInt(id)) {
5858
setIsLoaded(false);
5959

60-
stackoverflow
61-
.get(`questions/${id}`, {
62-
filter: '!9MyMg2qFPpNbuLMPVtF3UyZX-N4MWSjZwlQ(VqCZ3LoiM_GpZITfZz5'
63-
})
64-
.then((response) => {
65-
const question = (response as any).items[0];
66-
67-
setQuestion(question);
68-
setIsLoaded(true);
69-
});
60+
getQuestionById(id).then((question) => {
61+
setQuestion(question);
62+
setIsLoaded(true);
63+
});
7064
}
7165

72-
socketClient.on(`1-question-${id}`, () => {
73-
console.log('Question', 'questions changed');
74-
});
66+
if (question) {
67+
socketClient.on(`1-question-${id}`, (data: any) => {
68+
const isQuestionUpdating = data.id === question.question_id;
69+
70+
// TODO parse action
71+
// TODO listen for more actions
72+
switch (data.a) {
73+
case 'score': // Score changed
74+
if (isQuestionUpdating) {
75+
setTimeout(async () => {
76+
const updatedQuestion = await getQuestionById(id);
77+
78+
setQuestion({
79+
...question,
80+
score: data.score,
81+
upvoted: updatedQuestion.upvoted,
82+
downvoted: updatedQuestion.downvoted
83+
});
84+
}, 500);
85+
} else {
86+
const _question = clone(question);
87+
const answer = _question.answers.find((a) => a.answer_id === data.id);
88+
89+
if (answer) {
90+
answer.score = data.score;
91+
setQuestion(_question);
92+
}
93+
}
94+
break;
95+
}
96+
97+
console.log('Question', 'questions changed', data);
98+
});
99+
}
75100

76101
return () => {
77102
socketClient.off(`1-question-${id}`);
78103
};
79104
}, [id, postType]);
80105

106+
function getQuestionById(id: string) {
107+
return stackoverflow
108+
.get(`questions/${id}`, {
109+
filter: '!9MyMg2qFPpNbuLMPVtF3UyZX-N4MWSjZwlQ(VqCZ)UMRPTuNScvYNba'
110+
})
111+
.then((response) => (response as any).items[0]);
112+
}
113+
81114
function jumpToAnswers() {
82115
const scrollableEl = document.getElementById('scrolling-container');
83116

@@ -118,11 +151,7 @@ export function QuestionDetailsPage() {
118151
_question!.answers.push(answer);
119152
_question!.answer_count++;
120153

121-
console.log(_question);
122-
123154
setQuestion(_question);
124-
125-
console.log(question);
126155
}
127156

128157
if (!isLoaded || !question) {

src/pages/SettingsPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { MdPowerSettingsNew } from 'react-icons/md';
33
import { BsBell, BsEye } from 'react-icons/bs';
44
import { FaRegKeyboard } from 'react-icons/fa';
55
import { RiSettings3Line } from 'react-icons/ri';
6-
import { IoAccessibilityOutline } from "react-icons/io5";
6+
import { IoAccessibilityOutline } from 'react-icons/io5';
77
import { Link, matchPath, Outlet, useLocation } from 'react-router-dom';
88
import { IconType } from 'react-icons';
99

@@ -54,7 +54,7 @@ function NavItem({ link, icon, title }: NavItemProps) {
5454
_hover={isActive ? undefined : { bgColor: 'gray.100' }}
5555
rounded="5px"
5656
>
57-
<ListIcon as={icon} />
57+
<ListIcon as={icon} position="relative" top="-1px" />
5858
{title}
5959
</ListItem>
6060
</Link>

0 commit comments

Comments
 (0)