Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 156f0c2

Browse files
gen/feat: Add create and/or join stream feature
1 parent 75dda37 commit 156f0c2

File tree

11 files changed

+274
-24
lines changed

11 files changed

+274
-24
lines changed

‎README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ LeetStream is a static website designed to help users improve their coding skill
88

99
Using LeetStream is simple:
1010

11+
1. **Create or Join a Stream**: **First**, create a stream by providing a title, start date, stream rate (problems/day), and a discussion group URL (WhatsApp, Telegram, Discord). Share this link with friends or a study group to solve problems together. **Second**, join the stream, you created or that was shared via the shared URL.
1112
1. **Solve the Daily Problem**: Each day, you'll be presented with a LeetCode problem categorized under one of the 26 coding patterns. Solve the problem to the best of your ability. **Remember, once the day is over, you won't be able to revisit or solve the problem again, so make it a habit to solve it daily.**
12-
2. **Submit Data**: After solving the problem, submit your data to calculate your score. Include information such as bugs encountered, hints used, time taken, and the type of solution (optimal, sub-optimal, brute force, ...).
13-
3. **Review Results**: Once you've submitted your data, review your score. You can download a **CSV file** containing your scores to further analyze your performance using spreadsheet programs. This file will help you gain deeper insights into your strengths and areas for improvement.
14-
4. **Access Study Material**: Explore links to articles and videos related to the coding patterns present in the problem you just solved. Use this material to deepen your understanding and improve your skills.
15-
5. **Repeat Daily**: Come back each day to tackle a new problem, track your progress, and continue learning. Remember, consistency is key, as you won't have another chance to solve missed problems.
13+
1. **Submit Data**: After solving the problem, submit your data to calculate your score. Include information such as bugs encountered, hints used, time taken, and the type of solution (optimal, sub-optimal, brute force, ...).
14+
1. **Review Results**: Once you've submitted your data, review your score. You can download a **CSV file** containing your scores to further analyze your performance using spreadsheet programs. This file will help you gain deeper insights into your strengths and areas for improvement.
15+
1. **Access Study Material**: Explore links to articles and videos related to the coding patterns present in the problem you just solved. Use this material to deepen your understanding and improve your skills.
16+
1. **Repeat Daily**: Come back each day to tackle a new problem, track your progress, and continue learning. Remember, consistency is key, as you won't have another chance to solve missed problems.
1617

1718
## Security and Privacy
1819

‎components/JoinStream.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { Component } from "./Component.js";
2+
import { sanitizeInput } from "../utils.js";
3+
import { UserDb } from "../db/userDb.js";
4+
5+
class JoinStream extends Component {
6+
constructor() {
7+
super();
8+
9+
this.querySelector("#join-stream").addEventListener("click", () => {
10+
let stored = UserDb.get();
11+
12+
if (
13+
stored.loggedIn &&
14+
!confirm(
15+
`Are you sure you want to log out from ${stored.stream.title} stream?`
16+
)
17+
)
18+
return;
19+
20+
UserDb.clear();
21+
stored = UserDb.get();
22+
stored.loggedIn = true;
23+
stored.stream = {
24+
title: this.title,
25+
startDate: this.startDate,
26+
streamRate: this.streamRate,
27+
discussionUrl: this.discussionUrl,
28+
};
29+
UserDb.set(stored);
30+
location.hash = "";
31+
location.reload();
32+
});
33+
34+
if (new Date(this.startDate) > new Date()) {
35+
this.querySelector("#join-stream").disabled = true;
36+
this.querySelector("#join-stream").innerText = "Stream not started yet";
37+
}
38+
}
39+
render() {
40+
let url = new URL(window.location.href);
41+
42+
this.title = sanitizeInput(url.searchParams.get("title"));
43+
this.streamRate = sanitizeInput(url.searchParams.get("stream-rate") || "1");
44+
this.startDate = sanitizeInput(url.searchParams.get("start-date"));
45+
this.discussionUrl = sanitizeInput(url.searchParams.get("discussion-url"));
46+
url = sanitizeInput(url.toString());
47+
48+
return /*HTML*/ `
49+
<style>
50+
join-stream>table {
51+
margin: 0 auto;
52+
max-width: 70ch;
53+
font-size: 1.2rem;
54+
}
55+
56+
join-stream>table>thead>th>h1 {
57+
text-align: center;
58+
}
59+
60+
join-stream>table>tbody>tr>td {
61+
padding: .5rem 0;
62+
}
63+
64+
join-stream {
65+
padding: 1rem;
66+
}
67+
68+
join-stream .submit {
69+
display: block;
70+
margin: 1rem auto;
71+
}
72+
73+
#join-stream:disabled {
74+
background-image: linear-gradient(319deg, #bbb 0%, #727272 37%, #1f1f1f 100%);
75+
cursor: not-allowed;
76+
}
77+
</style>
78+
<table>
79+
<thead>
80+
<th colspan="2">
81+
<h1>Do you want to join ${this.title} stream 🌊?</h1>
82+
</th>
83+
</thead>
84+
<tbody>
85+
<tr>
86+
<td>
87+
Stream Title
88+
</td>
89+
<td>
90+
${this.title}
91+
</td>
92+
</tr>
93+
<tr>
94+
<td>
95+
Stream URL
96+
</td>
97+
<td>
98+
<a href="${url}">${url}</a>
99+
</td>
100+
</tr>
101+
<tr>
102+
<td>
103+
Stream Rate
104+
</td>
105+
<td>
106+
${this.streamRate}
107+
</td>
108+
</tr>
109+
<tr>
110+
<td>
111+
Start Date (Africa/Cairo time)
112+
</td>
113+
<td>
114+
${this.startDate}
115+
</td>
116+
</tr>
117+
<tr>
118+
<td>
119+
Discussion Group URL
120+
</td>
121+
<td>
122+
<a href="${this.discussionUrl}">${this.discussionUrl}</a>
123+
</td>
124+
</tr>
125+
</tbody>
126+
<tfoot>
127+
<tr>
128+
<td colspan="2">
129+
<button class="submit" id="join-stream">Join Stream</button>
130+
</td>
131+
</tr>
132+
</tfoot>
133+
</table>
134+
`;
135+
}
136+
}
137+
138+
customElements.define("join-stream", JoinStream);

