Skip to content

Commit

Permalink
Second upload
Browse files Browse the repository at this point in the history
  • Loading branch information
yarinsa committed Apr 7, 2020
1 parent 9c50cb2 commit 36fbba2
Show file tree
Hide file tree
Showing 27 changed files with 1,557 additions and 49 deletions.
25 changes: 25 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
extends: [
'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
+'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
+'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
],
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
ecmaFeatures: {
jsx: true, // Allows for the parsing of JSX
},
},
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
},
settings: {
react: {
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
},
},
};
7 changes: 7 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
semi: true,
trailingComma: 'all',
singleQuote: true,
printWidth: 120,
tabWidth: 4,
};
12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^24.0.0",
"@types/faker": "^4.1.11",
"@types/jest": "^25.1.4",
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"@types/styled-components": "^5.0.1",
"moment": "^2.24.0",
"normalize": "^0.3.1",
"normalize.css": "^8.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"styled-components": "^5.0.1",
"typescript": "~3.7.2"
},
"scripts": {
Expand All @@ -35,5 +41,9 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"babel-plugin-styled-components": "^1.10.7",
"faker": "^4.1.0"
}
}
7 changes: 4 additions & 3 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import styled from 'styled-components';

test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
69 changes: 47 additions & 22 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
import React from 'react';
import logo from './logo.svg';
import React, { useEffect, useState } from 'react';
import 'normalize.css';
import './App.css';
import styled from 'styled-components/macro';

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
//Components
import { LeftSection } from './components/ContactsSection/Side';
import { RightSection } from './components/ConversationSection/ConversationView';

//Interfaces
import { Conversation } from './api/conversations';

//Services
import * as conversationService from './api/conversations.service';

const App: React.FunctionComponent = () => {
const [loading, setLoading] = useState(false);
const [loadedConversations, setLoadedConversations] = useState<Conversation[]>();
const [selectedConversation, setSelectedConversation] = useState<Conversation>();
useEffect(() => {
async function loadConversations() {
const conversations = await conversationService.getConversations();
console.log(conversations);
setLoadedConversations(conversations);
setSelectedConversation(conversations[0]);
const selectedConversation = React.createContext<Conversation>(conversations[0]);
setLoading(false);
}

setLoading(true);
loadConversations();
}, []);
return (
//TODO: make styled component
<div className="App">
<Root>
{loadedConversations ? <LeftSection conversations={loadedConversations} /> : 'Loading...'}

{selectedConversation ? <RightSection {...selectedConversation} /> : 'Loading'}
</Root>
</div>
);
};

export default App;

const Root = styled.div`
display: flex;
overflow: hidden;
max-height: 100vh;
`;
82 changes: 82 additions & 0 deletions src/api/conversations.service.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as faker from 'faker';
import { Participant, Conversation, Message } from './conversations';

const KEY = 'conversations';
let conversationsList: Conversation[] = [];

export { getConversations, getById };

const getConversations = () => {
const conversations: null | string = localStorage.getItem(KEY);
if (conversations) {
conversationsList = JSON.parse(conversations);
return Promise.resolve<Conversation[]>(JSON.parse(conversations));
} else {
const conversationsList = _createConversations();
localStorage.setItem(KEY, JSON.stringify(conversationsList));
return Promise.resolve<Conversation[]>(conversationsList);
}
};

const getById = (conversationId: string) => {
const conversationIndex = conversationsList.findIndex((conversation) => conversationId === conversation.id);
if (conversationIndex !== -1) {
return Promise.resolve<Conversation>(conversationsList[conversationIndex]);
} else {
return Promise.reject('Cannot find conversation');
}
};

const _createConversations = () => {
const createMessages = (phoneNumbers: string[]) => {
const readAtOptions = [faker.date.recent(), null];
const contentOptions = [faker.lorem.sentences(Math.floor(Math.random() * 6 + 1)), faker.image.imageUrl()];
return Array(30)
.fill(null)
.map(() => {
const message: Message = {
readAt: faker.random.arrayElement(readAtOptions),
sentAt: faker.date.recent(),
by: phoneNumbers[Math.floor(Math.random() * phoneNumbers.length)],
content: faker.random.arrayElement(contentOptions),
type: '',
};
if (message.content.includes('http://lorempixel.com/')) {
message.type = 'image';
} else {
message.type = 'text';
}
return message;
});
};

const createParticipants = () => {
const participantsAmount = Math.floor(Math.random() * 3 + 1);
return Array(participantsAmount)
.fill(null)
.map(() => ({
phoneNumber: faker.phone.phoneNumber(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
imageUrl: faker.image.avatar(),
}));
};
const createConversation = () => {
const participants: Participant[] = createParticipants();
const messages: Message[] = createMessages(participants.map((participant) => participant.phoneNumber));
const conversation: Conversation = {
id: faker.random.uuid(),
title: faker.name.firstName(),
imageUrl: faker.image.avatar(),
participants: participants,
messages: messages,
};
console.log(conversation);
return conversation;
};
const conversations = Array(20)
.fill(null)
.map(() => createConversation());
console.log(conversations);
return conversations;
};
33 changes: 33 additions & 0 deletions src/api/conversations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export interface ConversationsList {
conversations: Conversation[];
}

export interface Conversation {
id: string;
title: string;
imageUrl: string;
messages: Message[];
participants: Participant[];
}

export interface ConversationPreview {
imageUrl: string;
title: string;
unreadCount: number;
time: Date;
content: string;
}
export interface Message {
readAt: Date | null;
sentAt: Date;
by: string; //phone number
content: string;
type: string;
}

export interface Participant {
phoneNumber: string;
firstName: string;
lastName: string;
imageUrl: string;
}
1 change: 1 addition & 0 deletions src/assets/icons/arrow-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/assets/icons/magnifier.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/menu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/send.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/smiley.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import styled from 'styled-components';

interface AvatarProps {
imageUrl: string;
size: string;
}
export const Avatar = styled.div<AvatarProps>`
background-image: url(${(props) => props.imageUrl});
width: ${(props) => props.size};
border-radius: 100vh;
height: ${(props) => props.size};
background-size: cover;
`;
40 changes: 40 additions & 0 deletions src/components/ContactsSection/ConversationList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import styled from 'styled-components';

//Components
import { ConversationListItem } from './ConversationListItem';

//Interfaces
import { Conversation } from '../../api/conversations';

interface ConversationListProps {
conversations: Conversation[];
}
export const ConversationList: React.FC<ConversationListProps> = ({ conversations }) => {
const ConversationList = conversations.map((conversation, index) => {
let unreadCount = conversation.messages.reduce((totalUnread, message) => {
return message.readAt ? totalUnread : totalUnread + 1; //if readAt === null then unread++
}, 0);

const lastMessage = conversation.messages[conversation.messages.length - 1];

// text > message preview
return (
<ConversationListItem
key={index}
imageUrl={conversation.imageUrl}
title={conversation.title}
unreadCount={unreadCount}
content={lastMessage.content}
time={lastMessage.sentAt}
/>
);
});
return <Root>{ConversationList}</Root>;
};

const Root = styled.div`
overflow-y: scroll;
height: 100vh;
width: 415px;
`;
Loading

0 comments on commit 36fbba2

Please sign in to comment.