From 782400b5a2a1ee6507810d55d265c09759b6d0f7 Mon Sep 17 00:00:00 2001 From: Kevan-Y <58233223+Kevan-Y@users.noreply.github.com> Date: Sat, 16 Apr 2022 16:51:59 -0400 Subject: [PATCH 01/38] Fix Search label text cut off --- src/web/app/src/components/SearchInput/SearchInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/app/src/components/SearchInput/SearchInput.tsx b/src/web/app/src/components/SearchInput/SearchInput.tsx index 210c6b83a3..771562d87b 100644 --- a/src/web/app/src/components/SearchInput/SearchInput.tsx +++ b/src/web/app/src/components/SearchInput/SearchInput.tsx @@ -19,7 +19,7 @@ const useStyles = makeStyles((theme: Theme) => transition: theme.transitions.create(['background-color', 'border-color'], { duration: '.5s', }), - fontSize: '1.2rem', + fontSize: '14px', display: 'block', color: theme.palette.text.primary, }, From ac01238b2bc716a409d0936bb604ceecdfe5ce3f Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 16 Apr 2022 06:28:39 +0000 Subject: [PATCH 02/38] Update dependency @bull-board/api to v3.10.4 --- package.json | 2 +- pnpm-lock.yaml | 28 +++++++++++++++++++++++----- src/api/parser/package.json | 2 +- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index f0dfa4fba8..c3d55f8e7a 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "homepage": "https://github.com/Seneca-CDOT/telescope#readme", "dependencies": { - "@bull-board/api": "3.10.3", + "@bull-board/api": "3.10.4", "@bull-board/express": "3.10.3", "@elastic/elasticsearch": "7.16.0", "@elastic/elasticsearch-mock": "0.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6b85c4945..760ba91169 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,7 +17,7 @@ importers: '@babel/preset-env': 7.16.11 '@babel/preset-react': 7.16.7 '@babel/preset-typescript': 7.16.7 - '@bull-board/api': 3.10.3 + '@bull-board/api': 3.10.4 '@bull-board/express': 3.10.3 '@elastic/elasticsearch': 7.16.0 '@elastic/elasticsearch-mock': 0.3.1 @@ -83,7 +83,7 @@ importers: turbo: 1.2.2 typescript: 4.4.4 dependencies: - '@bull-board/api': 3.10.3 + '@bull-board/api': 3.10.4 '@bull-board/express': 3.10.3 '@elastic/elasticsearch': 7.16.0 '@elastic/elasticsearch-mock': 0.3.1 @@ -220,7 +220,7 @@ importers: src/api/parser: specifiers: - '@bull-board/api': 3.10.3 + '@bull-board/api': 3.10.4 '@bull-board/express': 3.10.3 '@senecacdot/eslint-config-telescope': 1.1.0 '@senecacdot/satellite': ^1.27.0 @@ -241,7 +241,7 @@ importers: rss-parser: 3.12.0 sanitize-html: 2.5.3 dependencies: - '@bull-board/api': 3.10.3 + '@bull-board/api': 3.10.4 '@bull-board/express': 3.10.3 '@senecacdot/satellite': 1.27.0 '@supabase/supabase-js': 1.29.4 @@ -3310,6 +3310,12 @@ packages: redis-info: 3.1.0 dev: false + /@bull-board/api/3.10.4: + resolution: {integrity: sha512-JJjMg8O/ELeaqkuL1Wsdn6rdQfH+/2+BfnFD0B7j4ZCtLVAPfsOUZYpLqSKUgaNizwp1nTw0e3L/EI0yvX5aiw==} + dependencies: + redis-info: 3.1.0 + dev: false + /@bull-board/express/3.10.3: resolution: {integrity: sha512-ag6kziQjc6sYVtbHluev7dQznE143JnI12mwCusvOb1tMv8eKmC4Bvw9WhudOJNQwXKzhbxSOxxX5+1MpRo/XQ==} dependencies: @@ -7593,7 +7599,7 @@ packages: /axios/0.21.4_debug@4.3.3: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.14.7 + follow-redirects: 1.14.7_debug@4.3.3 transitivePeerDependencies: - debug dev: false @@ -11226,6 +11232,18 @@ packages: debug: optional: true + /follow-redirects/1.14.7_debug@4.3.3: + resolution: {integrity: sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dependencies: + debug: 4.3.3 + dev: false + /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: diff --git a/src/api/parser/package.json b/src/api/parser/package.json index a16146558a..918b3506a1 100644 --- a/src/api/parser/package.json +++ b/src/api/parser/package.json @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/Seneca-CDOT/telescope#readme", "dependencies": { - "@bull-board/api": "3.10.3", + "@bull-board/api": "3.10.4", "@bull-board/express": "3.10.3", "@senecacdot/satellite": "^1.27.0", "@supabase/supabase-js": "1.29.4", From 73118b0f8b619b9c746c751c1a53c755bd3d7d35 Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Fri, 15 Apr 2022 11:13:34 -0400 Subject: [PATCH 03/38] Make parser arg optional, rename main function for github-url-parser --- src/github-url-parser/package.json | 2 +- src/github-url-parser/src/index.ts | 2 +- src/github-url-parser/test/index.test.js | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/github-url-parser/package.json b/src/github-url-parser/package.json index 89df2b8f88..0ceaeb9b46 100644 --- a/src/github-url-parser/package.json +++ b/src/github-url-parser/package.json @@ -1,6 +1,6 @@ { "name": "@senecacdot/github-url-parser", - "version": "1.0.0", + "version": "2.0.0", "description": "A library for parsing GitHub URLs from HTM text", "source": "src/index.ts", "main": "dist/main.js", diff --git a/src/github-url-parser/src/index.ts b/src/github-url-parser/src/index.ts index 9720196472..b7913aa749 100644 --- a/src/github-url-parser/src/index.ts +++ b/src/github-url-parser/src/index.ts @@ -20,7 +20,7 @@ export const htmlToUrls = (htmlString: string, parser: DOMParser): string[] => { return Array.from(new Set(allGithubLinks)); }; -export const githubUrlParser = (htmlString: string, parser: DOMParser): GitHubInfo => { +export const parseGitHubUrls = (htmlString: string, parser?: DOMParser): GitHubInfo => { if (!parser && !('DOMParser' in globalThis)) { throw new Error('Missing parser property and environment does not support DOMParser'); } diff --git a/src/github-url-parser/test/index.test.js b/src/github-url-parser/test/index.test.js index 5692db114b..99388a4630 100644 --- a/src/github-url-parser/test/index.test.js +++ b/src/github-url-parser/test/index.test.js @@ -1,9 +1,9 @@ -const { githubUrlParser } = require('../dist/main'); +const { parseGitHubUrls } = require('../dist/main'); const { parser } = require('./util'); -describe('githubUrlParser', () => { +describe('parseGitHubUrls', () => { test('Should throw if missing DOMParser', () => { - expect(() => githubUrlParser('abc')).toThrow(); + expect(() => parseGitHubUrls('abc')).toThrow(); }); test('Should return expected GitHub Info for a string of HTML', () => { @@ -12,7 +12,7 @@ describe('githubUrlParser', () => { Telescope Issue #3452 Telescope PR commit 82f1659 `; - expect(githubUrlParser(html, parser())).toEqual({ + expect(parseGitHubUrls(html, parser())).toEqual({ users: ['Seneca-CDOT'], repos: ['Seneca-CDOT/telescope'], commits: [ From c8039e7962559bf7e88c759a69758443ba95068a Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Sun, 10 Apr 2022 14:58:03 -0400 Subject: [PATCH 04/38] Create CloudFormation template --- config/ec2-template.yml | 89 +++++++++++++++++++ .../docs/getting-started/vscode-ssh.md | 16 ++-- 2 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 config/ec2-template.yml diff --git a/config/ec2-template.yml b/config/ec2-template.yml new file mode 100644 index 0000000000..954a4efdc7 --- /dev/null +++ b/config/ec2-template.yml @@ -0,0 +1,89 @@ +AWSTemplateFormatVersion: 2010-09-09 + +# https://telescope.cdot.systems/docs/getting-started/vscode-ssh +Description: Telescope EC2 VSCode SSH Development Environment + +Parameters: + ImageId: + Type: AWS::EC2::Image::Id + Default: ami-0c02fb55956c7d316 + Description: EC2 Image ID, defaults to Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type - ami-0c02fb55956c7d316 (64-bit x86) + + KeyName: + Type: AWS::EC2::KeyPair::KeyName + Default: vockey # Default key in Vocareum + + MyIp: + Type: String + Description: Your home IP address (use http://checkip.amazonaws.com/ to check)` + + StorageSize: + Type: Number + Default: 40 + Description: Amount of storage (GiB) attached to instance + +Resources: + Ec2Instance: + Type: AWS::EC2::Instance + Properties: + InstanceType: r5.large + ImageId: !Ref ImageId + IamInstanceProfile: LabInstanceProfile + KeyName: !Ref KeyName + SecurityGroups: + - !Ref SecurityGroup + BlockDeviceMappings: + - DeviceName: /dev/xvda + Ebs: + VolumeSize: !Ref StorageSize + # TODO: could also pass a file via `--user-data file://script.sh` + UserData: !Base64 | + #!/bin/bash + sudo yum update -y + # Install docker + sudo amazon-linux-extras install docker + sudo service docker start + sudo systemctl enable docker + sudo usermod -a -G docker ec2-user + # Install Node.js + curl -sL https://rpm.nodesource.com/setup_lts.x | sudo bash - + sudo yum install -y nodejs + # Install pnpm + sudo npm install -g pnpm + # Install git + sudo yum install -y git + # Install GitHub CLI + sudo yum install -y https://github.com/cli/cli/releases/download/v2.7.0/gh_2.7.0_linux_amd64.rpm + + SecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Telescope SSH (22) and Web Access (80) + GroupName: telescope-sg + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: !Sub ${MyIp}/32 + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + # Also allow EC2 Connect (e.g., from console) for us-east-1 + CidrIp: 18.206.107.24/29 + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: !Sub ${MyIp}/32 + + EIP: + Type: AWS::EC2::EIP + Properties: + InstanceId: !Ref Ec2Instance + +Outputs: + PublicDnsName: + Value: !GetAtt Ec2Instance.PublicDnsName + Description: Public DNS for EC2 instance + PublicIp: + Value: !GetAtt Ec2Instance.PublicIp + Description: Public IP Address for EC2 instance diff --git a/src/web/docusaurus/docs/getting-started/vscode-ssh.md b/src/web/docusaurus/docs/getting-started/vscode-ssh.md index a3b6ffb89e..1c94b8184d 100644 --- a/src/web/docusaurus/docs/getting-started/vscode-ssh.md +++ b/src/web/docusaurus/docs/getting-started/vscode-ssh.md @@ -1,5 +1,11 @@ # Use AWS EC2 As Your Development Environment +:::info + +This guide has also been translated into a [CloudFormation](https://aws.amazon.com/cloudformation/) Template that can be used to automatically create the same resources. See [config/e2c-template.yml](../../../../../config/ec2-template.yml). + +::: + The following will show you how to create and connect to a virtual machine (VM) on AWS using the Visual Studio Code [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) extension. You'll be able to run Telescope in development on a remote machine with VS Code just like if the source code was local. This documentation is based on [Remote development over SSH](https://code.visualstudio.com/docs/remote/ssh-tutorial) **Disclaimer**: This guide is specifically designed for students who are enrolled in AWS Academy so the services and technologies used adhere by the AWS Academy Learner Lab - Foundation Services restrictions. @@ -30,13 +36,13 @@ Running Docker in development is CPU intensive so these are the EC2 instances I | 20GB EBS Volume | \$0.5 | \$0.5 | | Total | \$18.10 | \$24.08 | -## Prerequisites: +## Prerequisites - Download and install [Visual Studio Code](https://code.visualstudio.com/download) - Install the [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) extension - AWS Academy Account. You will need your `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` and your SSH key (`.pem` file) -## Create your virtual machine (AWS EC2): +## Create your virtual machine (AWS EC2) 1. In the upper-right hand corner of your AWS Management Console, select a region. In this tutorial, `US East (N. Virginia) us-east-1` as your `Region` @@ -201,7 +207,7 @@ Default output format [None]: 6. How would you like to authenticate GitHub CLI? `Login with a web browser` 7. First copy your one-time code: `ABC1-234D` -## Opening up the Telescope repository in AWS EC2: +## Opening up the Telescope repository in AWS EC2 1. Clone your fork of the Telescope repository. For example `gh repo clone cindyledev/telescope`. If you do not have a fork of the Telescope repository, run `gh repo clone -o upstream Seneca-CDOT/telescope` to clone the Telescope repository and name the remote `upstream` then proceed to Step 5. @@ -231,9 +237,9 @@ git remote -v sh ./tools/aws-ip.sh ``` -### If you did everything correctly, you've completed the environment setup! +### If you did everything correctly, you've completed the environment setup -## Now to get started with development... +## Now to get started with development 1. Install all dependencies From bd66b519ccbddf0812636e7122b570a80cbf0a05 Mon Sep 17 00:00:00 2001 From: Diana Belokon <67607236+dbelokon@users.noreply.github.com> Date: Tue, 19 Apr 2022 00:12:52 -0400 Subject: [PATCH 05/38] Fix image flickering on star field --- src/web/app/src/components/StarField.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/app/src/components/StarField.tsx b/src/web/app/src/components/StarField.tsx index 7da57e5f08..9e0000b3ef 100644 --- a/src/web/app/src/components/StarField.tsx +++ b/src/web/app/src/components/StarField.tsx @@ -67,7 +67,7 @@ class Star { this.p5 = p5; this.x = p5.random(-p5.width / 2, p5.width / 2); this.y = p5.random(-p5.height / 2, p5.height / 2); - this.z = p5.random(p5.width * 0.75, p5.width); + this.z = p5.random(p5.width * 0.1, p5.width * 0.95); } update(speed: number) { @@ -77,7 +77,7 @@ class Star { if (this.z < 1) { this.x = p5.random(-p5.width / 2, p5.width / 2); this.y = p5.random(-p5.height / 2, p5.height / 2); - this.z = p5.width; + this.z = p5.random(p5.width * 0.1, p5.width * 0.95); } } From e4aee6886dfeea355acbc0f656421f65ce54e592 Mon Sep 17 00:00:00 2001 From: DukeManh Date: Mon, 18 Apr 2022 16:25:11 -0400 Subject: [PATCH 06/38] Fix search icon position --- src/web/app/src/components/SearchInput/SearchInput.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/web/app/src/components/SearchInput/SearchInput.tsx b/src/web/app/src/components/SearchInput/SearchInput.tsx index 771562d87b..a87039a6b0 100644 --- a/src/web/app/src/components/SearchInput/SearchInput.tsx +++ b/src/web/app/src/components/SearchInput/SearchInput.tsx @@ -38,14 +38,15 @@ const useStyles = makeStyles((theme: Theme) => }, margin: 0, position: 'absolute', - top: '20px', - padding: '13px', + top: '50%', + right: '0.4rem', + transform: 'translateY(-50%)', color: '#A0D1FA', - marginRight: '0.5rem', }, wrapper: { display: 'flex', flexDirection: 'row-reverse', + position: 'relative', }, }) ); From 894e30762a0cd57746213f48adc19f26c81e86df Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 18 Apr 2022 22:53:17 +0000 Subject: [PATCH 07/38] Update dependency @bull-board/express to v3.10.4 --- package.json | 2 +- pnpm-lock.yaml | 28 +++++++++++----------------- src/api/parser/package.json | 2 +- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index c3d55f8e7a..3b901ba69e 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "homepage": "https://github.com/Seneca-CDOT/telescope#readme", "dependencies": { "@bull-board/api": "3.10.4", - "@bull-board/express": "3.10.3", + "@bull-board/express": "3.10.4", "@elastic/elasticsearch": "7.16.0", "@elastic/elasticsearch-mock": "0.3.1", "@wordpress/wordcount": "2.15.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 760ba91169..e9fe9b22bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,7 +18,7 @@ importers: '@babel/preset-react': 7.16.7 '@babel/preset-typescript': 7.16.7 '@bull-board/api': 3.10.4 - '@bull-board/express': 3.10.3 + '@bull-board/express': 3.10.4 '@elastic/elasticsearch': 7.16.0 '@elastic/elasticsearch-mock': 0.3.1 '@parcel/packager-ts': 2.4.1 @@ -84,7 +84,7 @@ importers: typescript: 4.4.4 dependencies: '@bull-board/api': 3.10.4 - '@bull-board/express': 3.10.3 + '@bull-board/express': 3.10.4 '@elastic/elasticsearch': 7.16.0 '@elastic/elasticsearch-mock': 0.3.1 '@wordpress/wordcount': 2.15.2 @@ -221,7 +221,7 @@ importers: src/api/parser: specifiers: '@bull-board/api': 3.10.4 - '@bull-board/express': 3.10.3 + '@bull-board/express': 3.10.4 '@senecacdot/eslint-config-telescope': 1.1.0 '@senecacdot/satellite': ^1.27.0 '@supabase/supabase-js': 1.29.4 @@ -242,7 +242,7 @@ importers: sanitize-html: 2.5.3 dependencies: '@bull-board/api': 3.10.4 - '@bull-board/express': 3.10.3 + '@bull-board/express': 3.10.4 '@senecacdot/satellite': 1.27.0 '@supabase/supabase-js': 1.29.4 bull: 3.29.3 @@ -3304,31 +3304,25 @@ packages: deprecated: Potential XSS vulnerability patched in v6.0.0. dev: false - /@bull-board/api/3.10.3: - resolution: {integrity: sha512-kV6EPwi9j71qBmozvDmtT01j986r4cFqNmBgq7HApYXW0G2U8Brmv0Ut0iMQZRc/X7aA5KYL3qXcEsriFnq+jw==} - dependencies: - redis-info: 3.1.0 - dev: false - /@bull-board/api/3.10.4: resolution: {integrity: sha512-JJjMg8O/ELeaqkuL1Wsdn6rdQfH+/2+BfnFD0B7j4ZCtLVAPfsOUZYpLqSKUgaNizwp1nTw0e3L/EI0yvX5aiw==} dependencies: redis-info: 3.1.0 dev: false - /@bull-board/express/3.10.3: - resolution: {integrity: sha512-ag6kziQjc6sYVtbHluev7dQznE143JnI12mwCusvOb1tMv8eKmC4Bvw9WhudOJNQwXKzhbxSOxxX5+1MpRo/XQ==} + /@bull-board/express/3.10.4: + resolution: {integrity: sha512-F8E54TUjQn1Q4tbYfRVlrs6okz4gkZbm0ksvog7XurshsOPC66q9HlRQCW6oVs1a6VBDPJZkvNAtKKA+OsKeFA==} dependencies: - '@bull-board/api': 3.10.3 - '@bull-board/ui': 3.10.3 + '@bull-board/api': 3.10.4 + '@bull-board/ui': 3.10.4 ejs: 3.1.6 express: 4.17.3 dev: false - /@bull-board/ui/3.10.3: - resolution: {integrity: sha512-6zYW3FqySg+4IKEeM1jt/5ixNVBKQjtZLG9W81ADVcHk8YceQ++7URWzDb8nQEct3rEW4bjR6nicVWNXMSN7Lw==} + /@bull-board/ui/3.10.4: + resolution: {integrity: sha512-nqnE3wqqpso7ORPcmcGVesYeFkHwv3AsBdRV2W0VLtfBPGzMdqZ1sJeSTAmlanFZnvTprU4Eg/G0DcEeMUTGhA==} dependencies: - '@bull-board/api': 3.10.3 + '@bull-board/api': 3.10.4 dev: false /@docsearch/css/3.0.0: diff --git a/src/api/parser/package.json b/src/api/parser/package.json index 918b3506a1..a5dc39e05a 100644 --- a/src/api/parser/package.json +++ b/src/api/parser/package.json @@ -19,7 +19,7 @@ "homepage": "https://github.com/Seneca-CDOT/telescope#readme", "dependencies": { "@bull-board/api": "3.10.4", - "@bull-board/express": "3.10.3", + "@bull-board/express": "3.10.4", "@senecacdot/satellite": "^1.27.0", "@supabase/supabase-js": "1.29.4", "bull": "3.29.3", From e522c31d5c6033e28c337ee9e45980018b439e50 Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Tue, 19 Apr 2022 09:49:25 -0400 Subject: [PATCH 08/38] Fix #3516: don't link outside src/web/docusaurus --- src/web/docusaurus/docs/getting-started/vscode-ssh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/docusaurus/docs/getting-started/vscode-ssh.md b/src/web/docusaurus/docs/getting-started/vscode-ssh.md index 1c94b8184d..80d050d925 100644 --- a/src/web/docusaurus/docs/getting-started/vscode-ssh.md +++ b/src/web/docusaurus/docs/getting-started/vscode-ssh.md @@ -2,7 +2,7 @@ :::info -This guide has also been translated into a [CloudFormation](https://aws.amazon.com/cloudformation/) Template that can be used to automatically create the same resources. See [config/e2c-template.yml](../../../../../config/ec2-template.yml). +This guide has also been translated into a [CloudFormation](https://aws.amazon.com/cloudformation/) Template that can be used to automatically create the same resources. See `config/e2c-template.yml`. ::: From f36ebaaa4c8300da708e71d04b9bbdf3c6fe817e Mon Sep 17 00:00:00 2001 From: Diana Belokon <67607236+dbelokon@users.noreply.github.com> Date: Tue, 19 Apr 2022 05:27:45 -0400 Subject: [PATCH 09/38] Write image service docs --- src/web/docusaurus/docs/api-services/image.md | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/web/docusaurus/docs/api-services/image.md diff --git a/src/web/docusaurus/docs/api-services/image.md b/src/web/docusaurus/docs/api-services/image.md new file mode 100644 index 0000000000..ac18164b8a --- /dev/null +++ b/src/web/docusaurus/docs/api-services/image.md @@ -0,0 +1,136 @@ +--- +sidebar_position: 4 +--- + +# Image Service + +## Overview + +The image service provides the images used in the hero banner found in the Telescope web page. + +## API endpoints + +### Get a random image or get a specific image by its name + +``` +GET / +``` + +#### Parameters + +| Name | Type | In | Description | +| ----- | ---------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------- | +| image | string OR number | path | _Optional_. The exact name of the image if the value is a string, otherwise an index of the image (value is a number). | +| w | number | query | _Optional_. The width of the image. It can be between 40 and 2000, inclusive. Defaults to 800. | +| h | number | query | _Optional_. The height of the image. It can be between 40 and 3000, inclusive. Defaults to the height of the image when width is 800. | +| t | string | query | _Optional_. The type of image to render. It may be one of the following: `jpeg`, `jpg`, `png`, `webp`. Defaults to `jpeg`. | + +#### Code Samples + +##### Shell + +The following example gets an available random image: + +```bash +curl http://localhost/v1/image +``` + +The following example gets a specific image by its index (the name is not needed): + +```bash +curl http://localhost/v1/image/0 +``` + +The following example gets a specific by its name (has to be exact): + +```bash +curl http://localhost/v1/image/default.jpg +``` + +##### JavaScript + +- With `fetch` + +The following example gets an available random image: + +```js +fetch('http://localhost/v1/image/'); +``` + +The following example gets a specific image by its index (the name is not needed): + +```js +fetch('http://localhost/v1/image/0'); +``` + +The following example gets a specific by its name (has to be exact): + +```js +fetch('http://localhost/v1/image/default.jpg'); +``` + +#### Responses + +##### Successful response + +``` +Status: 200 OK +``` + +The body is an image file, with a specific image format ([`WebP`](https://en.wikipedia.org/wiki/WebP)). + +##### Bad Request + +``` +Status: 400 Bad Request +``` + +This could be due to the `image` argument trying to access other parts of the filesystem by going to other directories (e.g. `../access/somewhere/dangerous.jpg`). + +##### Not Found + +``` +Status: 404 Not Found +``` + +This occurs when a given `image` parameter is for an image that does not actually exist. + +##### Internal Server Error + +``` +Status: 500 Internal Server Error +``` + +This error is an unexpected one, and a specific reason for the error to occur cannot be given. + +### Get a gallery of images + +``` +GET /gallery +``` + +#### Code Samples + +##### Shell + +```bash +curl http://localhost/v1/image/gallery +``` + +##### JavaScript + +- With `fetch` + +```js +fetch('http://localhost/v1/image/gallery'); +``` + +#### Responses + +##### Successful response + +``` +Status: 200 OK +``` + +The body is an HTML page with all of the images available enlisted. From 2d99bd1b6a778afbe977ace19af0a3f104cfdc3e Mon Sep 17 00:00:00 2001 From: Diana Belokon <67607236+dbelokon@users.noreply.github.com> Date: Tue, 19 Apr 2022 05:46:59 -0400 Subject: [PATCH 10/38] Resize stars depending on screen width --- src/web/app/src/components/StarField.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/web/app/src/components/StarField.tsx b/src/web/app/src/components/StarField.tsx index 9e0000b3ef..55a26ef002 100644 --- a/src/web/app/src/components/StarField.tsx +++ b/src/web/app/src/components/StarField.tsx @@ -1,7 +1,7 @@ import { NamedExoticComponent, useState, useEffect } from 'react'; import dynamic from 'next/dynamic'; import { request } from '@octokit/request'; -import { makeStyles } from '@material-ui/core/styles'; +import { makeStyles, Theme } from '@material-ui/core/styles'; import { P5Instance, P5WrapperProps } from 'react-p5-wrapper'; const ReactP5Wrapper = dynamic( @@ -17,10 +17,13 @@ const ReactP5Wrapper = dynamic( const P5_WRAPPER_ELEM_ID = 'p5-wrapper'; -const useStyles = makeStyles(() => { +const useStyles = makeStyles((theme: Theme) => { return { root: { width: '70vw', + [theme.breakpoints.down('xs')]: { + width: '90vw', + }, aspectRatio: '16 / 9', }, }; @@ -90,7 +93,7 @@ class Star { const sx = p5.map(x / z, 0, 1, 0, p5.width); const sy = p5.map(y / z, 0, 1, 0, p5.height); - const r = p5.map(this.z, 0, p5.width, 75, 0); + const r = p5.map(this.z, 0, p5.width, p5.width * 0.09, 0); p5.image(imageGraphic, sx, sy, r, r); } } From a3ccf04ee1de112cd0854842577bfacfbb910064 Mon Sep 17 00:00:00 2001 From: Diana Belokon <67607236+dbelokon@users.noreply.github.com> Date: Tue, 19 Apr 2022 04:17:27 -0400 Subject: [PATCH 11/38] Refactor copy button --- .../app/src/components/Posts/CopyButton.tsx | 101 +++++++++--------- .../app/src/components/Posts/ShareButton.tsx | 65 +++-------- src/web/app/src/components/Posts/Timeline.tsx | 21 +++- 3 files changed, 84 insertions(+), 103 deletions(-) diff --git a/src/web/app/src/components/Posts/CopyButton.tsx b/src/web/app/src/components/Posts/CopyButton.tsx index 2e6e46178f..aadb7f472c 100644 --- a/src/web/app/src/components/Posts/CopyButton.tsx +++ b/src/web/app/src/components/Posts/CopyButton.tsx @@ -1,52 +1,45 @@ -import { useState, CSSProperties, MouseEvent } from 'react'; -import { createStyles } from '@material-ui/core'; -import { makeStyles } from '@material-ui/core/styles'; -import Button from '@material-ui/core/IconButton'; -import Paper from '@material-ui/core/Paper'; -import FileCopyIcon from '@material-ui/icons/FileCopy'; -import { Transition } from 'react-transition-group'; -import clsx from 'clsx'; +import { useState, MouseEvent, AllHTMLAttributes } from 'react'; +import { Tooltip, IconButton, createStyles, Zoom } from '@material-ui/core'; +import { makeStyles, withStyles } from '@material-ui/core/styles'; +import CopyIcon from '@material-ui/icons/FileCopyOutlined'; +import Check from '@material-ui/icons/Check'; const useStyles = makeStyles(() => createStyles({ - copyButton: { - position: 'absolute', - right: 0, - padding: '1rem', - marginRight: '1.5rem', - transitionDuration: '0.2s', - transitionTimingFunction: 'ease', - cursor: 'pointer', - animation: 'fade-in-out 200ms both', + copy: { + fontSize: 'inherit', }, - icon: { - fontSize: '2rem', + check: { + fill: '#3fb950', + fontSize: 'inherit', }, - copyBadge: { - position: 'absolute', - top: '50%', - right: 0, - transitionProperty: 'transform, opacity', - transitionDuration: '0.2s', - transitionTimingFunction: 'ease', - borderRadius: '5px', - padding: '3px', + iconBtn: { + padding: '5px', + color: 'inherit', + fontSize: 'inherit', }, }) ); +const ButtonTooltip = withStyles({ + tooltip: { + fontSize: 'inherit', + margin: 0, + }, +})(Tooltip); + type CopyButtonProps = { onClick: (e: MouseEvent) => void; + beforeCopyMessage: string; + afterCopyMessage?: string; }; -const transition: { [state: string]: CSSProperties } = { - entered: { transform: 'translate(-70%, -50%)', opacity: 1 }, - entering: { transform: 'translate(0, -50%)', opacity: 0 }, - exited: { transform: `translate(0, -50%)`, opacity: 0 }, - exiting: { transform: `translate(0, -50%)`, opacity: 0 }, -}; - -const CopyButton = ({ onClick }: CopyButtonProps) => { +const CopyButton = ({ + onClick, + beforeCopyMessage, + afterCopyMessage, + ...allOtherProps +}: CopyButtonProps & AllHTMLAttributes) => { const [copied, setCopied] = useState(false); const classes = useStyles(); @@ -59,22 +52,26 @@ const CopyButton = ({ onClick }: CopyButtonProps) => { }, 2500); }; return ( - +
+ {!copied ? ( + + + + + + ) : ( + + + + + + )} +
); }; diff --git a/src/web/app/src/components/Posts/ShareButton.tsx b/src/web/app/src/components/Posts/ShareButton.tsx index 14e6954c48..f28f385995 100644 --- a/src/web/app/src/components/Posts/ShareButton.tsx +++ b/src/web/app/src/components/Posts/ShareButton.tsx @@ -1,32 +1,17 @@ -import { useState } from 'react'; -import { Tooltip, IconButton, createStyles, Zoom } from '@material-ui/core'; -import { makeStyles, Theme, withStyles } from '@material-ui/core/styles'; -import CopyIcon from '@material-ui/icons/FileCopyOutlined'; -import Check from '@material-ui/icons/Check'; +import { createStyles } from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import CopyButton from './CopyButton'; type Props = { url: string; }; -const ButtonTooltip = withStyles({ - tooltip: { - fontSize: '1.25rem', - margin: 0, - }, -})(Tooltip); - const useStyles = makeStyles((theme: Theme) => createStyles({ - copy: { - fill: theme.palette.primary.main, - }, - - check: { - fill: '#3fb950', - }, - - iconBtn: { - padding: '5px', + copyButton: { + color: theme.palette.primary.main, + fontSize: '1.25rem', + display: 'inline', }, }) ); @@ -34,34 +19,14 @@ const useStyles = makeStyles((theme: Theme) => const ShareButton = ({ url }: Props) => { const classes = useStyles(); - const [isCopiedToClipboard, setIsCopiedToClipboard] = useState(false); - - const copyToClipboardEvent = () => { - navigator.clipboard.writeText(url); - setIsCopiedToClipboard(true); - - setTimeout(() => { - setIsCopiedToClipboard(false); - }, 3000); - }; - - return !isCopiedToClipboard ? ( - - { - copyToClipboardEvent(); - }} - > - - - - ) : ( - - - - - + return ( + { + navigator.clipboard.writeText(url); + }} + beforeCopyMessage="Copy URL" + /> ); }; diff --git a/src/web/app/src/components/Posts/Timeline.tsx b/src/web/app/src/components/Posts/Timeline.tsx index 5ca4fb20c8..0aebf0215c 100644 --- a/src/web/app/src/components/Posts/Timeline.tsx +++ b/src/web/app/src/components/Posts/Timeline.tsx @@ -53,7 +53,26 @@ function removeButton(parent: HTMLElement) { parent.querySelectorAll('.copyCodeBtn').forEach((button) => button.remove()); } function createCopyButton(parent: HTMLElement, onClick: (e: MouseEvent) => void) { - render(, parent); + render( + , + parent + ); } function handleMouseMove(e: MouseEvent) { From e4b4b8bd7781faaa911e01b0824d6f143980bcbb Mon Sep 17 00:00:00 2001 From: Gerardo Enrique Arriaga Rendon <53304516+JerryHue@users.noreply.github.com> Date: Tue, 19 Apr 2022 12:02:23 -0400 Subject: [PATCH 12/38] Adapt star field to docusaurus (#3520) --- pnpm-lock.yaml | 4 + src/web/docusaurus/docs/overview.md | 8 + src/web/docusaurus/package.json | 8 +- .../docusaurus/src/components/StarField.jsx | 142 ++++++++++++++++++ .../src/components/StarField.module.css | 4 + 5 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 src/web/docusaurus/src/components/StarField.jsx create mode 100644 src/web/docusaurus/src/components/StarField.module.css diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9fe9b22bb..dcda5386d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -585,6 +585,7 @@ importers: '@docusaurus/preset-classic': 2.0.0-beta.17 '@docusaurus/types': 2.0.0-beta.17 '@mdx-js/react': 1.6.22 + '@octokit/request': 5.6.3 '@senecacdot/eslint-config-telescope': 1.1.0 '@types/react': 17.0.44 clsx: 1.1.1 @@ -595,6 +596,7 @@ importers: prism-react-renderer: 1.3.1 react: 17.0.2 react-dom: 17.0.2 + react-p5-wrapper: 3.1.0 typescript: 4.4.4 unist-util-visit: 2.0.0 dependencies: @@ -602,12 +604,14 @@ importers: '@docusaurus/plugin-content-docs': 2.0.0-beta.17_f315c2d88d281911b1fd83660389de05 '@docusaurus/preset-classic': 2.0.0-beta.17_e659d6b5e555996e015cdab3a346f7f4 '@mdx-js/react': 1.6.22_react@17.0.2 + '@octokit/request': 5.6.3 clsx: 1.1.1 mdx-mermaid: 1.2.2_86225b747a04048675421f37325d0553 mermaid: 8.13.8 prism-react-renderer: 1.3.1_react@17.0.2 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 + react-p5-wrapper: 3.1.0_react-dom@17.0.2+react@17.0.2 devDependencies: '@algolia/client-search': 4.9.1 '@docusaurus/types': 2.0.0-beta.17 diff --git a/src/web/docusaurus/docs/overview.md b/src/web/docusaurus/docs/overview.md index b961e51752..c3d5594136 100644 --- a/src/web/docusaurus/docs/overview.md +++ b/src/web/docusaurus/docs/overview.md @@ -2,8 +2,16 @@ sidebar_position: 0 --- +import StarField from '@site/src/components/StarField'; + # Telescope Overview +## Our Contributors + +The result of Telescope is thanks to the combined work of all these talented people (and robots). + + + ## Introduction One of the key features of Seneca's open source involvement has been the diff --git a/src/web/docusaurus/package.json b/src/web/docusaurus/package.json index 6a8e21f295..31ea9b8d09 100644 --- a/src/web/docusaurus/package.json +++ b/src/web/docusaurus/package.json @@ -22,12 +22,14 @@ "@docusaurus/plugin-content-docs": "2.0.0-beta.17", "@docusaurus/preset-classic": "2.0.0-beta.17", "@mdx-js/react": "1.6.22", + "@octokit/request": "5.6.3", "clsx": "1.1.1", "mdx-mermaid": "1.2.2", "mermaid": "8.13.8", "prism-react-renderer": "1.3.1", "react": "17.0.2", - "react-dom": "17.0.2" + "react-dom": "17.0.2", + "react-p5-wrapper": "3.1.0" }, "devDependencies": { "@algolia/client-search": "4.9.1", @@ -35,9 +37,9 @@ "@senecacdot/eslint-config-telescope": "1.1.0", "@types/react": "17.0.44", "eslint": "7.32.0", + "eslint-plugin-import": "2.25.4", "typescript": "4.4.4", - "unist-util-visit": "2.0.0", - "eslint-plugin-import": "2.25.4" + "unist-util-visit": "2.0.0" }, "browserslist": { "production": [ diff --git a/src/web/docusaurus/src/components/StarField.jsx b/src/web/docusaurus/src/components/StarField.jsx new file mode 100644 index 0000000000..c678f2e042 --- /dev/null +++ b/src/web/docusaurus/src/components/StarField.jsx @@ -0,0 +1,142 @@ +import React, { useState, useEffect } from 'react'; +import { request } from '@octokit/request'; +import { ReactP5Wrapper } from 'react-p5-wrapper'; +import styles from './StarField.module.css'; + +// The original idea of the process to calculate the position and size of the +// stars is from Daniel Shiffman (http://codingtra.in). +// The original video can be found here: https://youtu.be/17WoOqgXsRM + +const P5_WRAPPER_ELEM_ID = 'p5-wrapper'; + +class Star { + constructor(profileImage, p5) { + const side = 100; + const innerCircleRadius = side / 2; + + const starGraphic = p5.createGraphics(side, side); + starGraphic.image(profileImage, 0, 0, side, side); + + // Inner circle delineates the border of the picture + starGraphic.noFill(); + starGraphic.strokeWeight(1); + starGraphic.stroke('black'); + starGraphic.square(0, 0, side, innerCircleRadius); + + // Draw a bigger square that will erase the + // outside corners + const strokeWeight = innerCircleRadius * (p5.sqrt(2) - 1) * 2; + starGraphic.blendMode(starGraphic.REMOVE); + starGraphic.stroke(0, 0, 0, 255); + starGraphic.strokeWeight(strokeWeight); + starGraphic.square( + -strokeWeight / 2, + -strokeWeight / 2, + side + strokeWeight, + innerCircleRadius + strokeWeight / 2 + ); + + this.imageGraphic = starGraphic; + this.p5 = p5; + this.x = p5.random(-p5.width / 2, p5.width / 2); + this.y = p5.random(-p5.height / 2, p5.height / 2); + this.z = p5.random(p5.width * 0.1, p5.width * 0.95); + } + + update(speed) { + const { p5 } = this; + + this.z -= speed; + if (this.z < 1) { + this.x = p5.random(-p5.width / 2, p5.width / 2); + this.y = p5.random(-p5.height / 2, p5.height / 2); + this.z = p5.random(p5.width * 0.1, p5.width * 0.95); + } + } + + draw() { + const { p5, x, y, z, imageGraphic } = this; + + p5.fill(255); + p5.noStroke(); + + const sx = p5.map(x / z, 0, 1, 0, p5.width); + const sy = p5.map(y / z, 0, 1, 0, p5.height); + + const r = p5.map(this.z, 0, p5.width, p5.width * 0.09, 0); + p5.image(imageGraphic, sx, sy, r, r); + } +} + +const sketch = (p5) => { + const stars = []; + const speed = 3; + + p5.setup = () => { + const { width, height } = document.getElementById(P5_WRAPPER_ELEM_ID).getBoundingClientRect(); + p5.createCanvas(width, height); + }; + + p5.windowResized = () => { + const { width, height } = document.getElementById(P5_WRAPPER_ELEM_ID).getBoundingClientRect(); + p5.resizeCanvas(width, height); + }; + + p5.updateWithProps = (props) => { + if (props.contributorList) { + props.contributorList.forEach((urlImage) => { + p5.loadImage(urlImage, (profileImage) => { + stars.push(new Star(profileImage, p5)); + }); + }); + } + }; + + p5.draw = () => { + p5.background(0); + + p5.translate(p5.width / 2, p5.height / 2); + + p5.push(); + stars.forEach((star) => { + star.draw(); + star.update(speed); + }); + p5.pop(); + }; +}; + +const StarField = () => { + const [contributorList, setContributorList] = useState(['dummy']); + const [contributorPage, setContributorPage] = useState(1); + + useEffect(() => { + request('GET /repos/{owner}/{repo}/contributors{?page}', { + owner: 'Seneca-CDOT', + repo: 'telescope', + page: contributorPage, + }) + .then((res) => { + const contributorImages = res.data + .map((contributor) => contributor.avatar_url) + .filter((url) => url !== undefined); + + setContributorList(contributorImages); + + if (contributorImages.length > 0) { + setCOntributorPage(contributorPage + 1); + } + + return null; + }) + .catch((err) => console.error(err)); + }, [contributorPage]); + + return ( +
+ +
+ ); +}; + +export default StarField; diff --git a/src/web/docusaurus/src/components/StarField.module.css b/src/web/docusaurus/src/components/StarField.module.css new file mode 100644 index 0000000000..ac080b47b4 --- /dev/null +++ b/src/web/docusaurus/src/components/StarField.module.css @@ -0,0 +1,4 @@ +.p5Canvas { + width: 100%; + aspect-ratio: 16 / 9; +} From 0770ed4f186429e2b65332dc3ec6e7fba756dccc Mon Sep 17 00:00:00 2001 From: DukeManh Date: Mon, 18 Apr 2022 20:20:22 -0400 Subject: [PATCH 13/38] Reorganize status cards --- src/api/status/src/views/partials/commitCard.hbs | 4 ++-- src/api/status/src/views/partials/contributionCard.hbs | 3 +-- src/api/status/src/views/partials/jobCountCard.hbs | 5 ++--- src/api/status/src/views/partials/totalFeedCard.hbs | 5 ++--- src/api/status/src/views/partials/totalPostCard.hbs | 9 ++++----- src/api/status/src/views/status.hbs | 2 -- 6 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/api/status/src/views/partials/commitCard.hbs b/src/api/status/src/views/partials/commitCard.hbs index d4b1335db3..258333982e 100644 --- a/src/api/status/src/views/partials/commitCard.hbs +++ b/src/api/status/src/views/partials/commitCard.hbs @@ -2,11 +2,11 @@
+ class="icon icon-lg icon-shape bg-gradient-info shadow-info text-center border-radius-xl mt-n4 position-absolute"> commit
-
{{githubInfo.title}}
+
Commits

Yearly Commits

{{formatNumber githubInfo.yearlyCommits.commits}}

diff --git a/src/api/status/src/views/partials/contributionCard.hbs b/src/api/status/src/views/partials/contributionCard.hbs index ea5ca68844..710a5e0008 100644 --- a/src/api/status/src/views/partials/contributionCard.hbs +++ b/src/api/status/src/views/partials/contributionCard.hbs @@ -6,8 +6,7 @@ person
-
{{githubInfo.title}}
-

Contributors

+
GitHub Contributors

{{githubInfo.totalContributors}}

diff --git a/src/api/status/src/views/partials/jobCountCard.hbs b/src/api/status/src/views/partials/jobCountCard.hbs index d5ea54fb39..9bf53dba32 100644 --- a/src/api/status/src/views/partials/jobCountCard.hbs +++ b/src/api/status/src/views/partials/jobCountCard.hbs @@ -10,11 +10,10 @@ mt-n4 position-absolute "> - person + queue
-
Telescope
-

Jobs in queue

+
Jobs in queue

{{#if jobCount}}{{formatNumber jobCount}}{{else}}N/A{{/if}}

diff --git a/src/api/status/src/views/partials/totalFeedCard.hbs b/src/api/status/src/views/partials/totalFeedCard.hbs index a2fb79e0dd..d037ddc413 100644 --- a/src/api/status/src/views/partials/totalFeedCard.hbs +++ b/src/api/status/src/views/partials/totalFeedCard.hbs @@ -10,11 +10,10 @@ mt-n4 position-absolute "> - person + rss_feed
-
Telescope
-

Total feeds

+
RSS feeds

{{#if totalFeeds}}{{formatNumber totalFeeds}}{{else}}N/A{{/if}}

diff --git a/src/api/status/src/views/partials/totalPostCard.hbs b/src/api/status/src/views/partials/totalPostCard.hbs index 50a6296701..8012ddb321 100644 --- a/src/api/status/src/views/partials/totalPostCard.hbs +++ b/src/api/status/src/views/partials/totalPostCard.hbs @@ -3,18 +3,17 @@
- person + menu_book
-
Telescope
-

Total posts

+
Published posts

{{#if totalPost}}{{formatNumber totalPost}}{{else}}N/A{{/if}}

diff --git a/src/api/status/src/views/status.hbs b/src/api/status/src/views/status.hbs index 820cacaad0..61d0c1b73f 100644 --- a/src/api/status/src/views/status.hbs +++ b/src/api/status/src/views/status.hbs @@ -35,8 +35,6 @@
{{> commitCard githubInfo=telescope}} {{> contributionCard githubInfo=telescope}} -
-
{{> totalPostCard}} {{> totalFeedCard}} {{> jobCountCard}} From 2753ed170e9f3efa32b06e89db1b331e332bc942 Mon Sep 17 00:00:00 2001 From: DukeManh Date: Mon, 18 Apr 2022 23:25:06 -0400 Subject: [PATCH 14/38] Add status cards gap --- src/api/status/public/css/telescope-dashboard.css | 4 ++++ src/api/status/src/views/partials/commitCard.hbs | 2 +- src/api/status/src/views/partials/contributionCard.hbs | 4 ++-- src/api/status/src/views/partials/jobCountCard.hbs | 2 +- src/api/status/src/views/partials/totalFeedCard.hbs | 2 +- src/api/status/src/views/partials/totalPostCard.hbs | 4 ++-- src/api/status/src/views/status.hbs | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/api/status/public/css/telescope-dashboard.css b/src/api/status/public/css/telescope-dashboard.css index 6f9ad40a6c..5d09d04bd8 100644 --- a/src/api/status/public/css/telescope-dashboard.css +++ b/src/api/status/public/css/telescope-dashboard.css @@ -6,3 +6,7 @@ .card-header--telescope { margin-bottom: auto; } + +#status-cards { + gap: 1.5rem 0; +} diff --git a/src/api/status/src/views/partials/commitCard.hbs b/src/api/status/src/views/partials/commitCard.hbs index 258333982e..995fc0a254 100644 --- a/src/api/status/src/views/partials/commitCard.hbs +++ b/src/api/status/src/views/partials/commitCard.hbs @@ -1,4 +1,4 @@ -
+
+
person
-
GitHub Contributors
+
Contributors

{{githubInfo.totalContributors}}

diff --git a/src/api/status/src/views/partials/jobCountCard.hbs b/src/api/status/src/views/partials/jobCountCard.hbs index 9bf53dba32..f8491e4e38 100644 --- a/src/api/status/src/views/partials/jobCountCard.hbs +++ b/src/api/status/src/views/partials/jobCountCard.hbs @@ -1,4 +1,4 @@ -
+
+
+
menu_book
-
Published posts
+
Total Posts

{{#if totalPost}}{{formatNumber totalPost}}{{else}}N/A{{/if}}

diff --git a/src/api/status/src/views/status.hbs b/src/api/status/src/views/status.hbs index 61d0c1b73f..9a29cc568b 100644 --- a/src/api/status/src/views/status.hbs +++ b/src/api/status/src/views/status.hbs @@ -32,7 +32,7 @@
-
+
{{> commitCard githubInfo=telescope}} {{> contributionCard githubInfo=telescope}} {{> totalPostCard}} From 686770e4c3150db60e636c7783dd487e889701fc Mon Sep 17 00:00:00 2001 From: Gerardo Arriaga Rendon Date: Tue, 19 Apr 2022 12:42:35 -0400 Subject: [PATCH 15/38] Import p5.js dynamically on docs --- src/web/docusaurus/src/components/StarField.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/web/docusaurus/src/components/StarField.jsx b/src/web/docusaurus/src/components/StarField.jsx index c678f2e042..2ab82bcbe5 100644 --- a/src/web/docusaurus/src/components/StarField.jsx +++ b/src/web/docusaurus/src/components/StarField.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; +import BrowserOnly from '@docusaurus/BrowserOnly'; import { request } from '@octokit/request'; -import { ReactP5Wrapper } from 'react-p5-wrapper'; import styles from './StarField.module.css'; // The original idea of the process to calculate the position and size of the @@ -133,9 +133,16 @@ const StarField = () => { }, [contributorPage]); return ( -
- -
+ + {() => { + const ReactP5Wrapper = require('react-p5-wrapper').ReactP5Wrapper; + return ( +
+ +
+ ); + }} +
); }; From bee285a87c87b6f66ab7bbe33fa768e9e954f9cc Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Tue, 19 Apr 2022 09:38:39 -0400 Subject: [PATCH 16/38] Refactor front-end to use @senecacdot/github-url-parser --- pnpm-lock.yaml | 6 + src/web/app/package.json | 9 +- .../src/components/GenericInfoProvider.tsx | 7 +- src/web/app/src/components/GitHubInfo.tsx | 107 ------------------ src/web/app/src/githubReservedName.ts | 27 ----- src/web/app/src/hooks/use-genericInfo.ts | 6 +- 6 files changed, 18 insertions(+), 144 deletions(-) delete mode 100644 src/web/app/src/components/GitHubInfo.tsx delete mode 100644 src/web/app/src/githubReservedName.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcda5386d4..93e80499b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -493,6 +493,7 @@ importers: '@mui/styles': 5.5.1 '@octokit/request': 5.6.3 '@senecacdot/eslint-config-telescope': 1.1.0 + '@senecacdot/github-url-parser': 2.0.0 '@supabase/supabase-js': 1.29.4 '@testing-library/react': 12.1.4 '@types/node': 16.11.26 @@ -539,6 +540,7 @@ importers: '@mui/material': 5.5.2_f336e8f880f1224828908f4ff3e69012 '@mui/styles': 5.5.1_c8e45b4eb687790dba17b4e1c4b4273f '@octokit/request': 5.6.3 + '@senecacdot/github-url-parser': 2.0.0 '@supabase/supabase-js': 1.29.4 '@testing-library/react': 12.1.4_react-dom@17.0.2+react@17.0.2 '@types/smoothscroll-polyfill': 0.3.1 @@ -6004,6 +6006,10 @@ packages: eslint: 7.32.0 dev: true + /@senecacdot/github-url-parser/2.0.0: + resolution: {integrity: sha512-vzlEkMdYsTlrTNbOsrvnYK04/DX0NkyZPl/pm42+pOO0M03uC1GbyjrMVLA09n84AqLbEVnd4NehbMz/ji2Jgg==} + dev: false + /@senecacdot/satellite/1.27.0: resolution: {integrity: sha512-Eq8g6dKMYRV2EDEcYApodHOlqOcYV0kVKqMF+DJZsGgFJjQFXYqrLhc2zQ+3Ms9AbjjhL3rso9GAuRxsy5A+zQ==} engines: {node: '>=14.0.0', pnpm: '>=6'} diff --git a/src/web/app/package.json b/src/web/app/package.json index a1ccf84044..4d44f3a28a 100644 --- a/src/web/app/package.json +++ b/src/web/app/package.json @@ -15,7 +15,6 @@ "dependencies": { "@emotion/react": "11.8.2", "@emotion/styled": "11.8.1", - "@testing-library/react": "12.1.4", "@fontsource/pt-serif": "4.5.3", "@fontsource/spartan": "4.5.4", "@material-ui/core": "4.12.3", @@ -24,6 +23,9 @@ "@mui/material": "5.5.2", "@mui/styles": "5.5.1", "@octokit/request": "5.6.3", + "@senecacdot/github-url-parser": "2.0.0", + "@supabase/supabase-js": "1.29.4", + "@testing-library/react": "12.1.4", "@types/smoothscroll-polyfill": "0.3.1", "@types/yup": "0.29.13", "clsx": "1.1.1", @@ -33,7 +35,6 @@ "i18next": "21.6.14", "i18next-browser-languagedetector": "6.1.4", "jwt-decode": "3.1.2", - "@supabase/supabase-js": "1.29.4", "nanoid": "3.2.0", "next": "12.1.4", "next-compose-plugins": "2.2.1", @@ -53,10 +54,10 @@ "devDependencies": { "@senecacdot/eslint-config-telescope": "1.1.0", "@testing-library/react": "12.1.4", - "@typescript-eslint/eslint-plugin": "4.33.0", - "@typescript-eslint/parser": "4.33.0", "@types/node": "16.11.26", "@types/react": "17.0.44", + "@typescript-eslint/eslint-plugin": "4.33.0", + "@typescript-eslint/parser": "4.33.0", "babel-loader": "8.2.4", "eslint": "7.32.0", "terser-webpack-plugin": "5.3.1", diff --git a/src/web/app/src/components/GenericInfoProvider.tsx b/src/web/app/src/components/GenericInfoProvider.tsx index ee9a924670..5fabdff67d 100644 --- a/src/web/app/src/components/GenericInfoProvider.tsx +++ b/src/web/app/src/components/GenericInfoProvider.tsx @@ -1,10 +1,11 @@ import { createContext, useMemo, ReactNode } from 'react'; +import { parseGitHubUrls, GitHubInfo } from '@senecacdot/github-url-parser'; + import { Post } from '../interfaces'; -import { GitHubInfoContextInterface, extractGitHubInfo } from './GitHubInfo'; import { YouTubeInfoContextInterface, extractYouTubeInfo } from './YouTubeInfo'; type GenericInfoContextInterface = { - gitHubInfo: GitHubInfoContextInterface; + gitHubInfo: GitHubInfo; youTubeInfo: YouTubeInfoContextInterface; }; @@ -31,7 +32,7 @@ type Props = { const GenericInfoProvider = ({ children, post }: Props) => { const genericInfo = useMemo(() => { return { - gitHubInfo: extractGitHubInfo(post), + gitHubInfo: parseGitHubUrls(post.html), youTubeInfo: extractYouTubeInfo(post), }; }, [post]); diff --git a/src/web/app/src/components/GitHubInfo.tsx b/src/web/app/src/components/GitHubInfo.tsx deleted file mode 100644 index a179ddc7e0..0000000000 --- a/src/web/app/src/components/GitHubInfo.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Post } from '../interfaces'; -import githubReservedNames from '../githubReservedName'; - -export interface GitHubInfoContextInterface { - issues: string[]; - pullRequests: string[]; - repos: string[]; - commits: string[]; - users: string[]; -} - -const extractGitHubUrlsFromPost = (htmlString: string): string[] => { - const parser = new DOMParser(); - const postDoc = parser.parseFromString(htmlString, 'text/html'); - - const allGithubLinks = Array.from( - // all links that have href starts with 'https://github.com' - postDoc.querySelectorAll("a[href^='https://github.com']"), - (element) => (element as HTMLAnchorElement).href - ); - - // unique links only - return allGithubLinks.reduce( - (acc: string[], element) => (acc.includes(element) ? acc : [...acc, element]), - [] - ); -}; - -const parseGitHubUrl = (url: string): URL | null => { - try { - const ghUrl = new URL(url); - if (ghUrl.hostname !== 'github.com') { - return null; - } - return ghUrl; - } catch (err) { - return null; - } -}; - -const fetchGitHubUrls = (extractedGitHubUrls: string[]): GitHubInfoContextInterface => { - const issues: Set = new Set(); - const pullRequests: Set = new Set(); - const repos: Set = new Set(); - const commits: Set = new Set(); - const users: Set = new Set(); - - const ghUrls = ( - extractedGitHubUrls.map((url) => parseGitHubUrl(url)).filter((url) => url !== null) as URL[] - ).filter((url) => !githubReservedNames.includes(url.pathname.split('/').slice(1, 2)[0])); - - ghUrls.forEach((url) => { - const { pathname } = url; - - // Match urls that start with / and optionally end with / or //// - // can be number, or a mixed of 40 alphanumeric (commit id) - // Ex: /Seneca-CDOT/telescope/pull/2367 ✅ - // Ex: /Seneca-CDOT/telescope ✅ - // Ex: /Seneca-CDOT/telescope/pull/2367/commits/d3fagd3fagd3fagd3fagd3fagd3fag4d41265748 ✅ - // Ex: /Seneca-CDOT/telescope/issues ✅ - const matches = - /^\/(?[^/]+)(\/(?[^/]+)((\/(.*))?(\/(?[^/]+)?\/(?(\d+|\w{40}))\/?$))?)?/gi.exec( - pathname - ); - - if (matches?.groups) { - const { type, user, repo } = matches.groups; - - // if repo defined add to repos - if (repo) { - const repoUrl = `${user}/${repo}`; - repos.add(repoUrl); - } - users.add(user); - switch (type?.toLowerCase()) { - case 'pull': - pullRequests.add(pathname); - break; - - case 'issues': - issues.add(pathname); - break; - - case 'commit': - case 'commits': - commits.add(pathname); - break; - - default: - break; - } - } - }); - - return { - repos: Array.from(repos), - issues: Array.from(issues), - pullRequests: Array.from(pullRequests), - commits: Array.from(commits), - users: Array.from(users), - }; -}; - -export const extractGitHubInfo = (post: Post): GitHubInfoContextInterface => { - const gitHubUrls = extractGitHubUrlsFromPost(post.html); - return fetchGitHubUrls(gitHubUrls); -}; diff --git a/src/web/app/src/githubReservedName.ts b/src/web/app/src/githubReservedName.ts deleted file mode 100644 index de4796fd00..0000000000 --- a/src/web/app/src/githubReservedName.ts +++ /dev/null @@ -1,27 +0,0 @@ -export default [ - 'team', - 'enterprise', - 'explore', - 'marketplace', - 'pricing', - 'topics', - 'collections', - 'trending', - 'readme', - 'events', - 'sponsors', - 'mobile', - 'features', - 'customer-stories', - 'pulls', - 'issues', - 'notifications', - 'new', - 'organizations', - 'users', - 'settings', - 'discussions', - 'account', - 'about', - 'security', -]; diff --git a/src/web/app/src/hooks/use-genericInfo.ts b/src/web/app/src/hooks/use-genericInfo.ts index 6fa8af44e5..6ff9e70faa 100644 --- a/src/web/app/src/hooks/use-genericInfo.ts +++ b/src/web/app/src/hooks/use-genericInfo.ts @@ -1,9 +1,9 @@ import { useContext } from 'react'; -import { GitHubInfoContextInterface } from '../components/GitHubInfo'; +import { GitHubInfo } from '@senecacdot/github-url-parser'; + import { YouTubeInfoContextInterface } from '../components/YouTubeInfo'; import { GenericInfoContext } from '../components/GenericInfoProvider'; -export const useGithubInfo = (): GitHubInfoContextInterface => - useContext(GenericInfoContext).gitHubInfo; +export const useGithubInfo = (): GitHubInfo => useContext(GenericInfoContext).gitHubInfo; export const useYouTubeInfo = (): YouTubeInfoContextInterface => useContext(GenericInfoContext).youTubeInfo; From 30afc09116c447f53c1fe975e7cb482d9cbd919b Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Tue, 19 Apr 2022 14:01:46 -0400 Subject: [PATCH 17/38] Remove TIMING=1 from lint. Add separate lint-time (#3511) --- package.json | 4 +++- src/api/dependency-discovery/package.json | 4 +++- src/api/feed-discovery/package.json | 1 + src/api/image/package.json | 4 +++- src/api/parser/package.json | 4 +++- src/api/planet/package.json | 6 ++++-- src/api/posts/package.json | 4 +++- src/api/search/package.json | 4 +++- src/api/sso/package.json | 1 + src/api/status/package.json | 6 ++++-- src/github-url-parser/package.json | 2 ++ src/mobile/package.json | 4 +++- src/satellite/package.json | 6 ++++-- src/web/app/package.json | 4 +++- src/web/docusaurus/package.json | 4 +++- turbo.json | 3 +++ 16 files changed, 46 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 3b901ba69e..a17ef688c7 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "build": "pnpm turbo run build", "dev": "pnpm dev --prefix src/web/app --", "develop": "pnpm dev", - "eslint": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./src/backend/**/*.js\" \"./test/**/*.js\"", + "eslint": "eslint --config .eslintrc.js --ignore-path .gitignore \"./src/backend/**/*.js\" \"./test/**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./src/backend/**/*.js\" \"./test/**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js --ignore-path .gitignore \"./src/backend/**/*.js\" \"./test/**/*.js\" --fix", "lint": "pnpm turbo run lint && pnpm eslint", + "lint-time": "pnpm turbo run lint-time && pnpm eslint-time", "clean": "pnpm turbo run clean && pnpm -r exec rm -rf node_modules", "prettier": "prettier --write \"./**/*.{md,jsx,json,html,css,js,yml,ts,tsx}\"", "prettier-check": "prettier --check \"./**/*.{md,jsx,json,html,css,js,yml,ts,tsx}\"", diff --git a/src/api/dependency-discovery/package.json b/src/api/dependency-discovery/package.json index fe7cf1688f..2a06e7404f 100644 --- a/src/api/dependency-discovery/package.json +++ b/src/api/dependency-discovery/package.json @@ -7,7 +7,9 @@ "start": "node src/server.js", "dev": "env-cmd -f env.local nodemon src/server.js", "lint": "pnpm eslint", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"**/*.js\"", + "lint-time": "pnpm eslint-time", + "eslint": "eslint --config .eslintrc.js \"**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js \"**/*.js\" --fix", "test": "jest -c jest.config.js" }, diff --git a/src/api/feed-discovery/package.json b/src/api/feed-discovery/package.json index b263d45f04..a42e351cd8 100644 --- a/src/api/feed-discovery/package.json +++ b/src/api/feed-discovery/package.json @@ -10,6 +10,7 @@ "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\" ", "eslint-fix": "eslint --config .eslintrc.js \"./**/*.js\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js" }, "repository": "Seneca-CDOT/telescope", diff --git a/src/api/image/package.json b/src/api/image/package.json index 6b5da5ab52..b370af845f 100644 --- a/src/api/image/package.json +++ b/src/api/image/package.json @@ -8,7 +8,9 @@ "start": "node src/server.js", "clean": "find ./photos -type f -not -name 'default.jpg' -delete", "lint": "pnpm eslint", - "eslint": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\"", + "lint-time": "pnpm eslint-time", + "eslint": "eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\" --fix", "test": "jest -c jest.config.js" }, diff --git a/src/api/parser/package.json b/src/api/parser/package.json index a5dc39e05a..1458ae605a 100644 --- a/src/api/parser/package.json +++ b/src/api/parser/package.json @@ -6,9 +6,11 @@ "scripts": { "dev": "env-cmd -f env.local node src/index.js", "start": "node src/index.js", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\"", + "eslint": "eslint --config .eslintrc.js \"./**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js \"./**/*.js\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js" }, "repository": "Seneca-CDOT/telescope", diff --git a/src/api/planet/package.json b/src/api/planet/package.json index 453654dff1..c1ac3f42d2 100644 --- a/src/api/planet/package.json +++ b/src/api/planet/package.json @@ -6,9 +6,11 @@ "scripts": { "dev": "env-cmd -f env.local nodemon src/server.js", "start": "node src/server.js", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\" ", + "eslint": "eslint --config .eslintrc.js \"./**/*.js\" ", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\" ", "eslint-fix": "eslint --config .eslintrc.js \"./**/*.js\" --fix", - "lint": "pnpm eslint" + "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time" }, "repository": "Seneca-CDOT/telescope", "license": "BSD-2-Clause", diff --git a/src/api/posts/package.json b/src/api/posts/package.json index 5b5ccf1f3b..57d7ababc2 100644 --- a/src/api/posts/package.json +++ b/src/api/posts/package.json @@ -5,9 +5,11 @@ "description": "A service for retrieving posts", "scripts": { "start": "node src/server.js", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\"", + "eslint": "eslint --config .eslintrc.js \"./**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js \"./**/*.js\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js" }, "repository": "Seneca-CDOT/telescope", diff --git a/src/api/search/package.json b/src/api/search/package.json index b19d7e21fc..7cced137f4 100644 --- a/src/api/search/package.json +++ b/src/api/search/package.json @@ -6,9 +6,11 @@ "scripts": { "dev": "nodemon src/server.js", "start": "node src/server.js", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"./**/**/*.js\"", + "eslint": "eslint --config .eslintrc.js \"./**/**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./**/**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js \"./**/**/*.js\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js" }, "repository": "Seneca-CDOT/telescope", diff --git a/src/api/sso/package.json b/src/api/sso/package.json index 7d87ad2fb4..b74b42fbe6 100644 --- a/src/api/sso/package.json +++ b/src/api/sso/package.json @@ -10,6 +10,7 @@ "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js \"./**/*.js\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js" }, "repository": "Seneca-CDOT/telescope", diff --git a/src/api/status/package.json b/src/api/status/package.json index f9c5e89375..d635747b78 100644 --- a/src/api/status/package.json +++ b/src/api/status/package.json @@ -11,9 +11,11 @@ "watch:server": "env-cmd -f env.local nodemon src/server.js", "compile:js": "vite build", "watch:js": "vite build --watch", - "eslint": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./src/**/*.js\"", + "eslint": "eslint --config .eslintrc.js --ignore-path .gitignore \"./src/**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./src/**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js --ignore-path .gitignore \"./src/**/*.js\" --fix", - "lint": "pnpm eslint" + "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time" }, "repository": "Seneca-CDOT/telescope", "license": "BSD-2-Clause", diff --git a/src/github-url-parser/package.json b/src/github-url-parser/package.json index 0ceaeb9b46..77af6539fe 100644 --- a/src/github-url-parser/package.json +++ b/src/github-url-parser/package.json @@ -12,8 +12,10 @@ "jest": "jest -c jest.config.js", "coverage": "jest -c jest.config.js --collect-coverage", "eslint": "eslint --config .eslintrc.js \"./src/**/*\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./src/**/*\"", "eslint-fix": "eslint --config .eslintrc.js \"./src/**/*\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "watch": "parcel watch", "build": "parcel build", "prepublish": "pnpm build" diff --git a/src/mobile/package.json b/src/mobile/package.json index 026ddd4dfa..37eb40b49b 100644 --- a/src/mobile/package.json +++ b/src/mobile/package.json @@ -8,9 +8,11 @@ "ios": "expo start --ios", "web": "expo start --web", "eject": "expo eject", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"**/*.{js,jsx}\"", + "eslint": "eslint --config .eslintrc.js \"**/*.{js,jsx}\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"**/*.{js,jsx}\"", "eslint-fix": "eslint --config .eslintrc.js \"**/*.{js,jsx}\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js" }, "dependencies": { diff --git a/src/satellite/package.json b/src/satellite/package.json index 962e94f84f..f5c15d5f4a 100644 --- a/src/satellite/package.json +++ b/src/satellite/package.json @@ -7,9 +7,11 @@ "test:watch": "jest --watch", "test": "jest -c jest.config.js", "coverage": "jest -c jest.config.js --collect-coverage", - "eslint": "TIMING=1 eslint --config .eslintrc.js \"./src/**/*.js\"", + "eslint": "eslint --config .eslintrc.js \"./src/**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js \"./src/**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js \"./src/**/*.js\" --fix", - "lint": "pnpm eslint" + "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time" }, "repository": "Seneca-CDOT/telescope", "license": "BSD-2-Clause", diff --git a/src/web/app/package.json b/src/web/app/package.json index 4d44f3a28a..388bd55009 100644 --- a/src/web/app/package.json +++ b/src/web/app/package.json @@ -5,9 +5,11 @@ "build": "next build && next export", "dev": "next dev -p 8000", "start": "next start -p 8000", - "eslint": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.{js,jsx,ts,tsx}\"", + "eslint": "eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.{js,jsx,ts,tsx}\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.{js,jsx,ts,tsx}\"", "eslint-fix": "eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.{js,jsx,ts,tsx}\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "test": "jest -c jest.config.js", "clean": "rm -rf .next .turbo out" }, diff --git a/src/web/docusaurus/package.json b/src/web/docusaurus/package.json index 31ea9b8d09..88ac7257f4 100644 --- a/src/web/docusaurus/package.json +++ b/src/web/docusaurus/package.json @@ -10,9 +10,11 @@ "deploy": "docusaurus deploy", "clear": "docusaurus clear", "serve": "docusaurus serve --port 4631 --host 0.0.0.0", - "eslint": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\"", + "eslint": "eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\"", + "eslint-time": "TIMING=1 eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\"", "eslint-fix": "eslint --config .eslintrc.js --ignore-path .gitignore \"./**/*.js\" --fix", "lint": "pnpm eslint", + "lint-time": "pnpm eslint-time", "clean": "rm -rf .docusaurus .turbo build", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids" diff --git a/turbo.json b/turbo.json index 897c1f24ff..ea1928dd47 100644 --- a/turbo.json +++ b/turbo.json @@ -26,6 +26,9 @@ "lint": { "outputs": [] }, + "lint-time": { + "outputs": [] + }, "clean": { "cache": false } From 122e152b3690649c226e6b84c7f43f16de1fde61 Mon Sep 17 00:00:00 2001 From: DukeManh Date: Mon, 18 Apr 2022 18:24:05 -0400 Subject: [PATCH 18/38] Improve Autocompete stylings --- src/web/app/src/components/SearchBar.tsx | 6 +- .../components/SearchInput/AuthorInput.tsx | 86 +++++++++++++++---- .../components/SearchInput/SearchInput.tsx | 18 +++- 3 files changed, 86 insertions(+), 24 deletions(-) diff --git a/src/web/app/src/components/SearchBar.tsx b/src/web/app/src/components/SearchBar.tsx index 6fa1dfbad3..70c20a5c8a 100644 --- a/src/web/app/src/components/SearchBar.tsx +++ b/src/web/app/src/components/SearchBar.tsx @@ -12,8 +12,8 @@ const useStyles = makeStyles((theme: Theme) => maxWidth: '785px', marginLeft: 'auto', marginRight: 'auto', - padding: theme.spacing(2, 2, 0, 2), - marginBottom: theme.spacing(8), + padding: theme.spacing(4, 2, 0, 2), + marginBottom: theme.spacing(12), }, card: { padding: theme.spacing(0, 0, 0, 2), @@ -107,7 +107,7 @@ const useStyles = makeStyles((theme: Theme) => }, }, advancedSearchInputDiv: { - margin: '15px 0px', + margin: '2.5rem 0px', }, searchButton: { float: 'right', diff --git a/src/web/app/src/components/SearchInput/AuthorInput.tsx b/src/web/app/src/components/SearchInput/AuthorInput.tsx index d2ef5e542d..265b1d286d 100644 --- a/src/web/app/src/components/SearchInput/AuthorInput.tsx +++ b/src/web/app/src/components/SearchInput/AuthorInput.tsx @@ -1,26 +1,69 @@ -import { TextField } from '@material-ui/core'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import CloseIcon from '@material-ui/icons/Close'; import { Autocomplete } from '@mui/material'; +import TextField from '@material-ui/core/TextField'; import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { searchServiceUrl } from '../../config'; -const useStyles = makeStyles((theme: Theme) => +type AutocompleteStyleProps = { + isListOpen: boolean; +}; + +const useStyles = makeStyles((theme) => createStyles({ input: { - fontSize: '1.2rem', + fontSize: '1.4rem', color: theme.palette.text.primary, - borderRadius: `4rem`, + borderRadius: ({ isListOpen }) => (isListOpen ? '2rem 2rem 0 0' : '4rem'), borderColor: theme.palette.info.main, - borderWidth: `2px`, + borderWidth: '2px', + padding: '1rem !important', + '&:hover fieldset': { + borderColor: `${theme.palette.info.main} !important`, + }, + '&.Mui-focused fieldset': { + borderColor: `${theme.palette.info.main} !important`, + }, + '& fieldset': { + borderColor: theme.palette.info.main, + borderWidth: '2px', + borderBottom: ({ isListOpen }) => + isListOpen ? `none` : `2px solid ${theme.palette.info.main}`, + }, + }, + label: { + color: `${theme.palette.text.secondary} !important`, + fontSize: '1.4rem', + }, + popper: { + boxSizing: 'border-box', + borderWidth: '1px 2px 2px', + borderColor: ({ isListOpen }) => (isListOpen ? theme.palette.info.main : 'transparent'), + borderStyle: 'solid', + + overflow: 'hidden', + borderRadius: '0 0 2rem 2rem', + '& > div': { + backgroundColor: theme.palette.background.default, + borderRadius: 0, + }, }, listbox: { - fontSize: '1.2rem', color: theme.palette.text.primary, - backgroundColor: theme.palette.background.paper, - '& :hover': { - color: theme.palette.text.secondary, - backgroundColor: theme.palette.background.default, - border: '2px solid', + borderRadius: 0, + fontFamily: 'inherit', + fontSize: '1.4rem', + }, + clear: { + color: theme.palette.text.primary, + '& svg': { + fontSize: '2rem', + }, + }, + option: { + borderRadius: '3px', + '&.Mui-focused': { + backgroundColor: `${theme.palette.background.paper} !important`, }, }, }) @@ -50,8 +93,11 @@ interface AuthorInputInterface { } const AuthorInput = ({ text, setText, labelFor }: AuthorInputInterface) => { - const cs = useStyles(); + const [isListOpen, setIsListOpen] = useState(false); const [options, setOptions] = useState<[{ author: string; highlight: string }]>(); + const cs = useStyles({ + isListOpen: isListOpen && !!options?.length, + }); useEffect(() => { // debounce so it searches every 0.5 seconds, instead of on every stroke @@ -70,13 +116,15 @@ const AuthorInput = ({ text, setText, labelFor }: AuthorInputInterface) => { return () => clearTimeout(debounce); }, [text]); - // mui Autocomplete component https://mui.com/components/autocomplete/ return ( (typeof option === 'string' ? option : option.author)} options={options || []} + onOpen={() => setIsListOpen(true)} + onClose={() => setIsListOpen(false)} + open={isListOpen} // disable built-in filtering for search as you type // https://mui.com/components/autocomplete/#search-as-you-type filterOptions={(x) => x} @@ -85,8 +133,12 @@ const AuthorInput = ({ text, setText, labelFor }: AuthorInputInterface) => { setText(newInputValue); }} fullWidth + clearIcon={} classes={{ + popper: cs.popper, listbox: cs.listbox, + endAdornment: cs.clear, + option: cs.option, }} renderInput={(params) => ( { variant="outlined" size="medium" fullWidth + color="primary" label={labelFor} InputProps={{ ...params.InputProps, - type: 'search', + type: 'text', classes: { root: cs.input, focused: cs.input, - notchedOutline: cs.input, }, }} InputLabelProps={{ classes: { - root: cs.input, - focused: cs.input, + root: cs.label, + focused: cs.label, }, }} /> diff --git a/src/web/app/src/components/SearchInput/SearchInput.tsx b/src/web/app/src/components/SearchInput/SearchInput.tsx index a87039a6b0..1568c2a578 100644 --- a/src/web/app/src/components/SearchInput/SearchInput.tsx +++ b/src/web/app/src/components/SearchInput/SearchInput.tsx @@ -19,13 +19,23 @@ const useStyles = makeStyles((theme: Theme) => transition: theme.transitions.create(['background-color', 'border-color'], { duration: '.5s', }), - fontSize: '14px', + fontSize: '1.4rem', display: 'block', color: theme.palette.text.primary, + '&:hover fieldset': { + borderColor: `${theme.palette.info.main} !important`, + }, + '&.Mui-focused fieldset': { + borderColor: `${theme.palette.info.main} !important`, + }, + }, + label: { + color: `${theme.palette.text.secondary} !important`, + fontSize: '1.4rem', }, customInputText: { color: theme.palette.text.primary, - fontSize: '14px', + fontSize: '1.4rem', }, iconButton: { backgroundColor: theme.palette.info.main, @@ -83,8 +93,8 @@ const SearchInput = ({ text, setText, labelFor, clickEvent, onEnterKey }: Search }} InputLabelProps={{ classes: { - root: classes.customInputText, - focused: classes.customInputText, + root: classes.label, + focused: classes.label, }, }} onKeyDown={(event) => { From dd7a46e60beb0841d87aa8e6c0782239a5e94e45 Mon Sep 17 00:00:00 2001 From: DukeManh Date: Tue, 19 Apr 2022 08:26:56 -0400 Subject: [PATCH 19/38] Add boxshadows, use useSWR and useDebounce --- .../components/SearchInput/AuthorInput.tsx | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/web/app/src/components/SearchInput/AuthorInput.tsx b/src/web/app/src/components/SearchInput/AuthorInput.tsx index 265b1d286d..2d5dccde4e 100644 --- a/src/web/app/src/components/SearchInput/AuthorInput.tsx +++ b/src/web/app/src/components/SearchInput/AuthorInput.tsx @@ -1,10 +1,17 @@ +import useSWR from 'swr'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import CloseIcon from '@material-ui/icons/Close'; import { Autocomplete } from '@mui/material'; import TextField from '@material-ui/core/TextField'; -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { Dispatch, SetStateAction, useState } from 'react'; +import { useDebounce } from 'react-use'; import { searchServiceUrl } from '../../config'; +type AutocompleteOption = { + author: string; + highlight: string; +}; + type AutocompleteStyleProps = { isListOpen: boolean; }; @@ -29,6 +36,8 @@ const useStyles = makeStyles((theme) => borderWidth: '2px', borderBottom: ({ isListOpen }) => isListOpen ? `none` : `2px solid ${theme.palette.info.main}`, + boxShadow: ({ isListOpen }) => + isListOpen ? `${theme.palette.info.main} 0px 1px 4px` : 'none', }, }, label: { @@ -43,6 +52,8 @@ const useStyles = makeStyles((theme) => overflow: 'hidden', borderRadius: '0 0 2rem 2rem', + boxShadow: ({ isListOpen }) => + isListOpen ? `${theme.palette.info.main} 0px 1px 4px` : 'none', '& > div': { backgroundColor: theme.palette.background.default, borderRadius: 0, @@ -69,52 +80,42 @@ const useStyles = makeStyles((theme) => }) ); -const fetchResults = async (url: string | null) => { - if (url) { - try { +interface AuthorInputInterface { + text: string; + setText: Dispatch>; + labelFor: string; +} + +const AuthorInput = ({ text, setText, labelFor }: AuthorInputInterface) => { + const [isListOpen, setIsListOpen] = useState(false); + const [author, setAuthor] = useState(text); + + const shouldFetch = () => author.length > 0; + const prepareUrl = () => `${searchServiceUrl}/authors/autocomplete/?author=${text}`; + const { data: options } = useSWR( + shouldFetch() ? prepareUrl() : null, + async (url) => { const res = await fetch(url); if (res.ok) { const results = await res.json(); // return up to first 10 results return results.res.slice(0, 10); } - } catch (error) { - console.error(error); return []; } - } - return []; -}; - -interface AuthorInputInterface { - text: string; - setText: Dispatch>; - labelFor: string; -} + ); -const AuthorInput = ({ text, setText, labelFor }: AuthorInputInterface) => { - const [isListOpen, setIsListOpen] = useState(false); - const [options, setOptions] = useState<[{ author: string; highlight: string }]>(); const cs = useStyles({ isListOpen: isListOpen && !!options?.length, }); - useEffect(() => { - // debounce so it searches every 0.5 seconds, instead of on every stroke - const debounce = setTimeout(() => { - (async () => { - const prepareUrl = () => `${searchServiceUrl}/authors/autocomplete/?author=${text}`; - // Do the request if there is something to search for - const shouldFetch = () => text.length > 0; - const data = await fetchResults(shouldFetch() ? prepareUrl() : null); - if (data) { - setOptions(data); - } - })(); - }, 500); - - return () => clearTimeout(debounce); - }, [text]); + useDebounce( + () => { + setAuthor(text); + }, + 500, + [text] + ); return ( Date: Tue, 19 Apr 2022 08:38:47 -0400 Subject: [PATCH 20/38] Border transition --- src/web/app/src/components/SearchInput/AuthorInput.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/web/app/src/components/SearchInput/AuthorInput.tsx b/src/web/app/src/components/SearchInput/AuthorInput.tsx index 2d5dccde4e..ad507bc6c8 100644 --- a/src/web/app/src/components/SearchInput/AuthorInput.tsx +++ b/src/web/app/src/components/SearchInput/AuthorInput.tsx @@ -22,6 +22,9 @@ const useStyles = makeStyles((theme) => fontSize: '1.4rem', color: theme.palette.text.primary, borderRadius: ({ isListOpen }) => (isListOpen ? '2rem 2rem 0 0' : '4rem'), + transition: theme.transitions.create(['border-radius'], { + duration: '.1s', + }), borderColor: theme.palette.info.main, borderWidth: '2px', padding: '1rem !important', From d36d2ecedf9e7165fad4067332f38006695a113b Mon Sep 17 00:00:00 2001 From: DukeManh Date: Tue, 19 Apr 2022 09:34:16 -0400 Subject: [PATCH 21/38] remove border radius on popper option --- src/web/app/src/components/SearchInput/AuthorInput.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/web/app/src/components/SearchInput/AuthorInput.tsx b/src/web/app/src/components/SearchInput/AuthorInput.tsx index ad507bc6c8..66cc87cf1e 100644 --- a/src/web/app/src/components/SearchInput/AuthorInput.tsx +++ b/src/web/app/src/components/SearchInput/AuthorInput.tsx @@ -75,7 +75,6 @@ const useStyles = makeStyles((theme) => }, }, option: { - borderRadius: '3px', '&.Mui-focused': { backgroundColor: `${theme.palette.background.paper} !important`, }, From d780d630abdd903b55a2a645b0f98ee96554e434 Mon Sep 17 00:00:00 2001 From: DukeManh Date: Mon, 18 Apr 2022 20:39:18 -0400 Subject: [PATCH 22/38] Trigger search on enter --- src/web/app/src/components/SearchBar.tsx | 26 ++++++------------- .../components/SearchInput/SearchInput.tsx | 9 +++---- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/web/app/src/components/SearchBar.tsx b/src/web/app/src/components/SearchBar.tsx index 70c20a5c8a..c475f5e461 100644 --- a/src/web/app/src/components/SearchBar.tsx +++ b/src/web/app/src/components/SearchBar.tsx @@ -1,7 +1,7 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import { useRouter } from 'next/router'; import SearchIcon from '@material-ui/icons/Search'; -import { Box, Button } from '@material-ui/core'; +import { Button } from '@material-ui/core'; import { FormEvent, useEffect, useState } from 'react'; import SearchInput from './SearchInput/SearchInput'; @@ -140,7 +140,7 @@ const SearchBar = () => { : router.query.title || ''; const onSubmitHandler = (event: FormEvent) => { - event?.preventDefault(); + event.preventDefault(); // creates url params out of key/value pairs const parameters = new URLSearchParams(); @@ -165,36 +165,26 @@ const SearchBar = () => { }, [postParam, authorParam, titleParam]); return ( - +
onSubmitHandler(e)} /> {openDialog && ( <>
- onSubmitHandler(e)} - /> +
- onSubmitHandler(e)} - /> +
)} {openDialog && ( )} - + ); }; diff --git a/src/web/app/src/components/SearchInput/SearchInput.tsx b/src/web/app/src/components/SearchInput/SearchInput.tsx index 1568c2a578..73e0c8a221 100644 --- a/src/web/app/src/components/SearchInput/SearchInput.tsx +++ b/src/web/app/src/components/SearchInput/SearchInput.tsx @@ -1,6 +1,7 @@ import { Dispatch, SetStateAction } from 'react'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; -import { IconButton, TextField } from '@material-ui/core'; +import TextField from '@material-ui/core/TextField'; +import IconButton from '@material-ui/core/IconButton'; import SearchIcon from '@material-ui/icons/Search'; import AuthorInput from './AuthorInput'; @@ -66,10 +67,9 @@ interface SearchInputInterface { setText: Dispatch>; labelFor: string; clickEvent?: any; - onEnterKey?: any; } -const SearchInput = ({ text, setText, labelFor, clickEvent, onEnterKey }: SearchInputInterface) => { +const SearchInput = ({ text, setText, labelFor, clickEvent }: SearchInputInterface) => { const classes = useStyles(); return ( @@ -97,9 +97,6 @@ const SearchInput = ({ text, setText, labelFor, clickEvent, onEnterKey }: Search focused: classes.label, }, }} - onKeyDown={(event) => { - if (event.key === 'Enter') onEnterKey(); - }} /> )} {clickEvent && ( From 509c8b97c2291afe5720d1a356ca9dc380d12651 Mon Sep 17 00:00:00 2001 From: Roxanne Lee <32626950+rclee91@users.noreply.github.com> Date: Tue, 19 Apr 2022 22:35:21 -0400 Subject: [PATCH 23/38] Update Search readme (#3524) --- src/api/search/README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/api/search/README.md b/src/api/search/README.md index 30161f051c..ab10e16ac0 100644 --- a/src/api/search/README.md +++ b/src/api/search/README.md @@ -11,16 +11,26 @@ Run `pnpm install` from the root. ## Usage ``` -# normal mode +# docker mode pnpm start ``` By default the server is running on http://localhost:4445/. +``` +# dev mode +pnpm services:start +``` + +By default the server is running on http://localhost/v1/search. + ### Examples -- `GET /query?text=Telescope&filter=post&page=0` - Returns the search results of posts containing the keyword "Telescope" -- `GET /query?text=SenecaCDOT?filter=author&page=0` - Returns the search results of authors which relate to the keyword "SenecaCDOT" +- `GET /?post=Telescope&page=0` - Returns the search results of posts containing the keyword "Telescope" +- `GET /?title=Release&page=0` - Returns the search results of titles containing the keyword "Release" +- `GET /?author=SenecaCDOT&page=0` - Returns the search results of authors which relate to the keyword "SenecaCDOT" +- `GET /authors/autocomplete/?author=te` - Returns the search results of authors with names that start with "te" +- `GET /authors/autocomplete/?author=t s` - Returns the search results of authors with names that start with "t" and "s" - `GET /healthcheck` - returns `{ "status": "ok" }` if everything is running properly ## Docker / Docker-Compose @@ -32,6 +42,6 @@ By default the server is running on http://localhost:4445/. ### Docker-Compose -_Commands to be run from the `~/src/api` directory_ +_Commands to be run from the `root` directory_ -- To build and run: `docker-compose -f docker-compose-api-production.yml up --build search` +- To build and run: `docker-compose -f docker/development.yml up --build search` From ebe33d82b3015cf9dfa33c13af374a9c8aed2546 Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Tue, 19 Apr 2022 23:37:02 -0400 Subject: [PATCH 24/38] Remove showLastUpdate in Docusaurus (#3522) --- src/web/docusaurus/docusaurus.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/web/docusaurus/docusaurus.config.js b/src/web/docusaurus/docusaurus.config.js index 43221f091d..6ef9114e0e 100644 --- a/src/web/docusaurus/docusaurus.config.js +++ b/src/web/docusaurus/docusaurus.config.js @@ -25,8 +25,6 @@ const config = { sidebarPath: require.resolve('./sidebars.js'), // Please change this to your repo. editUrl: 'https://github.com/Seneca-CDOT/telescope/tree/master/src/web/docusaurus/', - showLastUpdateAuthor: true, - showLastUpdateTime: true, routeBasePath: '/', remarkPlugins: [require('mdx-mermaid')], }, From 151491e313dd83b801d4303f9b37f3d7f5b67f2e Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Mon, 18 Apr 2022 15:27:58 -0400 Subject: [PATCH 25/38] add Twitch sign-up page --- .../SignUp/Forms/YoutubeTwitchFeeds.tsx | 270 ++++++++++++++++++ .../components/SignUp/Schema/FormModel.tsx | 7 +- .../components/SignUp/Schema/FormSchema.tsx | 2 + src/web/app/src/interfaces/index.ts | 1 + src/web/app/src/pages/signup.tsx | 10 +- 5 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx diff --git a/src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx b/src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx new file mode 100644 index 0000000000..d7dede33b7 --- /dev/null +++ b/src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx @@ -0,0 +1,270 @@ +import { useState, useEffect, useRef } from 'react'; +import { Button, createStyles, makeStyles, Theme } from '@material-ui/core'; +import { connect } from 'formik'; +import FormControl from '@material-ui/core/FormControl'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Checkbox from '@material-ui/core/Checkbox'; + +import { feedDiscoveryServiceUrl } from '../../../config'; +import useAuth from '../../../hooks/use-auth'; +import { SignUpForm } from '../../../interfaces'; +import { TextInput } from '../FormFields'; +import formModels from '../Schema/FormModel'; + +const { twitchUrl } = formModels; + +// See feed-discovery service +type FeedType = 'blog' | 'youtube' | 'twitch'; +type DiscoveredFeed = { feedUrl: string; type: FeedType }; +type DiscoveredFeeds = { feedUrls: DiscoveredFeed[] }; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + position: 'relative', + width: '100%', + minHeight: '80%', + [theme.breakpoints.down(600)]: { + minHeight: '75%', + }, + }, + container: { + display: 'grid', + gridTemplateAreas: '1fr', + textAlign: 'center', + justifyItems: 'center', + alignItems: 'center', + position: 'absolute', + minHeight: '100%', + width: '100%', + [theme.breakpoints.down(600)]: { + width: '95%', + marginLeft: '2.5%', + }, + }, + blogPageTitle: { + fontSize: '1.5em', + }, + helpText: { + fontSize: '1.1em', + lineHeight: '1.8em', + }, + infoContainer: { + display: 'grid', + textAlign: 'center', + gridGap: '10%', + justifyItems: 'center', + alignItems: 'center', + width: '90%', + }, + inputsContainer: { + width: '100%', + display: 'grid', + gridTemplateColumns: '75% 25%', + '& .MuiFormHelperText-root': { + fontSize: '0.9em', + color: 'black', + }, + '& .MuiFormLabel-root': { + color: 'black', + }, + [theme.breakpoints.down(600)]: { + gridTemplateColumns: '80% 20%', + }, + }, + helpMessage: { + fontSize: '.9em', + color: 'black', + }, + button: { + height: '35px', + width: '50%', + alignSelf: 'center', + fontSize: '0.8em', + marginLeft: '5%', + background: '#121D59', + color: '#A0D1FB', + '&:hover': { + color: 'black', + border: '1px solid #121D59', + }, + '&.Mui-disabled': { + backgroundColor: 'inherit', + }, + [theme.breakpoints.down(600)]: { + width: 'auto', + }, + }, + RssButtonContainer: { + width: '90%', + display: 'grid', + }, + infoRSSContainer: { + minHeight: '120px', + maxHeight: '120px', + width: '100%', + overflowY: 'auto', + }, + noBlogMessage: { + fontSize: '1em', + color: '#474747', + marginTop: '40px', + }, + text: { + fontSize: '0.9em', + alignSelf: 'end', + color: '#474747', + }, + RssButtonWrapper: { + width: '100%', + }, + RssButton: { + width: '101%', + borderRadius: '0', + background: '#121D59', + color: '#A0D1FB', + '&:hover': { + color: 'black', + border: '1px solid #121D59', + }, + }, + agreeMessage: { + [theme.breakpoints.down(600)]: { + alignSelf: 'end', + }, + }, + formControlLabel: { + fontSize: '.9em', + height: '10px', + color: '#474747', + }, + }) +); + +const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { + const classes = useStyles(); + const { values, errors, setFieldValue } = props.formik; + const { token } = useAuth(); + + const [twitchUrlError, setTwitchUrlError] = useState(''); + const [validating, setValidating] = useState(false); + const controllerRef = useRef(); + + const validateTwitch = async () => { + if (errors.twitchUrl) { + setFieldValue('feeds', [], true); + return; + } + try { + setValidating(true); + controllerRef?.current?.abort(); + controllerRef.current = new AbortController(); + // Allow a list of URLs, separated by spaces + const urls = values.twitchUrl.split(/ +/); + const response = await fetch(`${feedDiscoveryServiceUrl}`, { + signal: controllerRef.current?.signal, + method: 'post', + headers: { + Authorization: `bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(urls), + }); + if (!response.ok) { + throw new Error(response.statusText); + } + const { feedUrls }: DiscoveredFeeds = await response.json(); + + setTwitchUrlError(''); + setFieldValue( + 'allFeeds', + feedUrls.map((discoveredFeed: DiscoveredFeed) => discoveredFeed.feedUrl) + ); + } catch (err) { + console.error(err, 'Unable to discover feeds'); + + setTwitchUrlError('Unable to discover feeds'); + setFieldValue('allFeeds', []); + } finally { + // eslint-disable-next-line require-atomic-updates + controllerRef.current = null; + setValidating(false); + } + }; + + const handleCheck = (url: string) => { + const selectedFeeds = values.feeds.includes(url) + ? values.feeds.filter((val: string) => val !== url) + : [...values.feeds, url]; + + setFieldValue('feeds', selectedFeeds, true); + }; + + useEffect(() => { + if (errors.twitchUrl) { + validateTwitch(); + } + + return () => { + controllerRef?.current?.abort(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+
+

Youtube and Twitch

+

+ OPTIONAL: Enter your Twitch URL and select the RSS you want to use in Telescope ecosystem. +

+
+
+ + +
+
+
+ {values.allFeeds.length ? ( + + + {values.allFeeds.map((url) => ( + handleCheck(url)} + /> + } + label={

{url}

} + /> + ))} +
+ + {errors.feeds || ''} + +
+ ) : ( +

Please validate your Twitch URL

+ )} +
+
+
+
+
+ ); +}); + +export default YoutubeTwitchFeeds; diff --git a/src/web/app/src/components/SignUp/Schema/FormModel.tsx b/src/web/app/src/components/SignUp/Schema/FormModel.tsx index a574420426..b662d1bbb8 100644 --- a/src/web/app/src/components/SignUp/Schema/FormModel.tsx +++ b/src/web/app/src/components/SignUp/Schema/FormModel.tsx @@ -35,10 +35,15 @@ export default { }, blogUrl: { name: 'blogUrl', - label: 'Blog URl', + label: 'Blog URL', requiredErrorMsg: 'Blog URL is required', invalidErrorMsg: 'Invalid URL', }, + twitchUrl: { + name: 'twitchUrl', + label: 'Twitch URL', + invalidErrorMsg: 'Invalid URL', + }, feeds: { name: 'feeds', label: 'RSS Feeds', diff --git a/src/web/app/src/components/SignUp/Schema/FormSchema.tsx b/src/web/app/src/components/SignUp/Schema/FormSchema.tsx index 3a0aabecea..cdfe06c279 100644 --- a/src/web/app/src/components/SignUp/Schema/FormSchema.tsx +++ b/src/web/app/src/components/SignUp/Schema/FormSchema.tsx @@ -12,6 +12,7 @@ const { feeds, allFeeds, blogUrl, + twitchUrl, blogOwnership, } = formModels; @@ -45,6 +46,7 @@ export default [ // Third step we collect the user blog and the RSSfeeds from it. Yup.object().shape({ [blogUrl.name]: Yup.string().url().required(blogUrl.requiredErrorMsg), + [twitchUrl.name]: Yup.string().url(), [feeds.name]: Yup.array().of(Yup.string()).min(1, feeds.requiredErrorMsg), [allFeeds.name]: Yup.array().of(Yup.string()), [blogOwnership.name]: Yup.boolean().test( diff --git a/src/web/app/src/interfaces/index.ts b/src/web/app/src/interfaces/index.ts index f8f6a22f41..b2284b869b 100644 --- a/src/web/app/src/interfaces/index.ts +++ b/src/web/app/src/interfaces/index.ts @@ -29,6 +29,7 @@ export type SignUpForm = { githubUsername: string; githubOwnership: boolean; blogUrl: string; + twitchUrl: string; feeds: Array; allFeeds: Array; blogOwnership: boolean; diff --git a/src/web/app/src/pages/signup.tsx b/src/web/app/src/pages/signup.tsx index 6c9c585595..d88e937b07 100644 --- a/src/web/app/src/pages/signup.tsx +++ b/src/web/app/src/pages/signup.tsx @@ -9,6 +9,8 @@ import Overview from '../components/SignUp/Forms/Overview'; import BasicInfo from '../components/SignUp/Forms/BasicInfo'; import GitHubAccount from '../components/SignUp/Forms/GitHubAccount'; import RSSFeeds from '../components/SignUp/Forms/RSSFeeds'; +import YoutubeTwitchFeeds from '../components/SignUp/Forms/YoutubeTwitchFeeds'; + import Review from '../components/SignUp/Forms/Review'; import DynamicImage from '../components/BannerDynamicItems'; @@ -24,6 +26,7 @@ enum SIGN_UP_STEPS { BASIC_INFO, GITHUB_ACCOUNT, RSS_FEEDS, + YOUTUBE_TWITCH_FEEDS, REVIEW, } @@ -186,7 +189,7 @@ const SignUpPage = () => { }, [user]); const handleSubmit = async (values: SignUpForm, actions: FormikHelpers) => { - if (activeStep < 4) { + if (activeStep < 5) { handleNext(); actions.setTouched({}); actions.setSubmitting(false); @@ -248,6 +251,9 @@ const SignUpPage = () => { case SIGN_UP_STEPS.RSS_FEEDS: return ; + case SIGN_UP_STEPS.YOUTUBE_TWITCH_FEEDS: + return ; + case SIGN_UP_STEPS.REVIEW: return ; @@ -349,7 +355,7 @@ const SignUpPage = () => { )} {activeStep > 0 && loggedIn && ( )}
From cfe23fda852f540741e907dc19e178ce6ffcd5f9 Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Tue, 19 Apr 2022 10:38:00 -0400 Subject: [PATCH 26/38] Use channel to refer to Youtube and Twitch account --- ...outubeTwitchFeeds.tsx => ChannelFeeds.tsx} | 41 ++++++++++--------- .../components/SignUp/Schema/FormModel.tsx | 8 ++-- .../components/SignUp/Schema/FormSchema.tsx | 4 +- src/web/app/src/interfaces/index.ts | 2 +- src/web/app/src/pages/signup.tsx | 8 ++-- 5 files changed, 33 insertions(+), 30 deletions(-) rename src/web/app/src/components/SignUp/Forms/{YoutubeTwitchFeeds.tsx => ChannelFeeds.tsx} (86%) diff --git a/src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx b/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx similarity index 86% rename from src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx rename to src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx index d7dede33b7..950a9ac50d 100644 --- a/src/web/app/src/components/SignUp/Forms/YoutubeTwitchFeeds.tsx +++ b/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx @@ -13,7 +13,7 @@ import { SignUpForm } from '../../../interfaces'; import { TextInput } from '../FormFields'; import formModels from '../Schema/FormModel'; -const { twitchUrl } = formModels; +const { channelUrl } = formModels; // See feed-discovery service type FeedType = 'blog' | 'youtube' | 'twitch'; @@ -145,17 +145,17 @@ const useStyles = makeStyles((theme: Theme) => }) ); -const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { +const ChannelFeeds = connect<{}, SignUpForm>((props) => { const classes = useStyles(); const { values, errors, setFieldValue } = props.formik; const { token } = useAuth(); - const [twitchUrlError, setTwitchUrlError] = useState(''); + const [channelUrlError, setChannelUrlError] = useState(''); const [validating, setValidating] = useState(false); const controllerRef = useRef(); - const validateTwitch = async () => { - if (errors.twitchUrl) { + const validateChannel = async () => { + if (errors.channelUrl) { setFieldValue('feeds', [], true); return; } @@ -164,7 +164,7 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { controllerRef?.current?.abort(); controllerRef.current = new AbortController(); // Allow a list of URLs, separated by spaces - const urls = values.twitchUrl.split(/ +/); + const urls = values.channelUrl.split(/ +/); const response = await fetch(`${feedDiscoveryServiceUrl}`, { signal: controllerRef.current?.signal, method: 'post', @@ -179,7 +179,7 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { } const { feedUrls }: DiscoveredFeeds = await response.json(); - setTwitchUrlError(''); + setChannelUrlError(''); setFieldValue( 'allFeeds', feedUrls.map((discoveredFeed: DiscoveredFeed) => discoveredFeed.feedUrl) @@ -187,7 +187,7 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { } catch (err) { console.error(err, 'Unable to discover feeds'); - setTwitchUrlError('Unable to discover feeds'); + setChannelUrlError('Unable to discover feeds'); setFieldValue('allFeeds', []); } finally { // eslint-disable-next-line require-atomic-updates @@ -205,8 +205,8 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { }; useEffect(() => { - if (errors.twitchUrl) { - validateTwitch(); + if (errors.channelUrl) { + validateChannel(); } return () => { @@ -220,18 +220,19 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => {

Youtube and Twitch

- OPTIONAL: Enter your Twitch URL and select the RSS you want to use in Telescope ecosystem. + OPTIONAL: Enter your YouTube and/or Twitch channels and select the RSS feed(s) you want to + use in Telescope (separate more than one URL with a space)

-
@@ -257,7 +258,9 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { ) : ( -

Please validate your Twitch URL

+

+ Please validate your YouTube or Twitch URL(s) +

)}
@@ -267,4 +270,4 @@ const YoutubeTwitchFeeds = connect<{}, SignUpForm>((props) => { ); }); -export default YoutubeTwitchFeeds; +export default ChannelFeeds; diff --git a/src/web/app/src/components/SignUp/Schema/FormModel.tsx b/src/web/app/src/components/SignUp/Schema/FormModel.tsx index b662d1bbb8..cea1a0e84a 100644 --- a/src/web/app/src/components/SignUp/Schema/FormModel.tsx +++ b/src/web/app/src/components/SignUp/Schema/FormModel.tsx @@ -39,10 +39,10 @@ export default { requiredErrorMsg: 'Blog URL is required', invalidErrorMsg: 'Invalid URL', }, - twitchUrl: { - name: 'twitchUrl', - label: 'Twitch URL', - invalidErrorMsg: 'Invalid URL', + channelUrl: { + name: 'channelUrl', + label: 'Youtube/Twitch URL(s)', + invalidErrorMsg: 'Invalid URL(s)', }, feeds: { name: 'feeds', diff --git a/src/web/app/src/components/SignUp/Schema/FormSchema.tsx b/src/web/app/src/components/SignUp/Schema/FormSchema.tsx index cdfe06c279..2e2d057d2a 100644 --- a/src/web/app/src/components/SignUp/Schema/FormSchema.tsx +++ b/src/web/app/src/components/SignUp/Schema/FormSchema.tsx @@ -12,7 +12,7 @@ const { feeds, allFeeds, blogUrl, - twitchUrl, + channelUrl, blogOwnership, } = formModels; @@ -46,7 +46,7 @@ export default [ // Third step we collect the user blog and the RSSfeeds from it. Yup.object().shape({ [blogUrl.name]: Yup.string().url().required(blogUrl.requiredErrorMsg), - [twitchUrl.name]: Yup.string().url(), + [channelUrl.name]: Yup.string(), [feeds.name]: Yup.array().of(Yup.string()).min(1, feeds.requiredErrorMsg), [allFeeds.name]: Yup.array().of(Yup.string()), [blogOwnership.name]: Yup.boolean().test( diff --git a/src/web/app/src/interfaces/index.ts b/src/web/app/src/interfaces/index.ts index b2284b869b..b432d9bd24 100644 --- a/src/web/app/src/interfaces/index.ts +++ b/src/web/app/src/interfaces/index.ts @@ -29,7 +29,7 @@ export type SignUpForm = { githubUsername: string; githubOwnership: boolean; blogUrl: string; - twitchUrl: string; + channelUrl: string; feeds: Array; allFeeds: Array; blogOwnership: boolean; diff --git a/src/web/app/src/pages/signup.tsx b/src/web/app/src/pages/signup.tsx index d88e937b07..c5dd92fa11 100644 --- a/src/web/app/src/pages/signup.tsx +++ b/src/web/app/src/pages/signup.tsx @@ -9,7 +9,7 @@ import Overview from '../components/SignUp/Forms/Overview'; import BasicInfo from '../components/SignUp/Forms/BasicInfo'; import GitHubAccount from '../components/SignUp/Forms/GitHubAccount'; import RSSFeeds from '../components/SignUp/Forms/RSSFeeds'; -import YoutubeTwitchFeeds from '../components/SignUp/Forms/YoutubeTwitchFeeds'; +import ChannelFeeds from '../components/SignUp/Forms/ChannelFeeds'; import Review from '../components/SignUp/Forms/Review'; import DynamicImage from '../components/BannerDynamicItems'; @@ -26,7 +26,7 @@ enum SIGN_UP_STEPS { BASIC_INFO, GITHUB_ACCOUNT, RSS_FEEDS, - YOUTUBE_TWITCH_FEEDS, + CHANNEL_FEEDS, REVIEW, } @@ -251,8 +251,8 @@ const SignUpPage = () => { case SIGN_UP_STEPS.RSS_FEEDS: return ; - case SIGN_UP_STEPS.YOUTUBE_TWITCH_FEEDS: - return ; + case SIGN_UP_STEPS.CHANNEL_FEEDS: + return ; case SIGN_UP_STEPS.REVIEW: return ; From b5f7b244b398b22598b3605963725a024e333716 Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Tue, 19 Apr 2022 11:39:44 -0400 Subject: [PATCH 27/38] Add channelOwnership checkbox --- .../src/components/SignUp/Forms/ChannelFeeds.tsx | 9 +++++++-- .../app/src/components/SignUp/Schema/FormModel.tsx | 5 +++++ .../src/components/SignUp/Schema/FormSchema.tsx | 14 +++++++++++++- src/web/app/src/interfaces/index.ts | 1 + 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx b/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx index 950a9ac50d..a4d57deabb 100644 --- a/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx +++ b/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx @@ -10,10 +10,10 @@ import Checkbox from '@material-ui/core/Checkbox'; import { feedDiscoveryServiceUrl } from '../../../config'; import useAuth from '../../../hooks/use-auth'; import { SignUpForm } from '../../../interfaces'; -import { TextInput } from '../FormFields'; +import { TextInput, CheckBoxInput } from '../FormFields'; import formModels from '../Schema/FormModel'; -const { channelUrl } = formModels; +const { channelUrl, channelOwnership } = formModels; // See feed-discovery service type FeedType = 'blog' | 'youtube' | 'twitch'; @@ -265,6 +265,11 @@ const ChannelFeeds = connect<{}, SignUpForm>((props) => {
+
); diff --git a/src/web/app/src/components/SignUp/Schema/FormModel.tsx b/src/web/app/src/components/SignUp/Schema/FormModel.tsx index cea1a0e84a..80650b0025 100644 --- a/src/web/app/src/components/SignUp/Schema/FormModel.tsx +++ b/src/web/app/src/components/SignUp/Schema/FormModel.tsx @@ -57,4 +57,9 @@ export default { label: 'I declare I’m the owner and the maintainer of this blog account', invalidErrorMsg: 'You must be the owner of this account', }, + channelOwnership: { + name: 'channelOwnership', + label: 'I declare I’m the owner and the maintainer of the channel(s) above', + invalidErrorMsg: 'You must be the owner of the channel(s)', + }, }; diff --git a/src/web/app/src/components/SignUp/Schema/FormSchema.tsx b/src/web/app/src/components/SignUp/Schema/FormSchema.tsx index 2e2d057d2a..1e8c5b731a 100644 --- a/src/web/app/src/components/SignUp/Schema/FormSchema.tsx +++ b/src/web/app/src/components/SignUp/Schema/FormSchema.tsx @@ -14,6 +14,7 @@ const { blogUrl, channelUrl, blogOwnership, + channelOwnership, } = formModels; // Each signup step has one validation schema @@ -46,7 +47,6 @@ export default [ // Third step we collect the user blog and the RSSfeeds from it. Yup.object().shape({ [blogUrl.name]: Yup.string().url().required(blogUrl.requiredErrorMsg), - [channelUrl.name]: Yup.string(), [feeds.name]: Yup.array().of(Yup.string()).min(1, feeds.requiredErrorMsg), [allFeeds.name]: Yup.array().of(Yup.string()), [blogOwnership.name]: Yup.boolean().test( @@ -56,6 +56,18 @@ export default [ ), }), + // Fourth step we collect the user Youtube/Twitch channels and the RSSfeeds from it. + Yup.object().shape({ + [channelUrl.name]: Yup.string(), + [feeds.name]: Yup.array().of(Yup.string()), + [allFeeds.name]: Yup.array().of(Yup.string()), + [channelOwnership.name]: Yup.boolean().test( + 'agreed', + channelOwnership.invalidErrorMsg, + (val) => !!val + ), + }), + // Reviewing step has no validation logic. We just display all data that we collected. Yup.object().shape({}), ]; diff --git a/src/web/app/src/interfaces/index.ts b/src/web/app/src/interfaces/index.ts index b432d9bd24..5198f51267 100644 --- a/src/web/app/src/interfaces/index.ts +++ b/src/web/app/src/interfaces/index.ts @@ -33,6 +33,7 @@ export type SignUpForm = { feeds: Array; allFeeds: Array; blogOwnership: boolean; + channelOwnership: boolean; }; export type ThemeName = 'light' | 'dark'; From 929a162069b0425251f562f8c0b3f2886481c42f Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Tue, 19 Apr 2022 23:08:40 -0400 Subject: [PATCH 28/38] Youtube to YouTube --- src/api/parser/src/data/post.js | 2 +- src/backend/data/post.js | 2 +- src/db/prisma/schema.prisma | 2 +- src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx | 4 ++-- src/web/app/src/components/SignUp/Schema/FormModel.tsx | 2 +- src/web/app/src/components/SignUp/Schema/FormSchema.tsx | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/parser/src/data/post.js b/src/api/parser/src/data/post.js index a8c415a735..6003f4063e 100644 --- a/src/api/parser/src/data/post.js +++ b/src/api/parser/src/data/post.js @@ -145,7 +145,7 @@ class Post { article.date = article.pubdate; } - // All the Youtube feed return an array off html so we will need to convert it to a string so as to process and sanitize it + // All the YouTube feed return an array off html so we will need to convert it to a string so as to process and sanitize it if (Array.isArray(article.content)) { article.content = article.content.join(' '); } diff --git a/src/backend/data/post.js b/src/backend/data/post.js index a882e34427..b964c6f18a 100644 --- a/src/backend/data/post.js +++ b/src/backend/data/post.js @@ -149,7 +149,7 @@ class Post { article.date = article.pubdate; } - // All the Youtube feed return an array off html so we will need to convert it to a string so as to process and sanitize it + // All the YouTube feed return an array off html so we will need to convert it to a string so as to process and sanitize it if (Array.isArray(article.content)) { article.content = article.content.join(' '); } diff --git a/src/db/prisma/schema.prisma b/src/db/prisma/schema.prisma index c842af8ff1..1683891226 100644 --- a/src/db/prisma/schema.prisma +++ b/src/db/prisma/schema.prisma @@ -19,7 +19,7 @@ model feeds { id String // A short hashed id generated from the URL by Satellite, user_id String? // optional, a user can claim an existing feed when they register wiki_author_name String? // wiki owner of a feed, maybe unused when the feed is linked with an actual user - html_url String? //actual URL the feed refers to, could be a blog URL, a Youtube or Twitch channel + html_url String? //actual URL the feed refers to, could be a blog URL, a YouTube or Twitch channel type FeedType? @default(blog) invalid Boolean? @default(false) flagged Boolean? @default(false) diff --git a/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx b/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx index a4d57deabb..5406851e3c 100644 --- a/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx +++ b/src/web/app/src/components/SignUp/Forms/ChannelFeeds.tsx @@ -218,7 +218,7 @@ const ChannelFeeds = connect<{}, SignUpForm>((props) => { return (
-

Youtube and Twitch

+

YouTube and Twitch

OPTIONAL: Enter your YouTube and/or Twitch channels and select the RSS feed(s) you want to use in Telescope (separate more than one URL with a space) @@ -228,7 +228,7 @@ const ChannelFeeds = connect<{}, SignUpForm>((props) => { + {user?.isRegistered ? ( <> diff --git a/src/web/app/src/components/DependenciesPage.tsx b/src/web/app/src/components/DependenciesPage.tsx new file mode 100644 index 0000000000..0bec5d653c --- /dev/null +++ b/src/web/app/src/components/DependenciesPage.tsx @@ -0,0 +1,105 @@ +import { makeStyles } from '@material-ui/core/styles'; +import useSWR from 'swr'; +import { useState } from 'react'; +import IconButton from '@mui/material/IconButton'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; + +import { dependencyDiscoveryUrl } from '../config'; +import DependenciesTable from './DependenciesTable'; + +const useStyles = makeStyles((theme) => ({ + root: { + backgroundColor: theme.palette.background.default, + fontFamily: 'Spartan', + padding: '1em 0 2em 0', + paddingTop: 'env(safe-area-inset-top)', + wordWrap: 'break-word', + [theme.breakpoints.down(1024)]: { + maxWidth: 'none', + }, + '& h1': { + color: theme.palette.text.secondary, + fontSize: 24, + transition: 'color 1s', + marginTop: 0, + }, + '& p, blockquote': { + color: theme.palette.text.primary, + fontSize: 16, + margin: 0, + }, + }, + container: { + padding: '2vh 18vw', + [theme.breakpoints.down(1024)]: { + padding: '2vh 8vw', + wordWrap: 'break-word', + }, + }, + textCollapsed: { + display: '-webkit-box', + textOverflow: 'ellipsis', + overflow: 'hidden', + maxHeight: '6rem', + lineHeight: '3rem', + '-webkit-line-clamp': 2, + '-webkit-box-orient': 'vertical', + }, + textOpened: { + lineHeight: '3rem', + }, + colorButton: { + color: theme.palette.text.primary, + }, + buttonWrapper: { + marginBottom: '1rem', + display: 'flex', + justifyContent: 'end', + }, +})); + +const DependenciesPage = () => { + const classes = useStyles(); + const { data: dependencies = [] } = useSWR(`${dependencyDiscoveryUrl}/projects`); + const [open, setOpen] = useState(false); + + const toggleCollapse = () => { + setOpen(!open); + }; + + return ( +
+
+

Dependencies

+
+

+ Telescope is an open source project, built with other open source projects. We use more + than 1,000 libraries, frameworks, tools and other projects, and each one has + opportunities for you to contribute fixes, features, documentation, translations, and + tests. Find your next Issue in the list below, or search for something familiar. Help + improve Telescope by working on the many upstream projects we depend upon! +

+
+ toggleCollapse()}> + {open ? ( +
+ Show less + +
+ ) : ( +
+ Show more + +
+ )} +
+
+
+ +
+
+ ); +}; + +export default DependenciesPage; diff --git a/src/web/app/src/components/DependenciesTable/Row.tsx b/src/web/app/src/components/DependenciesTable/Row.tsx new file mode 100644 index 0000000000..3952015f75 --- /dev/null +++ b/src/web/app/src/components/DependenciesTable/Row.tsx @@ -0,0 +1,247 @@ +import { useState } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TableRow from '@mui/material/TableRow'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import IconButton from '@mui/material/IconButton'; +import { Box, Collapse } from '@material-ui/core'; +import GitHubIcon from '@mui/icons-material/GitHub'; +import { VscIssues } from 'react-icons/vsc'; +import FolderIcon from '@mui/icons-material/Folder'; +import CopyrightIcon from '@mui/icons-material/Copyright'; +import TableCell from '@mui/material/TableCell'; +import { FiPackage } from 'react-icons/fi'; +import useSWR from 'swr'; +import CircularProgress from '@mui/material/CircularProgress'; + +import { dependencyDiscoveryUrl } from '../../config'; + +const useStyles = makeStyles((theme) => ({ + root: { + '& .MuiTableCell-body': { + fontSize: '14px', + color: theme.palette.text.secondary, + fontWeight: 700, + }, + cursor: 'pointer', + }, + rowTitleIcon: { + marginRight: '1rem', + fontSize: '2rem', + }, + iconRow: { + textAlign: 'right !important' as 'right', + }, + icons: { + color: theme.palette.text.secondary, + fontSize: '2rem !important', + }, + dependencyInfo: { + display: 'inline-flex', + alignItems: 'center', + margin: '1rem', + }, + dependencyInfoIssue: { + margin: '1rem', + display: 'flex', + alignItems: 'center', + }, + dependencyInfoIcon: { + fontSize: '2rem !important', + marginRight: '1.5rem', + }, + issueCard: { + display: 'flex', + border: '1px', + borderColor: theme.palette.text.primary, + borderStyle: 'solid', + color: theme.palette.text.primary, + padding: '1rem', + marginTop: '1rem', + marginBottom: '1rem', + }, + issueCardLink: { + textDecoration: 'none', + }, + collapseBox: { + fontSize: '14px', + color: theme.palette.text.primary, + padding: '2rem', + fontFamily: 'Spartan', + }, + collapseBoxLoading: { + padding: '2rem', + display: 'flex', + justifyContent: 'center', + }, + issueCardTitle: { + fontWeight: 700, + fontFamily: 'Spartan', + }, + colorPrimary: { + color: theme.palette.text.secondary, + }, + links: { + color: theme.palette.primary.main, + }, +})); + +type issueInfo = { + htmlUrl: string; + title: string; + body: string; + createdAt: string; +}; + +type RowProps = { + dependency: string; +}; + +export const Row = ({ dependency }: RowProps) => { + const classes = useStyles(); + const [open, setOpen] = useState(false); + const [apiLimited, setApiLimited] = useState(false); + const { data: projectInfo } = useSWR( + open ? `${dependencyDiscoveryUrl}/projects/${dependency}` : null + ); + const { data: issues = [] } = useSWR( + open ? `${dependencyDiscoveryUrl}/github/${dependency}` : null, + async (u: string) => { + setApiLimited(false); + const issuesInfoReq = await fetch(u); + if (issuesInfoReq.status === 403) { + setApiLimited(true); + return []; + } + const issuesInfoJson = await issuesInfoReq.json(); + return issuesInfoJson; + } + ); + + // Handle collapse action + const toggleCollapse = () => { + setOpen(!open); + }; + + return ( + <> + toggleCollapse()}> + + + {dependency} + + + + toggleCollapse()}> + {open ? ( + + ) : ( + + )} + + + + + + + {projectInfo && issues ? ( + + {projectInfo?.gitRepository?.url ? ( + + ) : null} + {projectInfo?.directory ? ( +
+ +
+ Directory: + {projectInfo.directory} +
+
+ ) : null} + {projectInfo?.license ? ( +
+ +
+ License: + {projectInfo.license} +
+
+ ) : null} +
+ +
+ + Issues (Labeled with hacktoberfest, good first issue, or help wanted) + :{' '} + +
+
+ {issues.length !== 0 ? ( + issues.map((issue: issueInfo) => { + return ( + +
+ +
+
{issue.title}
+
+ #{issue.htmlUrl.match(/([0-9]*)$/)?.at(0)} opened on{' '} + {new Date(issue.createdAt).toDateString()} +
+
+
+
+ ); + }) + ) : ( +
+ {apiLimited ? ( + + Github API reached limit, please use the link directly{' '} + + {dependency} + + + ) : ( + 'No Issue found with label hacktoberfest, good first issue, or help wanted' + )} +
+ )} +
+ ) : ( + + + + )} +
+
+
+ + ); +}; + +export default Row; diff --git a/src/web/app/src/components/DependenciesTable/index.tsx b/src/web/app/src/components/DependenciesTable/index.tsx new file mode 100644 index 0000000000..4442e6082c --- /dev/null +++ b/src/web/app/src/components/DependenciesTable/index.tsx @@ -0,0 +1,75 @@ +import { useState, useMemo } from 'react'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableContainer from '@mui/material/TableContainer'; +import TablePagination from '@mui/material/TablePagination'; +import { makeStyles } from '@material-ui/core/styles'; + +import Row from './Row'; +import SearchInput from '../SearchInput/SearchInput'; + +const useStyles = makeStyles((theme) => ({ + root: { + '& .MuiTablePagination-displayedRows': { + fontSize: '14px', + color: theme.palette.text.primary, + }, + '& .MuiTablePagination-actions': { + color: theme.palette.text.primary, + }, + }, +})); + +type DependenciesTableProps = { + dependencies: string[]; +}; + +const DependenciesTable = ({ dependencies }: DependenciesTableProps) => { + const classes = useStyles(); + const [page, setPage] = useState(0); + const rowsPerPage = 15; // Set 15 element per page + const [searchField, setSearchField] = useState(''); + + // Handle page change + const handleChangePage = (event: unknown, newPage: number) => { + setPage(newPage); + }; + + // Compute dependencyList based on search query + const dependencyList = useMemo(() => { + setPage(0); + if (!searchField) return dependencies; + return dependencies.filter((dependency: string) => { + return dependency.toLowerCase().includes(searchField.toLowerCase()); + }); + }, [dependencies, searchField]); + + return ( + <> + + + + + + {dependencyList + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((dependency) => { + return ; + })} + +
+ +
+ + ); +}; + +export default DependenciesTable; diff --git a/src/web/app/src/components/NavBar/index.tsx b/src/web/app/src/components/NavBar/index.tsx index 69c5058b32..15e48c4bfe 100644 --- a/src/web/app/src/components/NavBar/index.tsx +++ b/src/web/app/src/components/NavBar/index.tsx @@ -7,6 +7,7 @@ import SearchIcon from '@material-ui/icons/Search'; import HomeIcon from '@material-ui/icons/Home'; import ContactSupportIcon from '@material-ui/icons/ContactSupport'; import { Transition } from 'react-transition-group'; +import { FiPackage } from 'react-icons/fi'; import dynamic from 'next/dynamic'; @@ -109,6 +110,12 @@ const iconProps: NavBarIconProps[] = [ ariaLabel: 'about', Icon: ContactSupportIcon, }, + { + href: '/dependencies', + title: 'Dependencies', + ariaLabel: 'Dependencies', + Icon: FiPackage, + }, ]; type NavBarProps = { diff --git a/src/web/app/src/config.ts b/src/web/app/src/config.ts index f3d5113175..828282ee13 100644 --- a/src/web/app/src/config.ts +++ b/src/web/app/src/config.ts @@ -14,6 +14,7 @@ const postsServiceUrl = process.env.NEXT_PUBLIC_POSTS_URL; const searchServiceUrl = process.env.NEXT_PUBLIC_SEARCH_URL; const feedDiscoveryServiceUrl = process.env.NEXT_PUBLIC_FEED_DISCOVERY_URL; const statusUrl = process.env.NEXT_PUBLIC_STATUS_URL; +const dependencyDiscoveryUrl = process.env.NEXT_PUBLIC_DEPENDENCY_DISCOVERY_URL; // Github sha commit const gitCommitSha = process.env.NEXT_PUBLIC_GIT_COMMIT; @@ -50,4 +51,5 @@ export { feedDiscoveryServiceUrl, statusUrl, gitCommitSha, + dependencyDiscoveryUrl, }; diff --git a/src/web/app/src/pages/dependencies.tsx b/src/web/app/src/pages/dependencies.tsx new file mode 100644 index 0000000000..929632d830 --- /dev/null +++ b/src/web/app/src/pages/dependencies.tsx @@ -0,0 +1,15 @@ +import SEO from '../components/SEO'; +import NavBar from '../components/NavBar'; +import DependenciesPage from '../components/DependenciesPage'; + +const dependencies = () => { + return ( +
+ + + +
+ ); +}; + +export default dependencies; From 9706d28e3dd7c0b3c775caae66965c0573557402 Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Wed, 20 Apr 2022 13:36:43 -0400 Subject: [PATCH 31/38] Disable failing e2e test until we solve #3504 --- src/api/sso/test/e2e/signup-flow.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/sso/test/e2e/signup-flow.test.js b/src/api/sso/test/e2e/signup-flow.test.js index 9f08031aea..f2de5aac8a 100644 --- a/src/api/sso/test/e2e/signup-flow.test.js +++ b/src/api/sso/test/e2e/signup-flow.test.js @@ -63,7 +63,8 @@ describe('Signup Flow', () => { it('should have none of the users in the Users service for test data accounts', () => ensureUsers(users, false)); - it('signup flow works to login and register a new Telescope user', async () => { + // https://github.com/Seneca-CDOT/telescope/issues/3504 + it.skip('signup flow works to login and register a new Telescope user', async () => { // Part 1: login using SSO, as a user who does not have a Telescope account. // Confirm that the payload of the token we get back matches what we expect. // NOTE: the data comes from config/simplesamlphp-users.php. From b3a57627d7c9fc597ada24a7febebc01918b56ac Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Wed, 20 Apr 2022 13:54:34 -0400 Subject: [PATCH 32/38] Disable e2e tests in Chromium until we fix #3504 --- jest-playwright.config.js | 6 +++++- src/api/sso/test/e2e/signup-flow.test.js | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/jest-playwright.config.js b/jest-playwright.config.js index 413043e59c..df5a4aefa8 100644 --- a/jest-playwright.config.js +++ b/jest-playwright.config.js @@ -8,5 +8,9 @@ module.exports = { // slowMo: 1000, headless: true, }, - browsers: ['chromium', 'firefox', 'webkit'], + browsers: [ + /* 'chromium' - chromium disabled until we fix https://github.com/Seneca-CDOT/telescope/issues/3504 */ + 'firefox', + 'webkit', + ], }; diff --git a/src/api/sso/test/e2e/signup-flow.test.js b/src/api/sso/test/e2e/signup-flow.test.js index f2de5aac8a..9f08031aea 100644 --- a/src/api/sso/test/e2e/signup-flow.test.js +++ b/src/api/sso/test/e2e/signup-flow.test.js @@ -63,8 +63,7 @@ describe('Signup Flow', () => { it('should have none of the users in the Users service for test data accounts', () => ensureUsers(users, false)); - // https://github.com/Seneca-CDOT/telescope/issues/3504 - it.skip('signup flow works to login and register a new Telescope user', async () => { + it('signup flow works to login and register a new Telescope user', async () => { // Part 1: login using SSO, as a user who does not have a Telescope account. // Confirm that the payload of the token we get back matches what we expect. // NOTE: the data comes from config/simplesamlphp-users.php. From db06785b21a41e870f6fa903fa6a11bc0a88d5b1 Mon Sep 17 00:00:00 2001 From: Kevan-Y <58233223+Kevan-Y@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:42:30 -0400 Subject: [PATCH 33/38] Add missing dependency_discovery_url in CD --- .github/workflows/docker-build-and-push.yml | 6 ++++++ .github/workflows/release.yml | 1 + 2 files changed, 7 insertions(+) diff --git a/.github/workflows/docker-build-and-push.yml b/.github/workflows/docker-build-and-push.yml index 4cf9f35077..c9563dcdee 100644 --- a/.github/workflows/docker-build-and-push.yml +++ b/.github/workflows/docker-build-and-push.yml @@ -47,6 +47,11 @@ on: required: false default: 'https://dev.api.telescope.cdot.systems/v1/status' type: string + dependency_discovery_url: + description: 'The feed-discovery microservice URL (defaults to staging)' + required: false + default: 'https://dev.api.telescope.cdot.systems/v1/dependency-discovery' + type: string supabase_url: description: 'The Supabase URL' required: false @@ -93,6 +98,7 @@ jobs: GIT_COMMIT=${{ github.sha }} SUPABASE_URL=${{ inputs.supabase_url }} ANON_KEY=${{ inputs.anon_key }} + DEPENDENCY_DISCOVERY_URL=${{ inputs.dependency_discovery_url }} - context: src/api/planet image: planet - context: src/api/posts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 470121ba39..04592af0b1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,7 @@ jobs: search_url: 'https://api.telescope.cdot.systems/v1/search' feed_discovery_url: 'https://api.telescope.cdot.systems/v1/feed-discovery' status_url: 'https://api.telescope.cdot.systems/v1/status' + dependency_discovery_url: 'https://api.telescope.cdot.systems/v1/dependency-discovery' supabase_url: 'https://api.telescope.cdot.systems/v1/supabase' anon_key: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UiLAogICAgImlhdCI6IDE2NDg2MTI4MDAsCiAgICAiZXhwIjogMTgwNjM3OTIwMAp9.YMOYR_8pZqqDRJkoAFuEWp-FaQfvmHXTOXKncBlRjdE' secrets: From 2700016086c3fbc0cdc9457084eeac859a8c08da Mon Sep 17 00:00:00 2001 From: Francesco Menghi <53121061+menghif@users.noreply.github.com> Date: Fri, 22 Apr 2022 10:42:06 -0400 Subject: [PATCH 34/38] Add --team flag to turborepo command (#3539) --- .github/workflows/eslint-ci.yml | 2 +- .github/workflows/unit-tests-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/eslint-ci.yml b/.github/workflows/eslint-ci.yml index ccfc36b1dd..e428a8687b 100644 --- a/.github/workflows/eslint-ci.yml +++ b/.github/workflows/eslint-ci.yml @@ -27,4 +27,4 @@ jobs: - name: Install dependencies and run eslint run: | pnpm install - pnpm turbo run lint --api="http://127.0.0.1:9080" --token="${{ env.SERVER_TOKEN }}" + pnpm turbo run lint --api="http://127.0.0.1:9080" --token="${{ env.SERVER_TOKEN }}" --team="telescope-admins" diff --git a/.github/workflows/unit-tests-ci.yml b/.github/workflows/unit-tests-ci.yml index fba6357060..1c4578a91a 100644 --- a/.github/workflows/unit-tests-ci.yml +++ b/.github/workflows/unit-tests-ci.yml @@ -31,4 +31,4 @@ jobs: - name: Install dependencies and run tests with default env run: | pnpm install - pnpm turbo run test --api="http://127.0.0.1:9080" --token="${{ env.SERVER_TOKEN }}" + pnpm turbo run test --api="http://127.0.0.1:9080" --token="${{ env.SERVER_TOKEN }}" --team="telescope-admins" From bbf2bd070bfb102a6758b0019980d31d8daa9754 Mon Sep 17 00:00:00 2001 From: tpmai Date: Fri, 22 Apr 2022 12:07:58 -0400 Subject: [PATCH 35/38] About us navigation (#3538) --- src/web/app/src/components/AboutPage.tsx | 2 +- src/web/app/src/components/BannerButtons.tsx | 2 +- src/web/docusaurus/docs/overview.md | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/web/app/src/components/AboutPage.tsx b/src/web/app/src/components/AboutPage.tsx index 815916b617..da43b4d468 100644 --- a/src/web/app/src/components/AboutPage.tsx +++ b/src/web/app/src/components/AboutPage.tsx @@ -110,7 +110,7 @@ const AboutPage = () => {

What does Telescope do?

In essence, Telescope takes RSS{' '} - blog post feeds, rebuilds those blog + blog post feeds, rebuilds those blog posts into HTML and collects them in a single web page to display. It is able to handle various formatting, such as code blocks or embedded videos. Telescope also collects data about those posts in the{' '} diff --git a/src/web/app/src/components/BannerButtons.tsx b/src/web/app/src/components/BannerButtons.tsx index 7fc0721aad..5193dfc854 100644 --- a/src/web/app/src/components/BannerButtons.tsx +++ b/src/web/app/src/components/BannerButtons.tsx @@ -71,7 +71,7 @@ const BannerButtons = () => { disagreeButtonText="CANCEL" /> )} - +