‎components/NewStream.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Component } from "./Component.js";
2+
3+
class NewStream extends Component {
4+
render() {
5+
return /*HTML*/ `
6+
<style>
7+
.pg-title {
8+
text-align: center;
9+
margin-bottom: 32px;
10+
}
11+
12+
.new-stream-form>div {
13+
display: flex;
14+
flex-direction: column;
15+
gap: 32px;
16+
max-width: 70ch;
17+
}
18+
19+
.new-stream-form .submit {
20+
align-self: center;
21+
}
22+
23+
.new-stream-form input, .new-stream-form input[type="number"]{
24+
flex-grow: 1;
25+
width: 100%;
26+
}
27+
</style>
28+
29+
<h1 class="pg-title">Create your Stream 🌊</h1>
30+
31+
<form class="new-stream-form" action="#joinStream">
32+
<div>
33+
<div class="labeled-input">
34+
<label for="title">Title</label>
35+
<input type="text" name="title" id="title">
36+
</div>
37+
38+
<div class="labeled-input">
39+
<label for="start-date">Start Date (Africa/Cairo time)</label>
40+
<input type="date" name="start-date" id="start-date">
41+
</div>
42+
43+
<div class="labeled-input">
44+
<label for="stream-rate">Stream Rate (problems per day)</label>
45+
<input type="number" name="stream-rate" id="stream-rate" disabled placeholder="1">
46+
</div>
47+
48+
<div class="labeled-input">
49+
<label for="discussion-url">Discussion Group URL (e.g., WhatsApp, Telegram, Discord, etc.)</label>
50+
<input type="url" name="discussion-url" id="discussion-url">
51+
</div>
52+
53+
<button class="submit" id="create-stream">Create Stream</button>
54+
</div>
55+
</form>
56+
`;
57+
}
58+
}
59+
60+
customElements.define("new-stream", NewStream);

‎components/ProblemForm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class ProblemForm extends Component {
157157
<textarea name="note" id="note"></textarea>
158158
</div>
159159
160-
<button type="done" id="done"><span>0.00</span> Done</button>
160+
<button type="done" id="done"><span>0.00</span> Done</button>
161161
</form>
162162
</div>
163163
`;

‎db/problems.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Difficulty } from "./difficulty.js";
22
import { patterns } from "./patterns.js";
33
import { sites } from "./sites.js";
4+
import { UserDb } from "./userDb.js";
45

56
export function getProblemOfDay() {
67
const currentDate = new Date();
78
currentDate.toLocaleString("en-US", { timeZone: "Africa/Cairo" });
89
return (
910
Math.floor(
10-
(currentDate.getTime() - new Date("2024-04-07T00:00:00").getTime()) /
11+
(currentDate.getTime() - new Date(`${UserDb.get().stream.startDate}T00:00:00`).getTime()) /
1112
(1000 * 3600 * 24)
1213
) % problems.length
1314
);
@@ -6094,7 +6095,7 @@ export const problems = [
60946095
id: "L84",
60956096
title: "Largest Rectangle in Histogram",
60966097
difficulty: Difficulty.HARD,
6097-
patterns: ["patterns.ChallengeYourself"],
6098+
patterns: [patterns.ChallengeYourself],
60986099
url: "https://leetcode.com/problems/largest-rectangle-in-histogram",
60996100
solutions: [
61006101
{

‎db/userDb.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
export class UserDb {
22
/*
33
{
4+
loggedIn: boolean,
5+
stream: {
6+
title: string,
7+
startDate: string,
8+
streamRate: number,
9+
discussionUrl: string
10+
},
411
maxHints: number,
512
maxBugs: number,
613
problems: id: {
@@ -15,6 +22,8 @@ export class UserDb {
1522
}
1623
*/
1724
static data = JSON.parse(localStorage.getItem("stored")) || {
25+
loggedIn: false,
26+
stream: null,
1827
maxHints: 0,
1928
maxBugs: 0,
2029
problems: {},

‎index.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!DOCTYPE html>
12
<html lang="en">
23

34
<head>
@@ -15,7 +16,8 @@
1516
<script defer src="./components/Confettiful.js" type="module"></script>
1617
<script defer src="./components/ContributeButton.js" type="module"></script>
1718
<script defer src="./components/MarkdownRenderer.js" type="module"></script>
18-
19+
<script defer src="./components/NewStream.js" type="module"></script>
20+
<script defer src="./components/JoinStream.js" type="module"></script>
1921
<meta name="google-site-verification" content="78cJJRgaw8hmyPZixXq6MIUZCR8kJaPf2Yh8oT38dqQ" />
2022
</head>
2123

@@ -27,9 +29,11 @@ <h2 class="logo"><span class="logo-text">LeetStream</span> 🌊</h2>
2729
</a>
2830
<ul>
2931
<li>🏠 <a href="/" class="active">Home</a></li>
32+
<li>📝 <a href="#newStream">New Stream</a></li>
3033
<li>📚 <a href="#usage">How to Use</a></li>
3134
<li>🤝 <a href="https://github.com/LeetStream/leetstream.github.io">Contribute</a></li>
3235
<li><button id="export">📤 Export as CSV</button></li>
36+
<li><button id="logout">Logout</button></li>
3337
</ul>
3438
</nav>
3539
</header>

‎routers.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@ import { UserDb } from "./db/userDb.js";
44
export const routers = {
55
"/": "<problem-form/>",
66
done: "<done-comp/>",
7-
usage: /*HTML*/ `<markdown-renderer src="./README.md"/>`,
7+
usage: "<markdown-renderer src='./README.md'/>",
8+
newStream: "<new-stream/>",
9+
joinStream: "<join-stream/>",
810
};
911

1012
export function routingRules() {
11-
if (
12-
location.hash.slice(1) === "done" &&
13-
(Object.keys(UserDb.get().problems).length === 0 ||
14-
!UserDb.get().problems[problems[getProblemOfDay()].id])
15-
)
16-
location.hash = "";
17-
else if (
18-
Object.keys(UserDb.get().problems).length > 0 &&
19-
UserDb.get().problems[problems[getProblemOfDay()].id] &&
20-
["", "problem"].includes(location.hash.slice(1))
21-
)
22-
location.hash = "done";
13+
if (!UserDb.get().loggedIn) {
14+
if (location.hash.slice(1) === "") location.hash = "newStream";
15+
} else {
16+
if (
17+
location.hash.slice(1) === "done" &&
18+
(Object.keys(UserDb.get().problems).length === 0 ||
19+
!UserDb.get().problems[problems[getProblemOfDay()].id])
20+
)
21+
location.hash = "";
22+
else if (
23+
location.hash.slice(1) === "" &&
24+
Object.keys(UserDb.get().problems).length > 0 &&
25+
UserDb.get().problems[problems[getProblemOfDay()].id]
26+
)
27+
location.hash = "done";
28+
}
2329
}
2430

2531
export function router() {

‎script.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,19 @@ function activeNavLink() {
5151
`nav li a[href="${location.hash || "/"}"]`
5252
);
5353
if (activeLink) activeLink.classList.add("active");
54+
if (UserDb.get().loggedIn) {
55+
document.querySelector("#export").style.display = "block";
56+
document.querySelector("#logout").style.display = "block";
57+
}
5458
}
59+
60+
document.querySelector("#logout").addEventListener("click", () => {
61+
if (
62+
!confirm(
63+
`Are you sure you want to log out from ${UserDb.get().stream.title}?`
64+
)
65+
)
66+
return;
67+
UserDb.clear();
68+
location.hash = "newStream";
69+
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /